[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

🆑
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.
/🆑

* Improves duplication

Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-01-13 21:44:44 -08:00
committed by GitHub
co-authored by John Willard
parent cb486eb0a7
commit 81cafafbf6
6 changed files with 100 additions and 154 deletions
+93
View File
@@ -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
+1 -1
View File
@@ -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))
+1 -1
View File
@@ -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.
+4 -4
View File
@@ -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)
-147
View File
@@ -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
+1 -1
View File
@@ -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"