mirror of
https://github.com/Skyrat-SS13/Skyrat-tg.git
synced 2026-08-22 20:39:06 +01:00
* Shuttle gibbing code improvements (#81726) ## About The Pull Request Adds a resistance flag, `SHUTTLE_CRUSH_PROOF`, adds it to the existing things that have it, and adds it to the immortality spectres. ## Why It's Good For The Game Gets rid of some hardcoded checks, making it easier for future code (or admins) to make things immune to shuttle crushing. Also makes it so immortality spectres are immune to being shuttle crushed, as that kinda ruins the whole "immortality" thing... ## Changelog 🆑 refactor: Improved shuttle gibbing code, adding a new resistance flag, `SHUTTLE_CRUSH_PROOF`. fix: Immortality revival spectres can no longer be crushed by shuttles. add: The ghost of Poly can no longer be shuttle-crushed, nor can anything incorporeal. /🆑 Co-authored-by: san7890 <the@ san7890.com> * Shuttle gibbing code improvements --------- Co-authored-by: Lucy <lucy@absolucy.moe> Co-authored-by: san7890 <the@ san7890.com>
249 lines
9.1 KiB
Plaintext
249 lines
9.1 KiB
Plaintext
/// Default poly, presumably died the last shift and has no special traits.
|
|
#define POLY_DEFAULT "default"
|
|
/// Poly has survived a number of rounds equivalent to the longest survival of his being.
|
|
#define POLY_LONGEST_SURVIVAL "longest_survival"
|
|
/// Poly has survived a number of rounds equivalent to the longest deathstreak of his being.
|
|
#define POLY_BEATING_DEATHSTREAK "longest_deathstreak"
|
|
/// Poly has only just survived a round, and is doing a consecutive one.
|
|
#define POLY_CONSECUTIVE_ROUND "consecutive_round"
|
|
/// haunt filter we apply to who we possess
|
|
#define POLY_POSSESS_FILTER
|
|
/// haunt filter color we apply to who we possess
|
|
#define POLY_POSSESS_GLOW "#522059"
|
|
|
|
/// The classically famous compadre to the Chief Engineer, Poly.
|
|
/mob/living/basic/parrot/poly
|
|
name = "Poly"
|
|
desc = "Poly the Parrot. An expert on quantum cracker theory."
|
|
gold_core_spawnable = NO_SPAWN
|
|
speech_probability_rate = 13
|
|
|
|
/// Callback to save our memory at the end of the round.
|
|
var/datum/callback/roundend_callback = null
|
|
/// Did we write the memory to disk?
|
|
var/memory_saved = FALSE
|
|
/// How long has this bird been alive for?
|
|
var/rounds_survived = 0
|
|
/// How long have we survived for at max?
|
|
var/longest_survival = 0
|
|
/// How many rounds in a row have we been dead for?
|
|
var/longest_deathstreak = 0
|
|
|
|
/mob/living/basic/parrot/poly/Initialize(mapload)
|
|
. = ..()
|
|
|
|
if(!memory_saved)
|
|
roundend_callback = CALLBACK(src, PROC_REF(Write_Memory))
|
|
SSticker.OnRoundend(roundend_callback)
|
|
|
|
REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly!
|
|
update_appearance()
|
|
|
|
if(!SStts.tts_enabled)
|
|
return
|
|
|
|
voice = pick(SStts.available_speakers)
|
|
if(SStts.pitch_enabled)
|
|
if(findtext(voice, "Woman"))
|
|
pitch = 12 // up-pitch by one octave
|
|
else
|
|
pitch = 24 // up-pitch by 2 octaves
|
|
else
|
|
voice_filter = "rubberband=pitch=1.5" // Use the filter to pitch up if we can't naturally pitch up.
|
|
|
|
/mob/living/basic/parrot/poly/Destroy()
|
|
LAZYREMOVE(SSticker.round_end_events, roundend_callback) // we do the memory writing stuff on death, but this is important to yeet as fast as we can if we need to destroy
|
|
roundend_callback = null
|
|
return ..()
|
|
|
|
/mob/living/basic/parrot/poly/death(gibbed)
|
|
if(HAS_TRAIT(src, TRAIT_DONT_WRITE_MEMORY))
|
|
return ..() // Don't read memory either.
|
|
if(!memory_saved)
|
|
Write_Memory(TRUE)
|
|
var/special_status = determine_special_poly()
|
|
if(special_status == POLY_LONGEST_SURVIVAL || special_status == POLY_BEATING_DEATHSTREAK || prob(0.666))
|
|
var/mob/living/basic/parrot/poly/ghost/specter = new(loc)
|
|
if(mind)
|
|
mind.transfer_to(specter)
|
|
else
|
|
specter.key = key
|
|
return ..()
|
|
|
|
/mob/living/basic/parrot/poly/get_static_list_of_phrases() // there's only one poly, so there should only be one ongoing list of phrases. i guess
|
|
var/static/list/phrases_to_return = list()
|
|
if(length(phrases_to_return))
|
|
return phrases_to_return
|
|
|
|
phrases_to_return += read_memory() // must come first!!!
|
|
// now add some valuable lines every poly should have
|
|
phrases_to_return += list(
|
|
":e Check the crystal, you chucklefucks!",
|
|
":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE",
|
|
":e WHO TOOK THE DAMN MODSUITS?",
|
|
":e Wire the solars, you lazy bums!",
|
|
"Poly wanna cracker!",
|
|
)
|
|
switch(determine_special_poly())
|
|
if(POLY_DEFAULT)
|
|
phrases_to_return += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!")
|
|
if(POLY_LONGEST_SURVIVAL)
|
|
phrases_to_return += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?")
|
|
if(POLY_BEATING_DEATHSTREAK)
|
|
phrases_to_return += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!")
|
|
if(POLY_CONSECUTIVE_ROUND)
|
|
phrases_to_return += pick("...again?", "No, It was over!", "Let me out!", "It never ends!")
|
|
|
|
return phrases_to_return
|
|
|
|
/mob/living/basic/parrot/poly/update_desc()
|
|
. = ..()
|
|
switch(determine_special_poly())
|
|
if(POLY_LONGEST_SURVIVAL)
|
|
desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]."
|
|
if(POLY_BEATING_DEATHSTREAK)
|
|
desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..."
|
|
if(POLY_CONSECUTIVE_ROUND)
|
|
desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!"
|
|
|
|
/mob/living/basic/parrot/poly/update_icon()
|
|
. = ..()
|
|
switch(determine_special_poly())
|
|
if(POLY_LONGEST_SURVIVAL)
|
|
add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY)
|
|
if(POLY_BEATING_DEATHSTREAK)
|
|
add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY)
|
|
|
|
/// Reads the memory of the parrot, and updates the necessary variables. Returns a list of phrases to add to the parrot's speech buffer.
|
|
/mob/living/basic/parrot/poly/proc/read_memory()
|
|
RETURN_TYPE(/list)
|
|
var/list/returnable_list = list()
|
|
if(fexists("data/npc_saves/Poly.sav")) //legacy compatability to convert old format to new
|
|
var/savefile/legacy = new /savefile("data/npc_saves/Poly.sav")
|
|
legacy["phrases"] >> returnable_list
|
|
legacy["roundssurvived"] >> rounds_survived
|
|
legacy["longestsurvival"] >> longest_survival
|
|
legacy["longestdeathstreak"] >> longest_deathstreak
|
|
fdel("data/npc_saves/Poly.sav")
|
|
|
|
else
|
|
var/json_file = file("data/npc_saves/Poly.json")
|
|
if(!fexists(json_file))
|
|
return list()
|
|
var/list/json = json_decode(file2text(json_file))
|
|
returnable_list = json["phrases"]
|
|
rounds_survived = json["roundssurvived"]
|
|
longest_survival = json["longestsurvival"]
|
|
longest_deathstreak = json["longestdeathstreak"]
|
|
|
|
return returnable_list
|
|
|
|
/// Determines the type of Poly we might have here based on the statistics we got from the memory.
|
|
/mob/living/basic/parrot/poly/proc/determine_special_poly()
|
|
if(rounds_survived == longest_survival)
|
|
return POLY_LONGEST_SURVIVAL
|
|
else if(rounds_survived == longest_deathstreak)
|
|
return POLY_BEATING_DEATHSTREAK
|
|
else if(rounds_survived > 0)
|
|
return POLY_CONSECUTIVE_ROUND
|
|
else
|
|
return POLY_DEFAULT
|
|
|
|
/mob/living/basic/parrot/poly/Write_Memory(dead, gibbed)
|
|
. = ..()
|
|
if(!. || memory_saved) // if we die, no more memory
|
|
return FALSE
|
|
|
|
if(!dead && (stat != DEAD))
|
|
dead = FALSE
|
|
|
|
var/file_path = "data/npc_saves/Poly.json"
|
|
var/list/file_data = list()
|
|
|
|
var/list/exportable_speech_buffer = ai_controller.blackboard[BB_EXPORTABLE_STRING_BUFFER_LIST] // should have been populated when we sent the signal out on parent
|
|
if(!!length(exportable_speech_buffer))
|
|
file_data["phrases"] = exportable_speech_buffer
|
|
|
|
if(dead)
|
|
file_data["roundssurvived"] = min(rounds_survived - 1, 0)
|
|
file_data["longestsurvival"] = longest_survival
|
|
if(rounds_survived - 1 < longest_deathstreak)
|
|
file_data["longestdeathstreak"] = rounds_survived - 1
|
|
else
|
|
file_data["longestdeathstreak"] = longest_deathstreak
|
|
else
|
|
|
|
file_data["roundssurvived"] = max(rounds_survived, 0) + 1
|
|
if(rounds_survived + 1 > longest_survival)
|
|
file_data["longestsurvival"] = rounds_survived + 1
|
|
else
|
|
file_data["longestsurvival"] = longest_survival
|
|
file_data["longestdeathstreak"] = longest_deathstreak
|
|
|
|
rustg_file_write(json_encode(file_data, JSON_PRETTY_PRINT), file_path)
|
|
memory_saved = TRUE
|
|
return TRUE
|
|
|
|
/mob/living/basic/parrot/poly/setup_headset()
|
|
ears = new /obj/item/radio/headset/headset_eng(src)
|
|
|
|
/mob/living/basic/parrot/poly/ghost
|
|
name = "The Ghost of Poly"
|
|
desc = "Doomed to squawk the Earth."
|
|
color = "#FFFFFF77"
|
|
status_flags = GODMODE
|
|
sentience_type = SENTIENCE_BOSS //This is so players can't mindswap into ghost poly to become a literal god
|
|
incorporeal_move = INCORPOREAL_MOVE_BASIC
|
|
butcher_results = list(/obj/item/ectoplasm = 1)
|
|
ai_controller = /datum/ai_controller/basic_controller/parrot/ghost
|
|
speech_probability_rate = 1
|
|
resistance_flags = parent_type::resistance_flags | SHUTTLE_CRUSH_PROOF
|
|
|
|
/mob/living/basic/parrot/poly/ghost/Initialize(mapload)
|
|
// block anything and everything that could possibly happen with writing memory for ghosts
|
|
memory_saved = TRUE
|
|
ADD_TRAIT(src, TRAIT_DONT_WRITE_MEMORY, INNATE_TRAIT)
|
|
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
|
|
return ..()
|
|
|
|
//we perch on human souls
|
|
/mob/living/basic/parrot/poly/ghost/perch_on_human(mob/living/carbon/human/target)
|
|
if(loc == target) //dismount
|
|
forceMove(get_turf(target))
|
|
return FALSE
|
|
if(ishuman(loc))
|
|
balloon_alert(src, "already possessing!")
|
|
return FALSE
|
|
forceMove(target)
|
|
return TRUE
|
|
|
|
/mob/living/basic/parrot/poly/ghost/proc/on_moved(atom/movable/movable, atom/old_loc)
|
|
SIGNAL_HANDLER
|
|
|
|
if(ishuman(old_loc))
|
|
var/mob/living/unpossessed_human = old_loc
|
|
unpossessed_human.remove_filter(POLY_POSSESS_FILTER)
|
|
return
|
|
|
|
if(!ishuman(loc))
|
|
return
|
|
|
|
var/mob/living/possessed_human = loc
|
|
possessed_human.add_filter(POLY_POSSESS_FILTER, 2, list("type" = "outline", "color" = POLY_POSSESS_GLOW, "size" = 2))
|
|
var/filter = possessed_human.get_filter(POLY_POSSESS_FILTER)
|
|
|
|
if(filter)
|
|
animate(filter, alpha = 200, time = 2 SECONDS, loop = -1)
|
|
animate(alpha = 60, time = 2 SECONDS)
|
|
|
|
var/datum/disease/parrot_possession/on_possession = new /datum/disease/parrot_possession
|
|
on_possession.set_parrot(src)
|
|
possessed_human.ForceContractDisease(on_possession, make_copy = FALSE, del_on_fail = TRUE)
|
|
|
|
#undef POLY_DEFAULT
|
|
#undef POLY_LONGEST_SURVIVAL
|
|
#undef POLY_BEATING_DEATHSTREAK
|
|
#undef POLY_CONSECUTIVE_ROUND
|
|
#undef POLY_POSSESS_FILTER
|
|
#undef POLY_POSSESS_GLOW
|