[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>
This commit is contained in:
SkyratBot
2020-10-12 23:26:04 +02:00
committed by GitHub
co-authored by Ryll Ryll
parent 9803236f70
commit b576900a8b
9 changed files with 75 additions and 34 deletions
+5 -3
View File
@@ -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
+1 -1
View File
@@ -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
+6 -3
View File
@@ -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.
+11 -7
View File
@@ -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]"
@@ -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()
@@ -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"
@@ -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
@@ -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
@@ -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()