From 24552f5f95cb7d9065fa41c45c8a894c33ccfe06 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Fri, 2 Jun 2023 01:24:54 +0200 Subject: [PATCH] [MIRROR] Refactors for material container, ammo box & recycler [MDB IGNORE] (#21519) * Refactors for material container, ammo box & recycler * Mirror! * Modular! --------- Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: Funce --- code/datums/components/material_container.dm | 212 ++++++++++++------ code/game/machinery/recycler.dm | 48 ++-- code/modules/mining/machine_processing.dm | 3 +- code/modules/mining/machine_redemption.dm | 15 +- code/modules/mining/machine_stacking.dm | 1 - .../boxes_magazines/_box_magazine.dm | 28 +-- .../modules/sec_haul/code/guns/ammo.dm | 7 - 7 files changed, 189 insertions(+), 125 deletions(-) diff --git a/code/datums/components/material_container.dm b/code/datums/components/material_container.dm index 07c347b31b1..0895383d496 100644 --- a/code/datums/components/material_container.dm +++ b/code/datums/components/material_container.dm @@ -116,82 +116,164 @@ examine_texts += span_notice("It has [amt] units of [lowertext(M.name)] stored.") /// Proc that allows players to fill the parent with mats -/datum/component/material_container/proc/on_attackby(datum/source, obj/item/I, mob/living/user) +/datum/component/material_container/proc/on_attackby(datum/source, obj/item/weapon, mob/living/user) SIGNAL_HANDLER - var/list/tc = allowed_item_typecache - if(!(mat_container_flags & MATCONTAINER_ANY_INTENT) && user.combat_mode) - return - if(I.item_flags & ABSTRACT) - return - if((I.flags_1 & HOLOGRAM_1) || (I.item_flags & NO_MAT_REDEMPTION) || (tc && !is_type_in_typecache(I, tc))) - if(!(mat_container_flags & MATCONTAINER_SILENT)) - to_chat(user, span_warning("[parent] won't accept [I]!")) - return - . = COMPONENT_NO_AFTERATTACK - var/datum/callback/pc = precondition - if(pc && !pc.Invoke(user)) - return - var/material_amount = get_item_material_amount(I, mat_container_flags) - if(!material_amount) - to_chat(user, span_warning("[I] does not contain sufficient materials to be accepted by [parent].")) - return - if(!has_space(material_amount)) - if(isstack(I)) - //figure out how much space is left - var/space_left = max_amount - total_amount - //figure out the amount of sheets that can fit that space - var/obj/item/stack/stack_to_split = I - var/material_per_sheet = material_amount / stack_to_split.amount - var/sheets_to_insert = round(space_left / material_per_sheet) - if(!sheets_to_insert) - to_chat(user, span_warning("[parent] can't hold any more of [I] sheets.")) - return - //split the amount we don't need off - INVOKE_ASYNC(stack_to_split, TYPE_PROC_REF(/obj/item/stack, split_stack), user, stack_to_split.amount - sheets_to_insert) - else - to_chat(user, span_warning("[I] contains more materials than [parent] has space to hold.")) - return - user_insert(I, user, mat_container_flags) + user_insert(weapon, user) -/// Proc used for when player inserts materials + return COMPONENT_NO_AFTERATTACK + +/** + * inserts an item from the players hand into the container. Loops through all the contents inside reccursively + * Arguments + * * held_item - the item to insert + * * user - the mob inserting this item + * * breakdown_flags - how this item and all it's contents inside are broken down during insertion. This is unique to the machine doing the insertion + */ /datum/component/material_container/proc/user_insert(obj/item/held_item, mob/living/user, breakdown_flags = mat_container_flags) set waitfor = FALSE - var/requested_amount - var/active_held = user.get_active_held_item() // differs from I when using TK - if(isstack(held_item) && precise_insertion) - var/atom/current_parent = parent - var/obj/item/stack/item_stack = held_item - requested_amount = tgui_input_number(user, "How much do you want to insert?", "Inserting [item_stack.singular_name]s", item_stack.amount, item_stack.amount) - if(!requested_amount || QDELETED(held_item) || QDELETED(user) || QDELETED(src)) - return - if(parent != current_parent || user.get_active_held_item() != active_held) - return - if(!user.temporarilyRemoveItemFromInventory(held_item)) - to_chat(user, span_warning("[held_item] is stuck to you and cannot be placed into [parent].")) + . = 0 + + //differs from held_item when using TK + var/active_held = user.get_active_held_item() + //don't attack the machine + if(!(mat_container_flags & MATCONTAINER_ANY_INTENT) && user.combat_mode) return - var/inserted = insert_item(held_item, stack_amt = requested_amount, breakdown_flags= mat_container_flags) - if(inserted) - to_chat(user, span_notice("You insert a material total of [inserted] into [parent].")) - qdel(held_item) - if(after_insert) - after_insert.Invoke(held_item, last_inserted_id, inserted) - else if(held_item == active_held) - user.put_in_active_hand(held_item) + //user defined conditions + if(precondition && !precondition.Invoke(user)) + return + + //loop through all contents inside this atom and salvage their material as well but in reverse so we don't delete parents before processing their children + var/list/contents = held_item.get_all_contents_type(/obj/item) + for(var/i = length(contents); i >= 1 ; i--) + var/obj/item/target = contents[i] + + //not a solid sub type + if(target.item_flags & ABSTRACT) + if(target == active_held) //was this the original item in the players hand? put it back because we coudn't salvage it + user.put_in_active_hand(target) + continue + //item is either not real, not allowed for redemption, not in the allowed types + if((target.flags_1 & HOLOGRAM_1) || (target.item_flags & NO_MAT_REDEMPTION) || (allowed_item_typecache && !is_type_in_typecache(target, allowed_item_typecache))) + if(!(mat_container_flags & MATCONTAINER_SILENT)) + to_chat(user, span_warning("[parent] won't accept [target]!")) + if(target == active_held) //was this the original item in the players hand? put it back because we coudn't salvage it + user.put_in_active_hand(target) + continue + + //if stack, check if we want to read precise amount of sheets to insert + var/obj/item/stack/item_stack = null + if(isstack(target) && precise_insertion) + var/atom/current_parent = parent + item_stack = target + var/requested_amount = tgui_input_number(user, "How much do you want to insert?", "Inserting [item_stack.singular_name]s", item_stack.amount, item_stack.amount) + if(!requested_amount || QDELETED(target) || QDELETED(user) || QDELETED(src)) + continue + if(parent != current_parent || user.get_active_held_item() != active_held) + continue + if(requested_amount != item_stack.amount) //only split if its not the whole amount + target = split_stack(item_stack, requested_amount) //split off the requested amount + requested_amount = 0 + + //is this item a stack and was it split by the player? + var/was_stack_split = !isnull(item_stack) && item_stack != target + //if it was split then item_stack has the reference to the original stack/item + var/original_item = was_stack_split ? item_stack : target + //if this item is not the one the player is holding then don't remove it from their hand + if(original_item != active_held) + original_item = null + if(!isnull(original_item) && !user.temporarilyRemoveItemFromInventory(original_item)) //remove from hand(if split remove the original stack else the target) + to_chat(user, span_warning("[held_item] is stuck to you and cannot be placed into [parent].")) + return + + //insert the item + var/inserted = insert_item(target, breakdown_flags = mat_container_flags) + if(inserted > 0) + . += inserted + + //stack was either split by the container(!QDELETED(target) means the container only consumed a part of it) or by the player, put whats left back of the original stack back in players hand + if((!QDELETED(target) || was_stack_split)) + + //stack was split by player and that portion was not fully consumed, merge whats left back with the original stack + if(!QDELETED(target) && was_stack_split) + var/obj/item/stack/inserting_stack = target + item_stack.add(inserting_stack.amount) + qdel(inserting_stack) + + //was this the original item in the players hand? put what's left back in the player's hand + if(!isnull(original_item)) + user.put_in_active_hand(original_item) + + to_chat(user, span_notice("You insert a material total of [inserted] into [parent].")) + else + //decode the error & print it + var/error_msg + if(inserted == -2) + error_msg = "[parent] has insufficient space to accept the [target]" + else + error_msg = "[target] has insufficient materials to be accepted by [parent]" + to_chat(user, span_warning(error_msg)) + + //player split the stack by the requested amount but even that split amount could not be salvaged. merge it back with the original + if(!isnull(item_stack) && was_stack_split) + var/obj/item/stack/inserting_stack = target + item_stack.add(inserting_stack.amount) + qdel(inserting_stack) + + //was this the original item in the players hand? put it back because we coudn't salvage it + if(!isnull(original_item)) + user.put_in_active_hand(original_item) + +/** + * Splits a stack. we don't use /obj/item/stack/proc/split_stack because Byond complains that should only be called asynchronously. + * This proc is also more faster because it doesn't deal with mobs, copying evidences or refreshing atom storages + */ +/datum/component/material_container/proc/split_stack(obj/item/stack/target, amount) + if(!target.use(amount, TRUE, FALSE)) + return null + + . = new target.type(target.drop_location(), amount, FALSE, target.mats_per_unit) + target.loc.atom_storage?.refresh_views() + + target.is_zero_amount(delete_if_zero = TRUE) /// Proc specifically for inserting items, returns the amount of materials entered. -/datum/component/material_container/proc/insert_item(obj/item/I, multiplier = 1, stack_amt, breakdown_flags = mat_container_flags) - if(QDELETED(I)) - return FALSE - +/datum/component/material_container/proc/insert_item(obj/item/weapon, multiplier = 1, breakdown_flags = mat_container_flags) + if(QDELETED(weapon)) + return -1 multiplier = CEILING(multiplier, 0.01) - var/material_amount = get_item_material_amount(I, breakdown_flags) - if(!material_amount || !has_space(material_amount)) - return FALSE + var/obj/item/target = weapon - last_inserted_id = insert_item_materials(I, multiplier, breakdown_flags) - return material_amount + var/material_amount = get_item_material_amount(target, breakdown_flags) * multiplier + if(!material_amount) + return -1 + var/obj/item/stack/item_stack + if(isstack(weapon) && !has_space(material_amount)) //not enugh space split and feed as many sheets possible + item_stack = weapon + var/space_left = max_amount - total_amount + if(!space_left) + return -2 + var/material_per_sheet = material_amount / item_stack.amount + var/sheets_to_insert = round(space_left / material_per_sheet) + if(!sheets_to_insert) + return -2 + target = split_stack(item_stack, sheets_to_insert) + material_amount = get_item_material_amount(target, breakdown_flags) * multiplier + if(!has_space(material_amount)) + return -2 + + last_inserted_id = insert_item_materials(target, multiplier, breakdown_flags) + if(!isnull(last_inserted_id)) + if(after_insert) + after_insert.Invoke(target, last_inserted_id, material_amount) + qdel(target) //item gone + return material_amount + else if(!isnull(item_stack) && item_stack != target) //insertion failed, merge the split stack back into the original + var/obj/item/stack/inserting_stack = target + item_stack.add(inserting_stack.amount) + qdel(inserting_stack) + return 0 /** * Inserts the relevant materials from an item into this material container. diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index bcb6d4b3f65..2b4ebe3a921 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -150,35 +150,44 @@ if(obj_flags & EMAGGED) for(var/CRUNCH in crunchy_nom) // Eat them and keep going because we don't care about safety. if(isliving(CRUNCH)) // MMIs and brains will get eaten like normal items + if(!is_operational) //we ran out of power after recycling a large amount to living stuff, time to stop + break crush_living(CRUNCH) use_power(active_power_usage) else // Stop processing right now without eating anything. emergency_stop() return - for(var/nommed in nom) - recycle_item(nommed) - use_power(active_power_usage) - if(nom.len && sound) - playsound(src, item_recycle_sound, (50 + nom.len*5), TRUE, nom.len, ignore_walls = (nom.len - 10)) // As a substitute for playing 50 sounds at once. - if(not_eaten) - playsound(src, 'sound/machines/buzz-sigh.ogg', (50 + not_eaten*5), FALSE, not_eaten, ignore_walls = (not_eaten - 10)) // Ditto. - if(!ismob(morsel)) - qdel(morsel) -/obj/machinery/recycler/proc/recycle_item(obj/item/I) - var/obj/item/grown/log/L = I - if(istype(L)) + /** + * we process the list in reverse so that atoms without parents/contents are deleted first & their parents are deleted next & so on. + * this is the reverse order in which get_all_contents() returns it's list + * if we delete an atom containing stuff then all its stuff are deleted with it as well so we will end recycling deleted items down the list and gain nothing from them + */ + for(var/i = length(nom); i >= 1; i--) + if(!is_operational) //we ran out of power after recycling a large amount to items, time to stop + break + use_power(active_power_usage / (recycle_item(nom[i]) ? 1 : 2)) //recycling stuff that produces no material takes just half the power + if(nom.len && sound) + playsound(src, item_recycle_sound, (50 + nom.len * 5), TRUE, nom.len, ignore_walls = (nom.len - 10)) // As a substitute for playing 50 sounds at once. + if(not_eaten) + playsound(src, 'sound/machines/buzz-sigh.ogg', (50 + not_eaten * 5), FALSE, not_eaten, ignore_walls = (not_eaten - 10)) // Ditto. + +/obj/machinery/recycler/proc/recycle_item(obj/item/weapon) + . = FALSE + var/obj/item/grown/log/wood = weapon + if(istype(wood)) var/seed_modifier = 0 - if(L.seed) - seed_modifier = round(L.seed.potency / 25) - new L.plank_type(loc, 1 + seed_modifier) + if(wood.seed) + seed_modifier = round(wood.seed.potency / 25) + new wood.plank_type(loc, 1 + seed_modifier) + . = TRUE else var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/material_amount = materials.get_item_material_amount(I, BREAKDOWN_FLAGS_RECYCLER) - if(material_amount) - materials.insert_item(I, material_amount, multiplier = (amount_produced / 100), breakdown_flags=BREAKDOWN_FLAGS_RECYCLER) + var/retrived = materials.insert_item(weapon, multiplier = (amount_produced / 100), breakdown_flags=BREAKDOWN_FLAGS_RECYCLER) + if(retrived > 0) //item was salvaged i.e. deleted materials.retrieve_all() - qdel(I) + return TRUE + qdel(weapon) /obj/machinery/recycler/proc/emergency_stop() playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE) @@ -192,7 +201,6 @@ update_appearance() /obj/machinery/recycler/proc/crush_living(mob/living/L) - L.forceMove(loc) if(issilicon(L)) diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index fe15d628fd2..869779f6c36 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -171,8 +171,7 @@ if(!materials.has_space(material_amount)) unload_mineral(O) else - materials.insert_item(O, breakdown_flags=BREAKDOWN_FLAGS_ORE_PROCESSOR) - qdel(O) + materials.insert_item(O, breakdown_flags = BREAKDOWN_FLAGS_ORE_PROCESSOR) if(mineral_machine) mineral_machine.updateUsrDialog() diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index fcfc7d416ac..f3a4278c2fe 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -75,9 +75,6 @@ if(gathered_ore.refined_type == null) return - if(gathered_ore?.refined_type) - points += gathered_ore.points * point_upgrade * gathered_ore.amount - var/material_amount = mat_container.get_item_material_amount(gathered_ore, BREAKDOWN_FLAGS_ORM) if(!material_amount) @@ -89,10 +86,14 @@ else var/list/stack_mats = gathered_ore.get_material_composition(BREAKDOWN_FLAGS_ORM) var/mats = stack_mats & mat_container.materials - var/amount = gathered_ore.amount - mat_container.insert_item(gathered_ore, ore_multiplier, breakdown_flags=BREAKDOWN_FLAGS_ORM) //insert it - materials.silo_log(src, "smelted", amount, gathered_ore.name, mats) - qdel(gathered_ore) + var/ore_amount = gathered_ore.amount + var/ore_points= gathered_ore.points + var/ore_name = gathered_ore.name + var/refined_type = gathered_ore?.refined_type + if(mat_container.insert_item(gathered_ore, ore_multiplier, breakdown_flags = BREAKDOWN_FLAGS_ORM) > 0) //increase points only if insertion was successfull + if(refined_type) + points += ore_points * point_upgrade * ore_amount + materials.silo_log(src, "smelted", ore_amount, ore_name, mats) SEND_SIGNAL(src, COMSIG_ORM_COLLECTED_ORE) diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 0ea2ddbcba4..366791c6a01 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -142,7 +142,6 @@ if (length(matlist)) var/inserted = materials.mat_container.insert_item(inp) materials.silo_log(src, "collected", inserted, "sheets", matlist) - qdel(inp) return // No silo attached process to internal storage diff --git a/code/modules/projectiles/boxes_magazines/_box_magazine.dm b/code/modules/projectiles/boxes_magazines/_box_magazine.dm index 6735992c2b1..bb2bb789a2d 100644 --- a/code/modules/projectiles/boxes_magazines/_box_magazine.dm +++ b/code/modules/projectiles/boxes_magazines/_box_magazine.dm @@ -31,10 +31,6 @@ var/multiload = TRUE ///Whether the magazine should start with nothing in it var/start_empty = FALSE - ///cost of all the bullets in the magazine/box - var/list/bullet_cost - ///cost of the materials in the magazine/box itself - var/list/base_cost /// If this and ammo_band_icon aren't null, run update_ammo_band(). Is the color of the band, such as blue on the detective's Iceblox. var/ammo_band_color @@ -45,9 +41,7 @@ /obj/item/ammo_box/Initialize(mapload) . = ..() - if(!bullet_cost) - base_cost = SSmaterials.FindOrCreateMaterialCombo(custom_materials, 0.1) - bullet_cost = SSmaterials.FindOrCreateMaterialCombo(custom_materials, 0.9 / max_ammo) + custom_materials = SSmaterials.FindOrCreateMaterialCombo(custom_materials, 0.1) if(!start_empty) top_off(starting=TRUE) update_icon_state() @@ -91,7 +85,7 @@ for(var/i in max(1, stored_ammo.len) to max_ammo) stored_ammo += new round_check() //SKYRAT EDTI CHANGE - SEC_HUAL - Moving to nullspace seems to help with lag. - update_ammo_count() + update_appearance() ///gets a round from the magazine, if keep is TRUE the round will stay in the gun /obj/item/ammo_box/proc/get_round(keep = FALSE) @@ -145,7 +139,7 @@ if(!did_load || !multiload) break if(num_loaded) - AM.update_ammo_count() + AM.update_appearance() if(isammocasing(A)) var/obj/item/ammo_casing/AC = A if(give_round(AC, replace_spent)) @@ -157,7 +151,7 @@ if(!silent) to_chat(user, span_notice("You load [num_loaded] shell\s into \the [src]!")) playsound(src, 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) - update_ammo_count() + update_appearance() return num_loaded @@ -171,11 +165,6 @@ A.bounce_away(FALSE, NONE) playsound(src, 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) to_chat(user, span_notice("You remove a round from [src]!")) - update_ammo_count() - -/// Updates the materials and appearance of this ammo box -/obj/item/ammo_box/proc/update_ammo_count() - update_custom_materials() update_appearance() /obj/item/ammo_box/update_desc(updates) @@ -206,13 +195,6 @@ ammo_band_image.appearance_flags = RESET_COLOR|KEEP_APART overlays += ammo_band_image -/// Updates the amount of material in this ammo box according to how many bullets are left in it. -/obj/item/ammo_box/proc/update_custom_materials() - var/temp_materials = custom_materials.Copy() - for(var/material in bullet_cost) - temp_materials[material] = (bullet_cost[material] * stored_ammo.len) + base_cost[material] - set_custom_materials(temp_materials) - ///Count of number of bullets in the magazine /obj/item/ammo_box/magazine/proc/ammo_count(countempties = TRUE) var/boolets = 0 @@ -237,7 +219,7 @@ /obj/item/ammo_box/magazine/handle_atom_del(atom/A) stored_ammo -= A - update_ammo_count() + update_appearance() //SKRYAT EDIT ADDITION BEGIN - SEC_HAUL /obj/item/ammo_box/Destroy() diff --git a/modular_skyrat/modules/sec_haul/code/guns/ammo.dm b/modular_skyrat/modules/sec_haul/code/guns/ammo.dm index 2cbbb9c3d11..746147694b2 100644 --- a/modular_skyrat/modules/sec_haul/code/guns/ammo.dm +++ b/modular_skyrat/modules/sec_haul/code/guns/ammo.dm @@ -138,13 +138,6 @@ if(AMMO_BOX_FULL_EMPTY) icon_state = "[initial(icon_state)]_[round_type]-[shells_left ? "full" : "empty"]" desc = "[initial(desc)] There [(shells_left == 1) ? "is" : "are"] [shells_left] shell\s left!" - if(length(bullet_cost)) - var/temp_materials = custom_materials.Copy() - for (var/material in bullet_cost) - var/material_amount = bullet_cost[material] - material_amount = (material_amount*stored_ammo.len) + base_cost[material] - temp_materials[material] = material_amount - set_custom_materials(temp_materials) /obj/item/ammo_box/revolver name = "speed loader"