From 81cafafbf6788bde1778f7ce2bf764eb642ca447 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sat, 14 Jan 2023 06:44:44 +0100 Subject: [PATCH] [MIRROR] Improves duplication [MDB IGNORE] (#18704) * Improves duplication (#72572) ## About The Pull Request - Improves duplication code significantly - Removes 'perfectcopy', 'newloc', 'nerf' and 'holoitem' args. These were made for holodeck items, but holodeck items do not use this proc so it's since been unused. - Adds many things to duplicate forbidden vars, such as external organs (and fixes internal organs), overlays, and signals. The signal part is what broke basic things for duplicated mobs, such as dying, huds, and lying down. - Duplicated mobs now properly carry over the identity of the old mob without losing anything in the process, and now actually work as a mob, with visible HUDs and everything. They also carry implants over now. - Duplicated mobs also now no longer cut all their contents and rebuild the entire mob, they don't carry overlays at all (so we don't have the problems that come along with it, like clothing sprites from clothes that don't exist). - As a minor detail, makes DuplicateObject use snake_case instead, and makes duplicate_forbidden_vars protected. - Removes copy_contents_to because it's unused. It was originally meant for Holodeck, but holodecks now use map templates so it's no longer used in-game. ![image](https://user-images.githubusercontent.com/53777086/211224777-6b417c6c-17d3-486b-85a4-41de30c6cfd2.png) ![image](https://user-images.githubusercontent.com/53777086/211223163-0cbb4581-c194-4251-9c7b-58d8c4bbaeb2.png) ## Why It's Good For The Game Closes https://github.com/tgstation/tgstation/issues/42212 Duplicating mobs no longer gives a broken mob, which was a common problem with cloning pods (the admin pods, that you drop down onto people). Updates very old code to modern code standards. This PR was made to help out https://github.com/tgstation/tgstation/pull/71141 too, the author of that PR is aware of this one. ## Changelog :cl: refactor: Duplicating mobs now should now give properly functioning mobs, as duplications in general have been reworked. Admins can feel free to use the pod feature on people. /:cl: * Improves duplication Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> --- code/__HELPERS/duplicating.dm | 93 ++++++++++++++ code/modules/admin/topic.dm | 2 +- code/modules/buildmode/submodes/copy.dm | 2 +- code/modules/cargo/centcom_podlauncher.dm | 8 +- code/modules/holodeck/area_copy.dm | 147 ---------------------- tgstation.dme | 2 +- 6 files changed, 100 insertions(+), 154 deletions(-) create mode 100644 code/__HELPERS/duplicating.dm delete mode 100644 code/modules/holodeck/area_copy.dm diff --git a/code/__HELPERS/duplicating.dm b/code/__HELPERS/duplicating.dm new file mode 100644 index 00000000000..e15f375d20a --- /dev/null +++ b/code/__HELPERS/duplicating.dm @@ -0,0 +1,93 @@ +///List of all vars that will not be copied over when using duplicate_object() +GLOBAL_LIST_INIT(duplicate_forbidden_vars, list( + "actions", + "active_hud_list", + "active_timers", + "AIStatus", + "appearance", + "area", + "atmos_adjacent_turfs", + "bodyparts", + "ckey", + "comp_lookup", + "computer_id", + "contents", + "cooldowns", + "datum_components", + "external_organs", + "external_organs_slot", + "group", + "hand_bodyparts", + "held_items", + "hud_list", + "implants", + "important_recursive_contents", + "internal_organs", + "internal_organs_slot", + "key", + "lastKnownIP", + "loc", + "locs", + "managed_overlays", + "managed_vis_overlays", + "overlays", + "overlays_standing", + "parent", + "parent_type", + "power_supply", + "quirks", + "reagents", + "signal_procs", + "status_traits", + "stat", + "tag", + "tgui_shared_states", + "type", + "vars", + "verbs", + "x", "y", "z", +)) +GLOBAL_PROTECT(duplicate_forbidden_vars) + +/** + * # duplicate_object + * + * Makes a copy of an item and transfers most vars over, barring GLOB.duplicate_forbidden_vars + * Args: + * original - Atom being duplicated + * spawning_location - Turf where the duplicated atom will be spawned at. + */ +/proc/duplicate_object(atom/original, turf/spawning_location) + RETURN_TYPE(original.type) + if(!original) + return + + var/atom/made_copy = new original.type(spawning_location) + + for(var/atom_vars in original.vars - GLOB.duplicate_forbidden_vars) + if(islist(original.vars[atom_vars])) + var/list/var_list = original.vars[atom_vars] + made_copy.vars[atom_vars] = var_list.Copy() + continue + else if(istype(original.vars[atom_vars], /datum) || ismob(original.vars[atom_vars])) + continue // this would reference the original's object, that will break when it is used or deleted. + made_copy.vars[atom_vars] = original.vars[atom_vars] + + if(isliving(made_copy)) + if(iscarbon(made_copy)) + var/mob/living/carbon/original_carbon = original + var/mob/living/carbon/copied_carbon = made_copy + //transfer DNA over (also body features), then update skin color. + original_carbon.dna.transfer_identity(copied_carbon, transfer_SE = TRUE) + copied_carbon.updateappearance(mutcolor_update = TRUE) + + var/mob/living/original_living = original + //transfer implants, we do this so the original's implants being removed won't destroy ours. + for(var/obj/item/implant/original_implants as anything in original_living.implants) + var/obj/item/implant/copied_implant = new original_implants.type + copied_implant.implant(made_copy, silent = TRUE, force = TRUE) + //transfer quirks, we do this because transfering the original's quirks keeps the 'owner' as the original. + for(var/datum/quirk/original_quirks as anything in original_living.quirks) + original_living.add_quirk(original_quirks.type) + + return made_copy diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 7dfc03e22c7..c5a77df1b46 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1212,7 +1212,7 @@ else if(href_list["dupe_marked_datum"]) if(!check_rights(R_SPAWN)) return - return DuplicateObject(marked_datum, perfectcopy=1, newloc=get_turf(usr)) + return duplicate_object(marked_datum, spawning_location = get_turf(usr)) else if(href_list["object_list"]) //this is the laggiest thing ever if(!check_rights(R_SPAWN)) diff --git a/code/modules/buildmode/submodes/copy.dm b/code/modules/buildmode/submodes/copy.dm index 739f53e9bc8..9effd1d9c30 100644 --- a/code/modules/buildmode/submodes/copy.dm +++ b/code/modules/buildmode/submodes/copy.dm @@ -18,7 +18,7 @@ if(LAZYACCESS(modifiers, LEFT_CLICK)) var/turf/T = get_turf(object) if(stored) - DuplicateObject(stored, perfectcopy=1, sameloc=0,newloc=T) + duplicate_object(stored, spawning_location = T) log_admin("Build Mode: [key_name(c)] copied [stored] to [AREACOORD(object)]") else if(LAZYACCESS(modifiers, RIGHT_CLICK)) if(ismovable(object)) // No copying turfs for now. diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index baec0b57829..8c25da21cf2 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -42,7 +42,7 @@ var/bayNumber //Quick reference to what bay we're in. Usually set to the loading_id variable for the related area type var/customDropoff = FALSE var/picking_dropoff_turf = FALSE - var/launchClone = FALSE //If true, then we don't actually launch the thing in the bay. Instead we call duplicateObject() and send the result + var/launchClone = FALSE //If true, then we don't actually launch the thing in the bay. Instead we call duplicate_object() and send the result var/launchRandomItem = FALSE //If true, lauches a single random item instead of everything on a turf. var/launchChoice = LAUNCH_RANDOM //Determines if we launch all at once (0) , in order (1), or at random(2) var/explosionChoice = 0 //Determines if there is no explosion (0), custom explosion (1), or just do a maxcap (2) @@ -721,7 +721,7 @@ /datum/centcom_podlauncher/proc/launch(turf/target_turf) //Game time started if (isnull(target_turf)) return - var/obj/structure/closet/supplypod/centcompod/toLaunch = DuplicateObject(temp_pod) //Duplicate the temp_pod (which we have been varediting or configuring with the UI) and store the result + var/obj/structure/closet/supplypod/centcompod/toLaunch = duplicate_object(temp_pod) //Duplicate the temp_pod (which we have been varediting or configuring with the UI) and store the result toLaunch.update_appearance()//we update_appearance() here so that the door doesnt "flicker on" right after it lands var/shippingLane = GLOB.areas_by_type[/area/centcom/central_command_areas/supplypod/supplypod_temp_holding] toLaunch.forceMove(shippingLane) @@ -734,7 +734,7 @@ toLaunch.turfs_in_cargo += atom_to_launch.type else var/atom/movable/movable_to_launch = launch_candidate - DuplicateObject(movable_to_launch).forceMove(toLaunch) //Duplicate a single atom/movable from launchList and forceMove it into the supplypod + duplicate_object(movable_to_launch).forceMove(toLaunch) //Duplicate a single atom/movable from launchList and forceMove it into the supplypod else for (var/launch_candidate in launchList) if (isnull(launch_candidate)) @@ -744,7 +744,7 @@ toLaunch.turfs_in_cargo += turf_to_launch.type else var/atom/movable/movable_to_launch = launch_candidate - DuplicateObject(movable_to_launch).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod + duplicate_object(movable_to_launch).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod else if(launchRandomItem) var/atom/random_item = pick_n_take(launchList) diff --git a/code/modules/holodeck/area_copy.dm b/code/modules/holodeck/area_copy.dm deleted file mode 100644 index 054f5d59abc..00000000000 --- a/code/modules/holodeck/area_copy.dm +++ /dev/null @@ -1,147 +0,0 @@ -//Vars that will not be copied when using /DuplicateObject -GLOBAL_LIST_INIT(duplicate_forbidden_vars,list( - "tag", "datum_components", "area", "type", "loc", "locs", "vars", "parent", "parent_type", "verbs", "ckey", "key", - "power_supply", "contents", "reagents", "stat", "x", "y", "z", "group", "atmos_adjacent_turfs", "comp_lookup", - "important_recursive_contents", "bodyparts", "internal_organs", "hand_bodyparts", "overlays_standing", "hud_list", - "actions", "AIStatus", "appearance", "managed_overlays", "managed_vis_overlays", "computer_id", "lastKnownIP", "implants", - "tgui_shared_states" - )) - -/proc/DuplicateObject(atom/original, perfectcopy = TRUE, sameloc, atom/newloc = null, nerf, holoitem) - RETURN_TYPE(original.type) - if(!original) - return - var/atom/O - - if(sameloc) - O = new original.type(original.loc) - else - O = new original.type(newloc) - - if(perfectcopy && O && original) - for(var/V in original.vars - GLOB.duplicate_forbidden_vars) - if(islist(original.vars[V])) - var/list/L = original.vars[V] - O.vars[V] = L.Copy() - else if(istype(original.vars[V], /datum) || ismob(original.vars[V])) - continue // this would reference the original's object, that will break when it is used or deleted. - else - O.vars[V] = original.vars[V] - - if(isobj(O)) - var/obj/N = O - if(holoitem) - N.resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF // holoitems do not burn - - if(nerf && isitem(O)) - var/obj/item/I = O - I.damtype = STAMINA // thou shalt not - - N.update_appearance() - if(ismachinery(O)) - var/obj/machinery/M = O - M.power_change() - if(istype(O, /obj/machinery/button)) - var/obj/machinery/button/B = O - B.setup_device() - - if(holoitem) - O.flags_1 |= HOLOGRAM_1 - for(var/atom/thing in O) - thing.flags_1 |= HOLOGRAM_1 - if(ismachinery(O)) - var/obj/machinery/M = O - for(var/atom/contained_atom in M.component_parts) - contained_atom.flags_1 |= HOLOGRAM_1 - if(M.circuit) - M.circuit.flags_1 |= HOLOGRAM_1 - - if(ismob(O)) //Overlays are carried over despite disallowing them, if a fix is found remove this. - var/mob/M = O - M.cut_overlays() - M.regenerate_icons() - return O - - -/area/proc/copy_contents_to(area/A , platingRequired = 0, nerf_weapons = 0 ) - //Takes: Area. Optional: If it should copy to areas that don't have plating - //Returns: Nothing. - //Notes: Attempts to move the contents of one area to another area. - // Movement based on lower left corner. Tiles that do not fit - // into the new area will not be moved. - - if(!A || !src) - return 0 - - var/list/turfs_src = get_area_turfs(src.type) - var/list/turfs_trg = get_area_turfs(A.type) - - var/src_min_x = 99999 - var/src_min_y = 99999 - var/list/refined_src = new/list() - - for (var/turf/T in turfs_src) - src_min_x = min(src_min_x,T.x) - src_min_y = min(src_min_y,T.y) - for (var/turf/T in turfs_src) - refined_src[T] = "[T.x - src_min_x].[T.y - src_min_y]" - - var/trg_min_x = 99999 - var/trg_min_y = 99999 - var/list/refined_trg = new/list() - - for (var/turf/T in turfs_trg) - trg_min_x = min(trg_min_x,T.x) - trg_min_y = min(trg_min_y,T.y) - for (var/turf/T in turfs_trg) - refined_trg["[T.x - trg_min_x].[T.y - trg_min_y]"] = T - - var/list/toupdate = new/list() - - var/copiedobjs = list() - - for (var/turf/T in refined_src) - var/coordstring = refined_src[T] - var/turf/B = refined_trg[coordstring] - if(!istype(B)) - continue - - if(platingRequired) - if(isspaceturf(B)) - continue - - var/old_dir1 = T.dir - var/old_icon_state1 = T.icon_state - var/old_icon1 = T.icon - - B = B.ChangeTurf(T.type) - B.setDir(old_dir1) - B.icon = old_icon1 - B.icon_state = old_icon_state1 - - for(var/obj/O in T) - var/obj/O2 = DuplicateObject(O , perfectcopy=TRUE, newloc = B, nerf=nerf_weapons, holoitem=TRUE) - if(!O2) - continue - copiedobjs += O2.get_all_contents() - - for(var/mob/M in T) - if(iscameramob(M)) - continue // If we need to check for more mobs, I'll add a variable - var/mob/SM = DuplicateObject(M , perfectcopy=TRUE, newloc = B, holoitem=TRUE) - copiedobjs += SM.get_all_contents() - - for(var/V in T.vars - GLOB.duplicate_forbidden_vars) - if(V == "air") - var/turf/open/O1 = B - var/turf/open/O2 = T - O1.air.copy_from(O2.return_air()) - continue - B.vars[V] = T.vars[V] - toupdate += B - - if(toupdate.len) - for(var/turf/T1 in toupdate) - CALCULATE_ADJACENT_TURFS(T1, KILL_EXCITED) - - return copiedobjs diff --git a/tgstation.dme b/tgstation.dme index 8c44ed2787f..89722ebfabf 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -406,6 +406,7 @@ #include "code\__HELPERS\dates.dm" #include "code\__HELPERS\datums.dm" #include "code\__HELPERS\dna.dm" +#include "code\__HELPERS\duplicating.dm" #include "code\__HELPERS\files.dm" #include "code\__HELPERS\filters.dm" #include "code\__HELPERS\forensics.dm" @@ -3343,7 +3344,6 @@ #include "code\modules\holiday\foreign_calendar.dm" #include "code\modules\holiday\holidays.dm" #include "code\modules\holiday\nth_week.dm" -#include "code\modules\holodeck\area_copy.dm" #include "code\modules\holodeck\computer.dm" #include "code\modules\holodeck\holo_effect.dm" #include "code\modules\holodeck\holodeck_map_templates.dm"