From b576900a8ba21558d5a8eb8711e629559360e897 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Mon, 12 Oct 2020 23:26:04 +0200 Subject: [PATCH] [MIRROR] Temporal Scarring is now per-character-slot, changelings now fake scars when disguising (#1274) * Temporal Scarring is now per-character-slot, changelings now fake scars when disguising (#54311) * Temporal Scarring is now per-character-slot, changelings now fake scars when disguising Co-authored-by: Ryll Ryll <3589655+Ryll-Ryll@users.noreply.github.com> --- code/__DEFINES/wounds.dm | 8 ++-- code/controllers/subsystem/persistence.dm | 2 +- code/datums/mind.dm | 9 ++-- code/datums/wounds/scars/_scars.dm | 18 ++++--- code/game/gamemodes/changeling/changeling.dm | 11 +++++ .../antagonists/changeling/changeling.dm | 8 ++++ .../changeling/powers/transform.dm | 4 ++ .../modules/mob/dead/new_player/new_player.dm | 1 + .../mob/living/carbon/human/human_helpers.dm | 48 +++++++++++-------- 9 files changed, 75 insertions(+), 34 deletions(-) diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index 5d060da2443..92a1d60ece9 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -134,10 +134,12 @@ GLOBAL_LIST_INIT(global_all_wound_types, list(/datum/wound/blunt/critical, /datu #define SCAR_SAVE_SEVERITY 5 /// Whether this is a BIO_JUST_BONE scar, a BIO_JUST_FLESH scar, or a BIO_FLESH_BONE scar (so you can't load fleshy human scars on a plasmaman character) #define SCAR_SAVE_BIOLOGY 6 +/// Which character slot this was saved to +#define SCAR_SAVE_CHAR_SLOT 7 ///how many fields we save for each scar (so the number of above fields) -#define SCAR_SAVE_LENGTH 6 +#define SCAR_SAVE_LENGTH 7 /// saved scars with a version lower than this will be discarded, increment when you update the persistent scarring format in a way that invalidates previous saved scars (new fields, reordering, etc) -#define SCAR_CURRENT_VERSION 2 -/// how many scar slots we have to cycle through for persistent scarring, if enabled in character prefs +#define SCAR_CURRENT_VERSION 3 +/// how many scar slots, per character slot, we have to cycle through for persistent scarring, if enabled in character prefs #define PERSISTENT_SCAR_SLOTS 3 diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index e1c82481646..a22a93fdb1e 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -397,7 +397,7 @@ SUBSYSTEM_DEF(persistence) /datum/controller/subsystem/persistence/proc/SaveScars() for(var/i in GLOB.joined_player_list) var/mob/living/carbon/human/ending_human = get_mob_by_ckey(i) - if(!istype(ending_human) || !ending_human.mind || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.persistent_scars) + if(!istype(ending_human) || !ending_human.mind?.original_character_slot_index || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.persistent_scars) continue var/mob/living/carbon/human/original_human = ending_human.mind.original_character diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 58cab90c7b4..726b237d1c3 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -69,10 +69,13 @@ var/list/skills_rewarded ///Assoc list of skills. Use SKILL_LVL to access level, and SKILL_EXP to access skill's exp. var/list/known_skills = list() - ///What character we spawned in as- either at roundstart or latejoin, so we know for persistent scars if we ended as the same person or not + ///What character we joined in as- either at roundstart or latejoin, so we know for persistent scars if we ended as the same person or not var/mob/original_character - /// What scar slot we have loaded, so we don't have to constantly check the savefile - var/current_scar_slot + /// The index for what character slot, if any, we were loaded from, so we can track persistent scars on a per-character basis. Each character slot gets PERSISTENT_SCAR_SLOTS scar slots + var/original_character_slot_index + /// The index for our current scar slot, so we don't have to constantly check the savefile (unlike the slots themselves, this index is independent of selected char slot, and increments whenever a valid char is joined with) + var/current_scar_slot_index + ///Skill multiplier, adjusts how much xp you get/loose from adjust_xp. Dont override it directly, add your reason to experience_multiplier_reasons and use that as a key to put your value in there. var/experience_multiplier = 1 ///Skill multiplier list, just slap your multiplier change onto this with the type it is coming from as key. diff --git a/code/datums/wounds/scars/_scars.dm b/code/datums/wounds/scars/_scars.dm index f948f3e97d4..d51554339c3 100644 --- a/code/datums/wounds/scars/_scars.dm +++ b/code/datums/wounds/scars/_scars.dm @@ -16,7 +16,7 @@ /// A string detailing the specific part of the bodypart the scar is on, for fluff purposes. See [/datum/scar/proc/generate] var/precise_location - /// In case we ever want to make scars that won't be saved for persistent scarring (formerly used by the now-removed longtimer quirk) + /// These scars are assumed to come from changeling disguises, rather than from persistence or wounds. As such, they are deleted by dropping changeling disguises, and are ignored by persistence var/fake=FALSE /// How many tiles away someone can see this scar, goes up with severity. Clothes covering this limb will decrease visibility by 1 each, except for the head/face which is a binary "is mask obscuring face" check var/visibility = 2 @@ -24,6 +24,8 @@ var/coverable = TRUE /// Obviously, scars that describe damaged flesh wouldn't apply to a skeleton (in some cases like bone wounds, there can be different descriptions for skeletons and fleshy humanoids) var/biology = BIO_FLESH_BONE + /// If we're a persistent scar or may become one, we go in this character slot + var/persistent_character_slot = 0 /datum/scar/Destroy(force, ...) if(limb) @@ -47,6 +49,7 @@ severity = W.severity if(limb.owner) victim = limb.owner + persistent_character_slot = victim.mind?.original_character_slot_index if(add_to_scars) LAZYADD(limb.scars, src) if(victim) @@ -79,7 +82,7 @@ LAZYADD(victim.all_scars, src) /// Used to "load" a persistent scar -/datum/scar/proc/load(obj/item/bodypart/BP, version, description, specific_location, severity=WOUND_SEVERITY_SEVERE, biology=BIO_FLESH_BONE) +/datum/scar/proc/load(obj/item/bodypart/BP, version, description, specific_location, severity=WOUND_SEVERITY_SEVERE, biology=BIO_FLESH_BONE, char_slot) if(!BP.is_organic_limb()) qdel(src) return @@ -94,6 +97,7 @@ src.severity = severity src.biology = biology + persistent_character_slot = char_slot LAZYADD(limb.scars, src) src.description = description @@ -107,7 +111,7 @@ visibility = 5 if(WOUND_SEVERITY_LOSS) visibility = 7 - return TRUE + return src /// What will show up in examine_more() if this scar is visible /datum/scar/proc/get_examine_description(mob/viewer) @@ -148,11 +152,11 @@ return TRUE -/// Used to format a scar to save in preferences for persistent scars +/// Used to format a scar to save for either persistent scars, or for changeling disguises /datum/scar/proc/format() - return fake ? null : "[SCAR_CURRENT_VERSION]|[limb.body_zone]|[description]|[precise_location]|[severity]|[biology]" + return "[SCAR_CURRENT_VERSION]|[limb.body_zone]|[description]|[precise_location]|[severity]|[biology]|[persistent_character_slot]" /// Used to format a scar to save in preferences for persistent scars /datum/scar/proc/format_amputated(body_zone) - description = pick(list("is several skintone shades paler than the rest of the body", "is a gruesome patchwork of artificial flesh", "has a large series of attachment scars at the articulation points")) - return "[SCAR_CURRENT_VERSION]|[body_zone]|[description]|amputated|[WOUND_SEVERITY_LOSS]|[BIO_FLESH_BONE]" + description = pick_list(FLESH_SCAR_FILE, "dismemberment") + return "[SCAR_CURRENT_VERSION]|[body_zone]|[description]|amputated|[WOUND_SEVERITY_LOSS]|[BIO_FLESH_BONE]|[persistent_character_slot]" diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 89751332bca..91898b500b0 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -110,6 +110,12 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th user.update_body() user.domutcheck() + // get rid of any scars from previous changeling-ing + for(var/i in user.all_scars) + var/datum/scar/iter_scar = i + if(iter_scar.fake) + qdel(iter_scar) + // Do skillchip code after DNA code. // There's a mutation that increases max chip complexity available, even though we force-implant skillchips. @@ -173,4 +179,9 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th if(equip) user.equip_to_slot_or_del(C, GLOB.slot2slot[slot]) + for(var/stored_scar_line in chosen_prof.stored_scars) + var/datum/scar/attempted_fake_scar = user.load_scar(stored_scar_line) + if(attempted_fake_scar) + attempted_fake_scar.fake = TRUE + user.regenerate_icons() diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index 34e62e15ccd..674b28ca70d 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -295,6 +295,10 @@ prof.skillchips = H.clone_skillchip_list(TRUE) + for(var/i in H.all_scars) + var/datum/scar/iter_scar = i + LAZYADD(prof.stored_scars, iter_scar.format()) + var/list/slots = list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store") for(var/slot in slots) if(slot in H.vars) @@ -529,9 +533,12 @@ var/socks var/list/skillchips = list() + /// What scars the target had when we copied them, in string form (like persistent scars) + var/list/stored_scars /datum/changelingprofile/Destroy() qdel(dna) + LAZYCLEARLIST(stored_scars) . = ..() /datum/changelingprofile/proc/copy_profile(datum/changelingprofile/newprofile) @@ -552,6 +559,7 @@ newprofile.worn_icon_list = worn_icon_list.Copy() newprofile.worn_icon_state_list = worn_icon_state_list.Copy() newprofile.skillchips = skillchips.Copy() + newprofile.stored_scars = stored_scars.Copy() /datum/antagonist/changeling/xenobio name = "Xenobio Changeling" diff --git a/code/modules/antagonists/changeling/powers/transform.dm b/code/modules/antagonists/changeling/powers/transform.dm index 1784e0f967c..cc1433e4062 100644 --- a/code/modules/antagonists/changeling/powers/transform.dm +++ b/code/modules/antagonists/changeling/powers/transform.dm @@ -166,6 +166,10 @@ for(var/slot in GLOB.slots) if(istype(user.vars[slot], GLOB.slot2type[slot])) qdel(user.vars[slot]) + for(var/i in user.all_scars) + var/datum/scar/iter_scar = i + if(iter_scar.fake) + qdel(iter_scar) var/datum/changelingprofile/prof = get_dna(chosen_name) return prof diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 50f7b18dc3f..950c2f4f4d2 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -464,6 +464,7 @@ if(transfer_after) mind.late_joiner = TRUE mind.active = FALSE //we wish to transfer the key manually + mind.original_character_slot_index = client.prefs.default_slot mind.transfer_to(H) //won't transfer key since the mind is not active mind.original_character = H diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index eb85fb2e360..d0bea4f15b3 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -175,15 +175,15 @@ return var/path = "data/player_saves/[check_ckey[1]]/[check_ckey]/scars.sav" - var/index = mind.current_scar_slot - if (!index) + var/index = mind.current_scar_slot_index + if(!index) if(fexists(path)) var/savefile/F = new /savefile(path) - index = F["current_index"] || 1 + index = F["current_scar_index"] || 1 else index = 1 - mind.current_scar_slot = (index % PERSISTENT_SCAR_SLOTS) + 1 || 1 + mind.current_scar_slot_index = (index % PERSISTENT_SCAR_SLOTS) + 1 || 1 /// For use formatting all of the scars this human has for saving for persistent scarring, returns a string with all current scars/missing limb amputation scars for saving or loading purposes /mob/living/carbon/human/proc/format_scars() @@ -195,55 +195,63 @@ var/datum/scar/scaries = new scars += "[scaries.format_amputated(i)]" for(var/i in all_scars) - var/datum/scar/scaries = i - scars += "[scaries.format()];" + var/datum/scar/iter_scar = i + if(!iter_scar.fake) + scars += "[iter_scar.format()];" return scars /// Takes a single scar from the persistent scar loader and recreates it from the saved data -/mob/living/carbon/human/proc/load_scar(scar_line) +/mob/living/carbon/human/proc/load_scar(scar_line, specified_char_index) var/list/scar_data = splittext(scar_line, "|") if(LAZYLEN(scar_data) != SCAR_SAVE_LENGTH) return // invalid, should delete var/version = text2num(scar_data[SCAR_SAVE_VERS]) if(!version || version < SCAR_CURRENT_VERSION) // get rid of old scars return + if(specified_char_index && (mind?.original_character_slot_index != specified_char_index)) + return var/obj/item/bodypart/the_part = get_bodypart("[scar_data[SCAR_SAVE_ZONE]]") var/datum/scar/scaries = new - return scaries.load(the_part, scar_data[SCAR_SAVE_VERS], scar_data[SCAR_SAVE_DESC], scar_data[SCAR_SAVE_PRECISE_LOCATION], text2num(scar_data[SCAR_SAVE_SEVERITY]), text2num(scar_data[SCAR_SAVE_BIOLOGY])) + return scaries.load(the_part, scar_data[SCAR_SAVE_VERS], scar_data[SCAR_SAVE_DESC], scar_data[SCAR_SAVE_PRECISE_LOCATION], text2num(scar_data[SCAR_SAVE_SEVERITY]), text2num(scar_data[SCAR_SAVE_BIOLOGY]), text2num(scar_data[SCAR_SAVE_CHAR_SLOT])) -/// Read all the scars we have at the designated slot, verify they're good (or dump them if they're old/wrong format), create them on the user, and write the scars that passed muster back to the file +/// Read all the scars we have for the designated character/scar slots, verify they're good/dump them if they're old/wrong format, create them on the user, and write the scars that passed muster back to the file /mob/living/carbon/human/proc/load_persistent_scars() - if(!ckey || !mind || !client?.prefs.persistent_scars) + if(!ckey || !mind?.original_character_slot_index || !client?.prefs.persistent_scars) return var/path = "data/player_saves/[ckey[1]]/[ckey]/scars.sav" - if (!fexists(path)) + var/loaded_char_slot = client.prefs.default_slot + + if(!loaded_char_slot || !fexists(path)) return FALSE var/savefile/F = new /savefile(path) if(!F) return - var/index = mind.current_scar_slot || F["current_index"] || 1 + var/char_index = mind.original_character_slot_index + var/scar_index = mind.current_scar_slot_index || F["current_scar_index"] || 1 - var/scar_string = F["scar[index]"] + var/scar_string = F["scar[char_index]-[scar_index]"] var/valid_scars = "" + for(var/scar_line in splittext(sanitize_text(scar_string), ";")) - if(load_scar(scar_line)) + if(load_scar(scar_line, char_index)) valid_scars += "[scar_line];" - WRITE_FILE(F["scar[index]"], sanitize_text(valid_scars)) + WRITE_FILE(F["scar[char_index]-[scar_index]"], sanitize_text(valid_scars)) /// Save any scars we have to our designated slot, then write our current slot so that the next time we call [/mob/living/carbon/human/proc/increment_scar_slot] (the next round we join), we'll be there /mob/living/carbon/human/proc/save_persistent_scars(nuke=FALSE) - if(!ckey || !mind || !client?.prefs.persistent_scars) + if(!ckey || !mind?.original_character_slot_index || !client?.prefs.persistent_scars) return var/path = "data/player_saves/[ckey[1]]/[ckey]/scars.sav" var/savefile/F = new /savefile(path) - var/index = mind.current_scar_slot || F["current_index"] || 1 + var/char_index = mind.original_character_slot_index + var/scar_index = mind.current_scar_slot_index || F["current_scar_index"] || 1 if(nuke) - WRITE_FILE(F["scar[index]"], "") + WRITE_FILE(F["scar[char_index]-[scar_index]"], "") return for(var/k in all_wounds) @@ -251,8 +259,8 @@ iter_wound.remove_wound() // so we can get the scars for open wounds var/valid_scars = format_scars() - WRITE_FILE(F["scar[index]"], sanitize_text(valid_scars)) - WRITE_FILE(F["current_index"], sanitize_integer(index)) + WRITE_FILE(F["scar[char_index]-[scar_index]"], sanitize_text(valid_scars)) + WRITE_FILE(F["current_scar_index"], sanitize_integer(scar_index)) /mob/living/carbon/human/get_biological_state() return dna.species.get_biological_state()