From e57fcbd25068bec7bafc0181e49ce5766a2d8db6 Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 4 Sep 2023 23:18:34 -0600 Subject: [PATCH 01/69] i'm dying jessser --- code/__DEFINES/combat.dm | 9 + .../signals_atom/signals_atom_movable.dm | 4 +- code/modules/mob/living/basic/pets/parrot.dm | 164 ++++++++++++++++++ .../mob/living/simple_animal/parrot.dm | 56 +----- tgstation.dme | 1 + 5 files changed, 178 insertions(+), 56 deletions(-) create mode 100644 code/modules/mob/living/basic/pets/parrot.dm diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 116b933c6ae22..46f32cbcd3577 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -279,6 +279,15 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) +#define CARBON_GENERIC_BODY_ZONES list(\ + BODY_ZONE_HEAD,\ + BODY_ZONE_CHEST,\ + BODY_ZONE_L_ARM,\ + BODY_ZONE_R_ARM,\ + BODY_ZONE_L_LEG,\ + BODY_ZONE_R_LEG,\ +) + #define BODY_ZONE_PRECISE_EYES "eyes" #define BODY_ZONE_PRECISE_MOUTH "mouth" #define BODY_ZONE_PRECISE_GROIN "groin" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm index d26dd4e8c8682..9e4d1744edddf 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -49,11 +49,11 @@ #define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit" ///from base of atom/movable/Hear(): (proc args list(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range)) #define COMSIG_MOVABLE_HEAR "movable_hear" - //#define HEARING_MESSAGE 1 - (I'm pretty sure this is never really used and can be gutted) + #define HEARING_MESSAGE 1 #define HEARING_SPEAKER 2 #define HEARING_LANGUAGE 3 #define HEARING_RAW_MESSAGE 4 - //#define HEARING_RADIO_FREQ 5 + #define HEARING_RADIO_FREQ 5 #define HEARING_SPANS 6 #define HEARING_MESSAGE_MODE 7 #define HEARING_RANGE 8 diff --git a/code/modules/mob/living/basic/pets/parrot.dm b/code/modules/mob/living/basic/pets/parrot.dm new file mode 100644 index 0000000000000..5915abe41de51 --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot.dm @@ -0,0 +1,164 @@ +/// Parrots! Klepto bastards that imitate your speech and hoard your shit. +/mob/living/basic/parrot + name = "parrot" + desc = "The parrot squawks, \"They're a Parrot! BAWWK!\"" + icon = 'icons/mob/simple/animal.dmi' + icon_state = "parrot_fly" + icon_living = "parrot_fly" + icon_dead = "parrot_dead" + density = FALSE + health = 80 + maxHealth = 80 + pass_flags = PASSTABLE | PASSMOB + + speak = list("Hi!","Hello!","Cracker?","BAWWWWK george mellons griffing me!") + speak_emote = list("squawks","says","yells") + emote_hear = list("squawks.","bawks!") + emote_see = list("flutters their wings.") + + speak_chance = 1 //1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s + turns_per_move = 5 + butcher_results = list(/obj/item/food/cracker = 1) + melee_damage_upper = 10 + melee_damage_lower = 5 + + response_help_continuous = "pets" + response_help_simple = "pet" + response_disarm_continuous = "gently moves aside" + response_disarm_simple = "gently move aside" + response_harm_continuous = "swats" + response_harm_simple = "swat" + stop_automated_movement = 1 + combat_mode = TRUE //parrots now start "aggressive" since only player parrots will nuzzle. + attack_verb_continuous = "chomps" + attack_verb_simple = "chomp" + attack_vis_effect = ATTACK_EFFECT_BITE + friendly_verb_continuous = "grooms" + friendly_verb_simple = "groom" + mob_size = MOB_SIZE_SMALL + gold_core_spawnable = FRIENDLY_SPAWN + + /// Icon we use while sitting + var/icon_sit = "parrot_sit" + /// The number of damage we do when we decide to aggro for our lives + var/parrot_damage_upper = 10 + //var/parrot_state = PARROT_WANDER + //var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick. + //var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down + /// Pottential bodyparts for us to attack + var/parrot_dam_zone = CARBON_GENERIC_BODY_ZONES + + //Headset for Poly to yell at engineers :) + var/obj/item/radio/headset/ears = null + + //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. + var/obj/item/held_item = null + + //var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. + //var/parrot_lastmove = null //Updates/Stores position of the parrot while it's moving + //var/parrot_stuck = 0 //If parrot_lastmove hasn't changed, this will increment until it reaches parrot_stuck_threshold + //var/parrot_stuck_threshold = 10 //if this == parrot_stuck, it'll force the parrot back to wandering + + var/list/speech_buffer = list() + var/speech_shuffle_rate = 20 + var/list/available_channels = list() + + ////The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, + ////mobs it wants to attack or mobs that have attacked it + //var/atom/movable/parrot_interest = null + + //Parrots will generally sit on their perch unless something catches their eye. + //These vars store their preffered perch and if they dont have one, what they can use as a perch + //var/obj/parrot_perch = null + var/obj/desired_perches = list( + /obj/machinery/computer, + /obj/machinery/dna_scannernew, + /obj/machinery/nuclearbomb, + /obj/machinery/recharge_station, + /obj/machinery/smartfridge, + /obj/machinery/suit_storage_unit, + /obj/machinery/telecomms, + /obj/machinery/teleport, + /obj/structure/displaycase, + /obj/structure/filingcabinet, + /obj/structure/frame/computer, + ) + + +/mob/living/basic/parrot/Initialize(mapload) + . = ..() + setup_headset() + + AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) + AddElement(/datum/element/simple_flying) + +/mob/living/basic/parrot/Destroy() + // should have cleaned these up on death, but let's be super safe in case that didn't happen + if(!QDELETED(ears)) + QDEL_NULL(ears) + if(!QDELETED(held_item)) + QDEL_NULL(held_item) + return ..() + +/mob/living/simple_animal/parrot/death(gibbed) + if(held_item) + held_item.forceMove(drop_location()) + held_item = null + + if(ears) + ears.forceMove(drop_location()) + ears = null + + //SSmove_manager.stop_looping(src) + + if(!isnull(buckled)) + buckled.unbuckle_mob(src, force = TRUE) + buckled = null + pixel_x = base_pixel_x + pixel_y = base_pixel_y + + return ..() + +/mob/living/basic/parrot/examine(mob/user) + . = ..() + if(stat != DEAD) + return + + if(HAS_MIND_TRAIT(user, TRAIT_NAIVE)) + . += pick( + "It seems tired and shagged out after a long squawk.", + "It seems to be pining for the fjords.", + "It's resting. It's a beautiful bird. Lovely plumage.", + ) + else + . += pick( + "This is a late parrot.", + "This is an ex-parrot.", + "This parrot is no more.", + ) + +/mob/living/basic/parrot/get_status_tab_items() + . = ..() + . += "Held Item: [held_item]" + +/mob/living/basic/parrot/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) + . = ..() + if(speaker != src && prob(50)) //Dont imitate ourselves + if(!radio_freq || prob(10)) + if(speech_buffer.len >= 500) + speech_buffer -= pick(speech_buffer) + speech_buffer |= html_decode(raw_message) + if(speaker == src && !client) //If a parrot squawks in the woods and no one is around to hear it, does it make a sound? This code says yes! + return !!message + +/// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. +/mob/living/basic/parrot/proc/setup_headset() + //return null + var/headset = pick( + /obj/item/radio/headset/headset_cargo, + /obj/item/radio/headset/headset_eng, + /obj/item/radio/headset/headset_med, + /obj/item/radio/headset/headset_sci, + /obj/item/radio/headset/headset_sec, + ) + ears = new headset(src) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index a1f12082ff1b7..48e0b84ff3412 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -122,68 +122,16 @@ /mob/living/simple_animal/parrot/proc/toggle_mode, /mob/living/simple_animal/parrot/proc/perch_mob_player)) - AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) - AddElement(/datum/element/simple_flying) - if(!spawn_headset) - return - if(!ears) - var/headset = pick(/obj/item/radio/headset/headset_sec, \ - /obj/item/radio/headset/headset_eng, \ - /obj/item/radio/headset/headset_med, \ - /obj/item/radio/headset/headset_sci, \ - /obj/item/radio/headset/headset_cargo) - ears = new headset(src) - -/mob/living/simple_animal/parrot/Destroy() - QDEL_NULL(ears) - return ..() -/mob/living/simple_animal/parrot/examine(mob/user) - . = ..() - if(stat != DEAD) - return - if(HAS_MIND_TRAIT(user, TRAIT_NAIVE)) - . += pick( - "It seems tired and shagged out after a long squawk.", - "It seems to be pining for the fjords.", - "It's resting. It's a beautiful bird. Lovely plumage.", - ) - else - . += pick( - "This parrot is no more.", - "This is a late parrot.", - "This is an ex-parrot.", - ) -/mob/living/simple_animal/parrot/death(gibbed) - if(held_item) - held_item.forceMove(drop_location()) - held_item = null - SSmove_manager.stop_looping(src) - if(buckled) - buckled.unbuckle_mob(src,force=1) - buckled = null - pixel_x = base_pixel_x - pixel_y = base_pixel_y - return ..() -/mob/living/simple_animal/parrot/get_status_tab_items() - . = ..() - . += "Held Item: [held_item]" -/mob/living/simple_animal/parrot/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) - . = ..() - if(speaker != src && prob(50)) //Dont imitate ourselves - if(!radio_freq || prob(10)) - if(speech_buffer.len >= 500) - speech_buffer -= pick(speech_buffer) - speech_buffer |= html_decode(raw_message) - if(speaker == src && !client) //If a parrot squawks in the woods and no one is around to hear it, does it make a sound? This code says yes! - return message + + /mob/living/simple_animal/parrot/radio(message, list/message_mods = list(), list/spans, language) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be. . = ..() diff --git a/tgstation.dme b/tgstation.dme index f1c0df8299b7f..8f59a4bf2bb57 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4556,6 +4556,7 @@ #include "code\modules\mob\living\basic\minebots\minebot_ai.dm" #include "code\modules\mob\living\basic\minebots\minebot_upgrades.dm" #include "code\modules\mob\living\basic\pets\fox.dm" +#include "code\modules\mob\living\basic\pets\parrot.dm" #include "code\modules\mob\living\basic\pets\penguin.dm" #include "code\modules\mob\living\basic\pets\pet.dm" #include "code\modules\mob\living\basic\pets\sloth.dm" From e8ad148d6f60ba5e1f48c8b65273f142e1fa0cde Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 4 Sep 2023 23:40:44 -0600 Subject: [PATCH 02/69] RURURUGHHGH --- code/datums/elements/listen_and_repeat.dm | 50 +++++++++++++++++++ .../living/basic/pets/{ => parrot}/parrot.dm | 14 ++---- .../mob/living/simple_animal/parrot.dm | 5 -- tgstation.dme | 3 +- 4 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 code/datums/elements/listen_and_repeat.dm rename code/modules/mob/living/basic/pets/{ => parrot}/parrot.dm (90%) diff --git a/code/datums/elements/listen_and_repeat.dm b/code/datums/elements/listen_and_repeat.dm new file mode 100644 index 0000000000000..06c716a8b2113 --- /dev/null +++ b/code/datums/elements/listen_and_repeat.dm @@ -0,0 +1,50 @@ +/// The maximum number of phrases we can store in our speech buffer +#define MAX_SPEECH_BUFFER_SIZE 500 +/// Tendency we have to ignore radio chatter +#define RADIO_IGNORE_CHANCE 10 + +/// Simple element that will deterministically set a value based on stuff that the source has heard and will then compel the source to repeat it.area +/// Requires a valid AI Blackboard. +/datum/element/listen_and_repeat + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + /// List of things that we've heard and will repeat. + var/list/speech_buffer = null + /// The AI Blackboard Key we assign the value to. + var/blackboard_key = null + /// Probability we actually do anything upon hearing something + var/probability_stat = null + +/datum/element/listen_and_repeat/Attach(datum/target, list/speech_buffer, blackboard_key, probability_stat) + . = ..() + if(!ismovable(target)) + return ELEMENT_INCOMPATIBLE + + if(isnull(probability_stat)) + probability_stat = 50 // default for sanity + + src.speech_buffer = speech_buffer + src.blackboard_key = blackboard_key + + RegisterSignal(target, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) + // register to detach when a client logs in maybe + +/// Called when we hear something. +/datum/element/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments) + SIGNAL_HANDLER + if(prob(probability_stat)) + return + + var/message = passed_arguments[HEARING_MESSAGE] + var/speaker = passed_arguments[HEARING_SPEAKER] + var/over_radio = !!passed_arguments[HEARING_RADIO_FREQ] + if(speaker == source) // don't parrot ourselves + return + + if(over_radio && prob(RADIO_IGNORE_CHANCE)) // tendency to ignore radio chatter + return + + if(LAZYLEN(speech_buffer) >= MAX_SPEECH_BUFFER_SIZE) + LAZYREMOVE(speech_buffer, pick(speech_buffer)) + + LAZYOR(speech_buffer, html_decode(raw_message)) diff --git a/code/modules/mob/living/basic/pets/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm similarity index 90% rename from code/modules/mob/living/basic/pets/parrot.dm rename to code/modules/mob/living/basic/pets/parrot/parrot.dm index 5915abe41de51..cf32b78709644 100644 --- a/code/modules/mob/living/basic/pets/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -1,3 +1,8 @@ +GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( + /datum/strippable_item/parrot_headset, +))) + + /// Parrots! Klepto bastards that imitate your speech and hoard your shit. /mob/living/basic/parrot name = "parrot" @@ -141,15 +146,6 @@ . = ..() . += "Held Item: [held_item]" -/mob/living/basic/parrot/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) - . = ..() - if(speaker != src && prob(50)) //Dont imitate ourselves - if(!radio_freq || prob(10)) - if(speech_buffer.len >= 500) - speech_buffer -= pick(speech_buffer) - speech_buffer |= html_decode(raw_message) - if(speaker == src && !client) //If a parrot squawks in the woods and no one is around to hear it, does it make a sound? This code says yes! - return !!message /// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. /mob/living/basic/parrot/proc/setup_headset() diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 48e0b84ff3412..713a5497ec0d9 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -152,11 +152,6 @@ return ITALICS | REDUCE_RANGE return FALSE - -GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( - /datum/strippable_item/parrot_headset, -))) - /datum/strippable_item/parrot_headset key = STRIPPABLE_ITEM_PARROT_HEADSET diff --git a/tgstation.dme b/tgstation.dme index 8f59a4bf2bb57..4fa612c083c56 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1388,6 +1388,7 @@ #include "code\datums\elements\light_blocking.dm" #include "code\datums\elements\light_eaten.dm" #include "code\datums\elements\light_eater.dm" +#include "code\datums\elements\listen_and_repeat.dm" #include "code\datums\elements\loomable.dm" #include "code\datums\elements\mirage_border.dm" #include "code\datums\elements\mob_grabber.dm" @@ -4556,7 +4557,6 @@ #include "code\modules\mob\living\basic\minebots\minebot_ai.dm" #include "code\modules\mob\living\basic\minebots\minebot_upgrades.dm" #include "code\modules\mob\living\basic\pets\fox.dm" -#include "code\modules\mob\living\basic\pets\parrot.dm" #include "code\modules\mob\living\basic\pets\penguin.dm" #include "code\modules\mob\living\basic\pets\pet.dm" #include "code\modules\mob\living\basic\pets\sloth.dm" @@ -4567,6 +4567,7 @@ #include "code\modules\mob\living\basic\ruin_defender\flesh.dm" #include "code\modules\mob\living\basic\ruin_defender\living_floor.dm" #include "code\modules\mob\living\basic\ruin_defender\skeleton.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard_ai.dm" From d7c2bb363d4b06d30320d8c8f48bf05a3eb9b559 Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 4 Sep 2023 23:49:49 -0600 Subject: [PATCH 03/69] gwak gwak --- code/datums/elements/listen_and_repeat.dm | 11 ++- .../mob/living/basic/pets/parrot/parrot.dm | 4 + .../mob/living/basic/pets/parrot/parrot_ai.dm | 0 .../living/basic/pets/parrot/parrot_items.dm | 86 ++++++++++++++++++ .../basic/pets/parrot/parrot_subtypes.dm | 0 code/modules/mob/living/living_say.dm | 2 +- .../mob/living/simple_animal/parrot.dm | 90 +------------------ tgstation.dme | 3 + 8 files changed, 104 insertions(+), 92 deletions(-) create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai.dm create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_items.dm create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm diff --git a/code/datums/elements/listen_and_repeat.dm b/code/datums/elements/listen_and_repeat.dm index 06c716a8b2113..0387c9444b993 100644 --- a/code/datums/elements/listen_and_repeat.dm +++ b/code/datums/elements/listen_and_repeat.dm @@ -41,10 +41,17 @@ if(speaker == source) // don't parrot ourselves return - if(over_radio && prob(RADIO_IGNORE_CHANCE)) // tendency to ignore radio chatter + if(over_radio && prob(RADIO_IGNORE_CHANCE)) return - if(LAZYLEN(speech_buffer) >= MAX_SPEECH_BUFFER_SIZE) + if(LAZYLEN(speech_buffer) >= MAX_SPEECH_BUFFER_SIZE) // only remove if we're full LAZYREMOVE(speech_buffer, pick(speech_buffer)) LAZYOR(speech_buffer, html_decode(raw_message)) + +/// Called to set a new value for the blackboard key. +/datum/element/listen_and_repeat/proc/set_new_blackboard_key(datum/source) + var/atom/movable/atom_source = source + var/datum/ai_controller/controller = atom_source.ai_controller + + controller.set_blackboard_key(blackboard_key, pick(speech_buffer)) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index cf32b78709644..34281e266e3f3 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -146,6 +146,10 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( . = ..() . += "Held Item: [held_item]" +/mob/living/basic/parrot/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) + if(stat != DEAD) // parrots have evolved to let them fly in space because fucking uhhhhhhhhhh + return TRUE + return ..() /// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. /mob/living/basic/parrot/proc/setup_headset() diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai.dm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm new file mode 100644 index 0000000000000..d13522faf4f8c --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm @@ -0,0 +1,86 @@ +/datum/strippable_item/parrot_headset + key = STRIPPABLE_ITEM_PARROT_HEADSET + +/datum/strippable_item/parrot_headset/get_item(atom/source) + var/mob/living/simple_animal/parrot/parrot_source = source + return istype(parrot_source) ? parrot_source.ears : null + +/datum/strippable_item/parrot_headset/try_equip(atom/source, obj/item/equipping, mob/user) + . = ..() + if (!.) + return FALSE + + if (!istype(equipping, /obj/item/radio/headset)) + to_chat(user, span_warning("[equipping] won't fit!")) + return FALSE + + return TRUE + +// There is no delay for putting a headset on a parrot. +/datum/strippable_item/parrot_headset/start_equip(atom/source, obj/item/equipping, mob/user) + return TRUE + +/datum/strippable_item/parrot_headset/finish_equip(atom/source, obj/item/equipping, mob/user) + var/obj/item/radio/headset/radio = equipping + if (!istype(radio)) + return + + var/mob/living/simple_animal/parrot/parrot_source = source + if (!istype(parrot_source)) + return + + if (!user.transferItemToLoc(radio, source)) + return + + parrot_source.ears = radio + + to_chat(user, span_notice("You fit [radio] onto [source].")) + + parrot_source.available_channels.Cut() + + for (var/channel in radio.channels) + var/channel_to_add + + switch (channel) + if (RADIO_CHANNEL_ENGINEERING) + channel_to_add = RADIO_TOKEN_ENGINEERING + if (RADIO_CHANNEL_COMMAND) + channel_to_add = RADIO_TOKEN_COMMAND + if (RADIO_CHANNEL_SECURITY) + channel_to_add = RADIO_TOKEN_SECURITY + if (RADIO_CHANNEL_SCIENCE) + channel_to_add = RADIO_TOKEN_SCIENCE + if (RADIO_CHANNEL_MEDICAL) + channel_to_add = RADIO_TOKEN_MEDICAL + if (RADIO_CHANNEL_SUPPLY) + channel_to_add = RADIO_TOKEN_SUPPLY + if (RADIO_CHANNEL_SERVICE) + channel_to_add = RADIO_TOKEN_SERVICE + + if (channel_to_add) + parrot_source.available_channels += channel_to_add + + if (radio.translate_binary) + parrot_source.available_channels.Add(MODE_TOKEN_BINARY) + +/datum/strippable_item/parrot_headset/start_unequip(atom/source, mob/user) + . = ..() + if (!.) + return FALSE + + var/mob/living/simple_animal/parrot/parrot_source = source + if (!istype(parrot_source)) + return + + if (!parrot_source.stat) + parrot_source.say("[parrot_source.available_channels.len ? "[pick(parrot_source.available_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + + return TRUE + +/datum/strippable_item/parrot_headset/finish_unequip(atom/source, mob/user) + var/mob/living/simple_animal/parrot/parrot_source = source + if (!istype(parrot_source)) + return + + parrot_source.ears.forceMove(parrot_source.drop_location()) + parrot_source.ears = null diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 1cd72a3cc4046..0cb895d01ed51 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -526,7 +526,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( I.talk_into(src, message, , spans, language, message_mods) return ITALICS | REDUCE_RANGE - return 0 + return NONE /mob/living/say_mod(input, list/message_mods = list()) if(message_mods[WHISPER_MODE] == MODE_WHISPER) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 713a5497ec0d9..da98d5d020e82 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -152,92 +152,7 @@ return ITALICS | REDUCE_RANGE return FALSE -/datum/strippable_item/parrot_headset - key = STRIPPABLE_ITEM_PARROT_HEADSET -/datum/strippable_item/parrot_headset/get_item(atom/source) - var/mob/living/simple_animal/parrot/parrot_source = source - return istype(parrot_source) ? parrot_source.ears : null - -/datum/strippable_item/parrot_headset/try_equip(atom/source, obj/item/equipping, mob/user) - . = ..() - if (!.) - return FALSE - - if (!istype(equipping, /obj/item/radio/headset)) - to_chat(user, span_warning("[equipping] won't fit!")) - return FALSE - - return TRUE - -// There is no delay for putting a headset on a parrot. -/datum/strippable_item/parrot_headset/start_equip(atom/source, obj/item/equipping, mob/user) - return TRUE - -/datum/strippable_item/parrot_headset/finish_equip(atom/source, obj/item/equipping, mob/user) - var/obj/item/radio/headset/radio = equipping - if (!istype(radio)) - return - - var/mob/living/simple_animal/parrot/parrot_source = source - if (!istype(parrot_source)) - return - - if (!user.transferItemToLoc(radio, source)) - return - - parrot_source.ears = radio - - to_chat(user, span_notice("You fit [radio] onto [source].")) - - parrot_source.available_channels.Cut() - - for (var/channel in radio.channels) - var/channel_to_add - - switch (channel) - if (RADIO_CHANNEL_ENGINEERING) - channel_to_add = RADIO_TOKEN_ENGINEERING - if (RADIO_CHANNEL_COMMAND) - channel_to_add = RADIO_TOKEN_COMMAND - if (RADIO_CHANNEL_SECURITY) - channel_to_add = RADIO_TOKEN_SECURITY - if (RADIO_CHANNEL_SCIENCE) - channel_to_add = RADIO_TOKEN_SCIENCE - if (RADIO_CHANNEL_MEDICAL) - channel_to_add = RADIO_TOKEN_MEDICAL - if (RADIO_CHANNEL_SUPPLY) - channel_to_add = RADIO_TOKEN_SUPPLY - if (RADIO_CHANNEL_SERVICE) - channel_to_add = RADIO_TOKEN_SERVICE - - if (channel_to_add) - parrot_source.available_channels += channel_to_add - - if (radio.translate_binary) - parrot_source.available_channels.Add(MODE_TOKEN_BINARY) - -/datum/strippable_item/parrot_headset/start_unequip(atom/source, mob/user) - . = ..() - if (!.) - return FALSE - - var/mob/living/simple_animal/parrot/parrot_source = source - if (!istype(parrot_source)) - return - - if (!parrot_source.stat) - parrot_source.say("[parrot_source.available_channels.len ? "[pick(parrot_source.available_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - - return TRUE - -/datum/strippable_item/parrot_headset/finish_unequip(atom/source, mob/user) - var/mob/living/simple_animal/parrot/parrot_source = source - if (!istype(parrot_source)) - return - - parrot_source.ears.forceMove(parrot_source.drop_location()) - parrot_source.ears = null /* * Attack responces @@ -325,10 +240,7 @@ icon_state = icon_living drop_held_item(0) -/mob/living/simple_animal/parrot/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) - if(!stat) //Birds can fly, fun fact. No I don't care that space doesn't have air. Space parrots bitch - return TRUE - return ..() + /* * AI - Not really intelligent, but I'm calling it AI anyway. */ diff --git a/tgstation.dme b/tgstation.dme index 4fa612c083c56..43c853d77bd39 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4568,6 +4568,9 @@ #include "code\modules\mob\living\basic\ruin_defender\living_floor.dm" #include "code\modules\mob\living\basic\ruin_defender\skeleton.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard_ai.dm" From 6e33017635868704cc76e1ee6f68988afa373543 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 00:08:16 -0600 Subject: [PATCH 04/69] GLORGUISIFSA --- code/__DEFINES/ai/pets.dm | 5 +++++ .../dcs/signals/signals_mob/signals_mob_ai.dm | 1 + .../signals/signals_mob/signals_mob_basic.dm | 3 +++ code/datums/ai/generic/generic_behaviors.dm | 2 ++ .../mob/living/basic/pets/parrot/parrot_ai.dm | 0 .../pets/parrot/parrot_ai/parroting_action.dm | 19 +++++++++++++++++++ tgstation.dme | 2 +- 7 files changed, 31 insertions(+), 1 deletion(-) delete mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai.dm create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index b3ad67ecc068d..6ab4a2e0dca19 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -27,3 +27,8 @@ #define BB_FIND_MOM_TYPES "BB_find_mom_types" ///list of types of mobs we must ignore #define BB_IGNORE_MOM_TYPES "BB_ignore_mom_types" + +/// The current string that this parrot will repeat back to someone +#define BB_PARROT_REPEAT_STRING "BB_parrot_repeat_string" +/// The odds that this parrot will repeat back a string +#define BB_PARROT_REPEAT_PROBABILITY "BB_parrot_repeat_proability" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm index 85630c8e8f041..a04b8e751a0c4 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm @@ -3,3 +3,4 @@ /// Signal sent when a blackboard key is cleared #define COMSIG_AI_BLACKBOARD_KEY_CLEARED(blackboard_key) "ai_blackboard_key_clear_[blackboard_key]" + diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm index 18c6c651435ba..1563b3e7dfee0 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm @@ -5,3 +5,6 @@ ///from the ranged_attacks component for basic mobs: (mob/living/basic/firer, atom/target, modifiers) #define COMSIG_BASICMOB_POST_ATTACK_RANGED "basicmob_post_attack_ranged" + +/// Sent from /datum/ai_planning_subtree/parrot_as_in_repeat() : () +#define COMSIG_PARROT_NEEDS_NEW_PHRASE "parrot_needs_new_phrase" diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm index 4f816de4be30a..962c8d141cd8a 100644 --- a/code/datums/ai/generic/generic_behaviors.dm +++ b/code/datums/ai/generic/generic_behaviors.dm @@ -299,6 +299,8 @@ /datum/ai_behavior/perform_speech /datum/ai_behavior/perform_speech/perform(seconds_per_tick, datum/ai_controller/controller, speech, speech_sound) + . = ..() + var/mob/living/living_pawn = controller.pawn if(!istype(living_pawn)) return diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai.dm deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm new file mode 100644 index 0000000000000..6c43e68464d15 --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -0,0 +1,19 @@ +/// When a parrot... parrots... +/datum/ai_planning_subtree/parrot_as_in_repeat + operational_datums = list(/datum/element/listen_and_repeat) + +/datum/ai_planning_subtree/parrot_as_in_repeat/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + . = ..() + var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] + var/atom/speaking_pawn = controller.pawn + if(prob(controller.blackboard[BB_PARROT_REPEAT_PROBABILITY])) + return + if(!potential_string) + SEND_SIGNAL(speaking_pawn, COMSIG_PARROT_NEEDS_NEW_PHRASE) + potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] + + controller.queue_behavior(/datum/ai_behavior/perform_speech/parrot, potential_string) + +/datum/ai_behavior/perform_speech/parrot + action_cooldown = 2 SECONDS + diff --git a/tgstation.dme b/tgstation.dme index 43c853d77bd39..a8d569a59f9d4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4568,9 +4568,9 @@ #include "code\modules\mob\living\basic\ruin_defender\living_floor.dm" #include "code\modules\mob\living\basic\ruin_defender\skeleton.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot.dm" -#include "code\modules\mob\living\basic\pets\parrot\parrot_ai.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard_ai.dm" From 82b0cc25799fd09213b1e990eff4807820f0f0e7 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 00:21:02 -0600 Subject: [PATCH 05/69] might be having a skill issue rn --- .../signals/signals_mob/signals_mob_basic.dm | 3 ++- code/datums/elements/listen_and_repeat.dm | 20 ++++++++++++++----- .../pets/parrot/parrot_ai/parroting_action.dm | 12 ++++++----- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm index 1563b3e7dfee0..2c856e850c3ca 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm @@ -7,4 +7,5 @@ #define COMSIG_BASICMOB_POST_ATTACK_RANGED "basicmob_post_attack_ranged" /// Sent from /datum/ai_planning_subtree/parrot_as_in_repeat() : () -#define COMSIG_PARROT_NEEDS_NEW_PHRASE "parrot_needs_new_phrase" +#define COMSIG_NEEDS_NEW_PHRASE "parrot_needs_new_phrase" + #define NO_NEW_PHRASE_AVAILABLE (1<<0) //! Cancel to try again later for when we actually get a new phrase diff --git a/code/datums/elements/listen_and_repeat.dm b/code/datums/elements/listen_and_repeat.dm index 0387c9444b993..d0c193c1dca93 100644 --- a/code/datums/elements/listen_and_repeat.dm +++ b/code/datums/elements/listen_and_repeat.dm @@ -8,14 +8,16 @@ /datum/element/listen_and_repeat element_flags = ELEMENT_BESPOKE argument_hash_start_idx = 2 - /// List of things that we've heard and will repeat. - var/list/speech_buffer = null + /// List of things that we start out having in our speech buffer + var/list/desired_phrases = null /// The AI Blackboard Key we assign the value to. var/blackboard_key = null /// Probability we actually do anything upon hearing something var/probability_stat = null + /// List of things that we've heard and will repeat. + var/list/speech_buffer = null -/datum/element/listen_and_repeat/Attach(datum/target, list/speech_buffer, blackboard_key, probability_stat) +/datum/element/listen_and_repeat/Attach(datum/target, list/desired_phrases, blackboard_key, probability_stat) . = ..() if(!ismovable(target)) return ELEMENT_INCOMPATIBLE @@ -23,10 +25,12 @@ if(isnull(probability_stat)) probability_stat = 50 // default for sanity - src.speech_buffer = speech_buffer + if(!isnull(desired_phrases)) + LAZYADD(speech_buffer, desired_phrases) src.blackboard_key = blackboard_key RegisterSignal(target, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) + RegisterSignal(target, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_key)) // register to detach when a client logs in maybe /// Called when we hear something. @@ -51,7 +55,13 @@ /// Called to set a new value for the blackboard key. /datum/element/listen_and_repeat/proc/set_new_blackboard_key(datum/source) + if(LAZYLEN(speech_buffer)) + controller.set_blackboard_key(blackboard_key, null) + return + var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller + var/selected_phrase = pick(speech_buffer) + + controller.set_blackboard_key(blackboard_key, selected_phrase) - controller.set_blackboard_key(blackboard_key, pick(speech_buffer)) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index 6c43e68464d15..641b7d8a1f2e0 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -4,13 +4,15 @@ /datum/ai_planning_subtree/parrot_as_in_repeat/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() - var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] var/atom/speaking_pawn = controller.pawn - if(prob(controller.blackboard[BB_PARROT_REPEAT_PROBABILITY])) + var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] + var/probability = controller.blackboard[BB_PARROT_REPEAT_PROBABILITY] + var/return_value = SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) + + potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] + + if(isnull(potential_string) || prob(probability)) return - if(!potential_string) - SEND_SIGNAL(speaking_pawn, COMSIG_PARROT_NEEDS_NEW_PHRASE) - potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] controller.queue_behavior(/datum/ai_behavior/perform_speech/parrot, potential_string) From cecc56371b766a8f9dd59277892388c8ff406800 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 00:36:55 -0600 Subject: [PATCH 06/69] i barely feel my head --- code/datums/elements/listen_and_repeat.dm | 7 +++--- .../mob/living/basic/pets/parrot/parrot.dm | 5 ++++ .../pets/parrot/parrot_ai/parroting_action.dm | 25 +++++++++++++++++++ .../mob/living/simple_animal/parrot.dm | 25 ------------------- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/code/datums/elements/listen_and_repeat.dm b/code/datums/elements/listen_and_repeat.dm index d0c193c1dca93..4b1e981022080 100644 --- a/code/datums/elements/listen_and_repeat.dm +++ b/code/datums/elements/listen_and_repeat.dm @@ -51,17 +51,16 @@ if(LAZYLEN(speech_buffer) >= MAX_SPEECH_BUFFER_SIZE) // only remove if we're full LAZYREMOVE(speech_buffer, pick(speech_buffer)) - LAZYOR(speech_buffer, html_decode(raw_message)) + LAZYOR(speech_buffer, html_decode(message)) /// Called to set a new value for the blackboard key. /datum/element/listen_and_repeat/proc/set_new_blackboard_key(datum/source) + var/atom/movable/atom_source = source + var/datum/ai_controller/controller = atom_source.ai_controller if(LAZYLEN(speech_buffer)) controller.set_blackboard_key(blackboard_key, null) return - var/atom/movable/atom_source = source - var/datum/ai_controller/controller = atom_source.ai_controller var/selected_phrase = pick(speech_buffer) - controller.set_blackboard_key(blackboard_key, selected_phrase) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 34281e266e3f3..2ed7e19ebfed8 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -162,3 +162,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /obj/item/radio/headset/headset_sec, ) ears = new headset(src) + +/// Gets the available channels that this parrot has access to. Returns a list of the channels we can use. +/mob/living/basic/parrot/proc/get_available_channels() + if(isnull(ears)) + return list() diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index 641b7d8a1f2e0..5d58da28b006f 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -19,3 +19,28 @@ /datum/ai_behavior/perform_speech/parrot action_cooldown = 2 SECONDS +/datum/ai_behavior/perform_speech/parrot/perform(seconds_per_tick, datum/ai_controller/controller, speech, speech_sound) + var/mob/living/basic/parrot/speaking_pawn = controller.pawn + var/list/available_channels = speaking_pawn.get_available_channels() + var/modified_speech = speech + var/use_radio = prob(50) // we might not even use the radio if we even have a channel + +#define HAS_CHANNEL_PREFIX (speech[1] in GLOB.department_radio_prefixes) && (copytext_char(speech, 2, 3) in GLOB.department_radio_keys) // determine if we need to crop the channel prefix + + if(!length(available_channels)) // might not even use the radio at all + if(HAS_CHANNEL_PREFIX) + modified_speech = copytext_char(speech, 3) + + else + if(HAS_CHANNEL_PREFIX) + modified_speech = "[use_radio ? pick(available_channels) : ""][copytext_char(speech, 3)]" + else + modified_speech = "[use_radio ? pick(available_channels) : ""][speech]" + + + living_pawn.say(speech, forced = "AI Controller") + if(speech_sound) + playsound(living_pawn, speech_sound, 80, vary = TRUE) + finish_action(controller, TRUE) + +#undef HAS_CHANNEL_PREFIX diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index da98d5d020e82..91ccb44ab27e0 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -299,31 +299,6 @@ //This way we only call the stuff below once every [sleep_max] ticks. parrot_sleep_dur = parrot_sleep_max - //Cycle through message modes for the headset - if(speak.len) - var/list/newspeak = list() - - if(available_channels.len && src.ears) - for(var/possible_phrase in speak) - - //50/50 chance to not use the radio at all - var/useradio = 0 - if(prob(50)) - useradio = 1 - - if((possible_phrase[1] in GLOB.department_radio_prefixes) && (copytext_char(possible_phrase, 2, 3) in GLOB.department_radio_keys)) - possible_phrase = "[useradio?pick(available_channels):""][copytext_char(possible_phrase, 3)]" //crop out the channel prefix - else - possible_phrase = "[useradio?pick(available_channels):""][possible_phrase]" - - newspeak.Add(possible_phrase) - - else //If we have no headset or channels to use, dont try to use any! - for(var/possible_phrase in speak) - if((possible_phrase[1] in GLOB.department_radio_prefixes) && (copytext_char(possible_phrase, 2, 3) in GLOB.department_radio_keys)) - possible_phrase = copytext_char(possible_phrase, 3) //crop out the channel prefix - newspeak.Add(possible_phrase) - speak = newspeak //Search for item to steal set_parrot_interest(search_for_item()) From e86404b03b67ce8ccf86e9164ac4166a63875954 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 00:45:23 -0600 Subject: [PATCH 07/69] JESUS CHRIST --- .../mob/living/basic/pets/parrot/parrot.dm | 20 ++++++++++++++++--- .../pets/parrot/parrot_ai/parroting_action.dm | 4 ++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 2ed7e19ebfed8..4b83d903ffe0b 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -64,9 +64,10 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( //var/parrot_stuck = 0 //If parrot_lastmove hasn't changed, this will increment until it reaches parrot_stuck_threshold //var/parrot_stuck_threshold = 10 //if this == parrot_stuck, it'll force the parrot back to wandering - var/list/speech_buffer = list() - var/speech_shuffle_rate = 20 - var/list/available_channels = list() + /// The blackboard key we use to store the string we're repeating + var/speech_blackboard_key = BB_PARROT_REPEAT_STRING + /// The generic probability odds we have to do a speech-related action + var/speech_probability_rate = 25 ////The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, ////mobs it wants to attack or mobs that have attacked it @@ -94,6 +95,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( . = ..() setup_headset() + AddElement(/datum/element/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) @@ -163,6 +165,18 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( ) ears = new headset(src) +/// Gets a static list of phrases we wish to pass to the element. +/mob/living/basic/parrot/proc/get_static_list_of_phrases() + var/static/list/default_phrases = list( + "BAWWWWK george mellons griffing me!", + "Cracker?", + "Hello!", + "Hi!", + ) + + return default_phrases + + /// Gets the available channels that this parrot has access to. Returns a list of the channels we can use. /mob/living/basic/parrot/proc/get_available_channels() if(isnull(ears)) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index 5d58da28b006f..0bf9c53b7ab12 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -38,9 +38,9 @@ modified_speech = "[use_radio ? pick(available_channels) : ""][speech]" - living_pawn.say(speech, forced = "AI Controller") + speaking_pawn.say(speech, forced = "AI Controller") if(speech_sound) - playsound(living_pawn, speech_sound, 80, vary = TRUE) + playsound(speaking_pawn, speech_sound, 80, vary = TRUE) finish_action(controller, TRUE) #undef HAS_CHANNEL_PREFIX From 937c7112b47e6923253c0d15a16b22697eb6d81b Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 01:07:36 -0600 Subject: [PATCH 08/69] alright that should be a good chunk of memory --- code/__DEFINES/ai/pets.dm | 2 + code/datums/elements/listen_and_repeat.dm | 9 ++ .../mob/living/basic/pets/parrot/parrot.dm | 10 +- .../basic/pets/parrot/parrot_subtypes.dm | 116 ++++++++++++++++++ .../mob/living/simple_animal/parrot.dm | 88 +++---------- 5 files changed, 145 insertions(+), 80 deletions(-) diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index 6ab4a2e0dca19..d0c98d39ff8e3 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -32,3 +32,5 @@ #define BB_PARROT_REPEAT_STRING "BB_parrot_repeat_string" /// The odds that this parrot will repeat back a string #define BB_PARROT_REPEAT_PROBABILITY "BB_parrot_repeat_proability" +/// A copy of the string buffer that we end the shift with. DO NOT ACCESS THIS DIRECTLY - YOU SHOULD USE THE ELEMENT IN MOST CASES +#define BB_EXPORTABLE_STRING_BUFFER_LIST "BB_parrot_repeat_string_buffer" diff --git a/code/datums/elements/listen_and_repeat.dm b/code/datums/elements/listen_and_repeat.dm index 4b1e981022080..2178cd0a0ce13 100644 --- a/code/datums/elements/listen_and_repeat.dm +++ b/code/datums/elements/listen_and_repeat.dm @@ -31,6 +31,7 @@ RegisterSignal(target, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) RegisterSignal(target, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_key)) + RegisterSignal(target, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(export_speech_buffer)) // register to detach when a client logs in maybe /// Called when we hear something. @@ -64,3 +65,11 @@ var/selected_phrase = pick(speech_buffer) controller.set_blackboard_key(blackboard_key, selected_phrase) +/// Exports all the speech buffer data to a dedicated blackboard key on the source. +/datum/element/listen_and_repeat/proc/on_write_memory(datum/source, dead, gibbed) + var/atom/movable/atom_source = source + var/datum/ai_controller/controller = atom_source.ai_controller + if(LAZYLEN(speech_buffer)) // what? well whatever let's just move on + return + + controller.set_blackboard_key(BB_EXPORTABLE_STRING_BUFFER_LIST, speech_buffer.Copy()) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 4b83d903ffe0b..9cdeedaad4492 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -155,15 +155,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. /mob/living/basic/parrot/proc/setup_headset() - //return null - var/headset = pick( - /obj/item/radio/headset/headset_cargo, - /obj/item/radio/headset/headset_eng, - /obj/item/radio/headset/headset_med, - /obj/item/radio/headset/headset_sci, - /obj/item/radio/headset/headset_sec, - ) - ears = new headset(src) + return /// Gets a static list of phrases we wish to pass to the element. /mob/living/basic/parrot/proc/get_static_list_of_phrases() diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm index e69de29bb2d1d..a188d8ae11dc0 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm @@ -0,0 +1,116 @@ +/// 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." + speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN MODSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE") + gold_core_spawnable = NO_SPAWN + speak_chance = 3 + + /// 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/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)) + phrases_to_return += read_memory() + + if(rounds_survived == longest_survival) + speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?") + desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]." + speak_chance = 20 //His hubris has made him more annoying/easier to justify killing + add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY) + else if(rounds_survived == longest_deathstreak) + speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!") + desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..." + add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) + else if(rounds_survived > 0) + speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!") + desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!" + else + speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") + + return phrases_to_return + +/// 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 + 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 + +/mob/living/basic/parrot/poly/Write_Memory(dead, gibbed) + . = ..() + if(!.) + return + + var/file_path = "data/npc_saves/Poly.json" + var/list/file_data = list() + + var/list/exportable_speech_buffer = 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 + + var/formatted_data +#if DM_VERSION >= 515 + formatted_data = json_encode(file_data, JSON_PRETTY_PRINT) +#else + formatted_data = json_encode(file_data) +#endif + + rustg_file_write(json_encode(formatted_data), file_path) + +/mob/living/basic/parrot/poly/setup_headset() + ears = new /obj/item/radio/headset/headset_eng(src) + +/// Parrot that will just randomly spawn with a headset. Nothing too special beyond that. +/mob/living/basic/parrot/headsetted + +/// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. +/mob/living/basic/parrot/headsetted/setup_headset() + var/headset = pick( + /obj/item/radio/headset/headset_cargo, + /obj/item/radio/headset/headset_eng, + /obj/item/radio/headset/headset_med, + /obj/item/radio/headset/headset_sci, + /obj/item/radio/headset/headset_sec, + ) + ears = new headset(src) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 91ccb44ab27e0..4cb55a572010f 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -738,26 +738,14 @@ to_chat(src, span_notice("You will now [combat_mode ? "Harm" : "Help"] others.")) return -/mob/living/simple_animal/parrot/natural - spawn_headset = FALSE /* * Sub-types */ -/mob/living/simple_animal/parrot/poly - name = "Poly" - desc = "Poly the Parrot. An expert on quantum cracker theory." - speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN MODSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE") - gold_core_spawnable = NO_SPAWN - speak_chance = 3 - var/memory_saved = FALSE - var/rounds_survived = 0 - var/longest_survival = 0 - var/longest_deathstreak = 0 /mob/living/simple_animal/parrot/poly/Initialize(mapload) - ears = new /obj/item/radio/headset/headset_eng(src) + if(SStts.tts_enabled) voice = pick(SStts.available_speakers) if(SStts.pitch_enabled) @@ -770,20 +758,20 @@ available_channels = list(":e") Read_Memory() - if(rounds_survived == longest_survival) - speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?") - desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]." - speak_chance = 20 //His hubris has made him more annoying/easier to justify killing - add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY) - else if(rounds_survived == longest_deathstreak) - speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!") - desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..." - add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) - else if(rounds_survived > 0) - speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!") - desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!" - else - speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") + //if(rounds_survived == longest_survival) + // speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?") + // desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]." + // speak_chance = 20 //His hubris has made him more annoying/easier to justify killing + // add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY) + //else if(rounds_survived == longest_deathstreak) + // speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!") + // desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..." + // add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) + //else if(rounds_survived > 0) + // speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!") + // desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!" + //else + // speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") . = ..() @@ -809,50 +797,8 @@ G.key = key return ..() -/mob/living/simple_animal/parrot/poly/proc/Read_Memory() - if(fexists("data/npc_saves/Poly.sav")) //legacy compatability to convert old format to new - var/savefile/S = new /savefile("data/npc_saves/Poly.sav") - S["phrases"] >> speech_buffer - S["roundssurvived"] >> rounds_survived - S["longestsurvival"] >> longest_survival - S["longestdeathstreak"] >> longest_deathstreak - fdel("data/npc_saves/Poly.sav") - else - var/json_file = file("data/npc_saves/Poly.json") - if(!fexists(json_file)) - return - var/list/json = json_decode(file2text(json_file)) - speech_buffer = json["phrases"] - rounds_survived = json["roundssurvived"] - longest_survival = json["longestsurvival"] - longest_deathstreak = json["longestdeathstreak"] - if(!islist(speech_buffer)) - speech_buffer = list() - -/mob/living/simple_animal/parrot/poly/Write_Memory(dead, gibbed) - . = ..() - if(!.) - return - var/json_file = file("data/npc_saves/Poly.json") - var/list/file_data = list() - if(islist(speech_buffer)) - file_data["phrases"] = 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 - fdel(json_file) - WRITE_FILE(json_file, json_encode(file_data)) + + /mob/living/simple_animal/parrot/poly/ghost name = "The Ghost of Poly" From f5f4bb87ea626823a8df69c039a5500ee8fbe632 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 01:07:36 -0600 Subject: [PATCH 09/69] alright that should be a good chunk of memory --- code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm index a188d8ae11dc0..188ee5160b5c2 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm @@ -90,6 +90,7 @@ file_data["longestdeathstreak"] = longest_deathstreak var/formatted_data + #if DM_VERSION >= 515 formatted_data = json_encode(file_data, JSON_PRETTY_PRINT) #else From 54508a9042022af4ec0d57c02d59b71cbbecf721 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 10:47:50 -0600 Subject: [PATCH 10/69] code kleenup --- .../listen_and_repeat.dm | 20 ++++++------- .../basic/pets/parrot/parrot_subtypes.dm | 4 +-- .../mob/living/simple_animal/parrot.dm | 28 +++++++++---------- tgstation.dme | 2 +- 4 files changed, 27 insertions(+), 27 deletions(-) rename code/datums/{elements => components}/listen_and_repeat.dm (76%) diff --git a/code/datums/elements/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm similarity index 76% rename from code/datums/elements/listen_and_repeat.dm rename to code/datums/components/listen_and_repeat.dm index 2178cd0a0ce13..59ff7b1a22619 100644 --- a/code/datums/elements/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -5,9 +5,7 @@ /// Simple element that will deterministically set a value based on stuff that the source has heard and will then compel the source to repeat it.area /// Requires a valid AI Blackboard. -/datum/element/listen_and_repeat - element_flags = ELEMENT_BESPOKE - argument_hash_start_idx = 2 +/datum/component/listen_and_repeat /// List of things that we start out having in our speech buffer var/list/desired_phrases = null /// The AI Blackboard Key we assign the value to. @@ -17,7 +15,7 @@ /// List of things that we've heard and will repeat. var/list/speech_buffer = null -/datum/element/listen_and_repeat/Attach(datum/target, list/desired_phrases, blackboard_key, probability_stat) +/datum/component/listen_and_repeat/Initialize(datum/target, list/desired_phrases, blackboard_key, probability_stat) . = ..() if(!ismovable(target)) return ELEMENT_INCOMPATIBLE @@ -31,11 +29,11 @@ RegisterSignal(target, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) RegisterSignal(target, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_key)) - RegisterSignal(target, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(export_speech_buffer)) + RegisterSignal(target, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory)) // register to detach when a client logs in maybe /// Called when we hear something. -/datum/element/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments) +/datum/component/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments) SIGNAL_HANDLER if(prob(probability_stat)) return @@ -49,13 +47,15 @@ if(over_radio && prob(RADIO_IGNORE_CHANCE)) return - if(LAZYLEN(speech_buffer) >= MAX_SPEECH_BUFFER_SIZE) // only remove if we're full - LAZYREMOVE(speech_buffer, pick(speech_buffer)) + var/number_of_excess_strings = LAZYLEN(speech_buffer) - MAX_SPEECH_BUFFER_SIZE + if(number_of_excess_strings > 0) // only remove if we're overfull + for(var/i in 1 to number_of_excess_strings) + LAZYREMOVE(speech_buffer, pick(speech_buffer)) LAZYOR(speech_buffer, html_decode(message)) /// Called to set a new value for the blackboard key. -/datum/element/listen_and_repeat/proc/set_new_blackboard_key(datum/source) +/datum/component/listen_and_repeat/proc/set_new_blackboard_key(datum/source) var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller if(LAZYLEN(speech_buffer)) @@ -66,7 +66,7 @@ controller.set_blackboard_key(blackboard_key, selected_phrase) /// Exports all the speech buffer data to a dedicated blackboard key on the source. -/datum/element/listen_and_repeat/proc/on_write_memory(datum/source, dead, gibbed) +/datum/component/listen_and_repeat/proc/on_write_memory(datum/source, dead, gibbed) var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller if(LAZYLEN(speech_buffer)) // what? well whatever let's just move on diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm index 188ee5160b5c2..9342ec314f77e 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm @@ -69,7 +69,7 @@ var/file_path = "data/npc_saves/Poly.json" var/list/file_data = list() - var/list/exportable_speech_buffer = controller.blackboard[BB_EXPORTABLE_STRING_BUFFER_LIST] // should have been populated when we sent the signal out on parent + 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 @@ -97,7 +97,7 @@ formatted_data = json_encode(file_data) #endif - rustg_file_write(json_encode(formatted_data), file_path) + rustg_file_write(formatted_data, file_path) /mob/living/basic/parrot/poly/setup_headset() ears = new /obj/item/radio/headset/headset_eng(src) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 4cb55a572010f..bea7bb9606f66 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -758,20 +758,20 @@ available_channels = list(":e") Read_Memory() - //if(rounds_survived == longest_survival) - // speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?") - // desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]." - // speak_chance = 20 //His hubris has made him more annoying/easier to justify killing - // add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY) - //else if(rounds_survived == longest_deathstreak) - // speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!") - // desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..." - // add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) - //else if(rounds_survived > 0) - // speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!") - // desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!" - //else - // speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") + if(rounds_survived == longest_survival) + speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?") + desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]." + speak_chance = 20 //His hubris has made him more annoying/easier to justify killing + add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY) + else if(rounds_survived == longest_deathstreak) + speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!") + desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..." + add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) + else if(rounds_survived > 0) + speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!") + desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!" + else + speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") . = ..() diff --git a/tgstation.dme b/tgstation.dme index a8d569a59f9d4..5859cf7b2a291 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1075,6 +1075,7 @@ #include "code\datums\components\leash.dm" #include "code\datums\components\life_link.dm" #include "code\datums\components\light_eater.dm" +#include "code\datums\components\listen_and_repeat.dm" #include "code\datums\components\ling_decoy_brain.dm" #include "code\datums\components\lock_on_cursor.dm" #include "code\datums\components\magnet.dm" @@ -1388,7 +1389,6 @@ #include "code\datums\elements\light_blocking.dm" #include "code\datums\elements\light_eaten.dm" #include "code\datums\elements\light_eater.dm" -#include "code\datums\elements\listen_and_repeat.dm" #include "code\datums\elements\loomable.dm" #include "code\datums\elements\mirage_border.dm" #include "code\datums\elements\mob_grabber.dm" From b0b1d71e12c578b7354956943f37c6bba6215728 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:00:02 -0600 Subject: [PATCH 11/69] glorgisisiss --- .../basic/pets/parrot/parrot_subtypes.dm | 67 ++++++++++++++----- .../mob/living/simple_animal/parrot.dm | 16 +---- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm index 9342ec314f77e..d4d3a5a165c82 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm @@ -1,3 +1,12 @@ +/// 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" + /// The classically famous compadre to the Chief Engineer, Poly. /mob/living/basic/parrot/poly name = "Poly" @@ -17,26 +26,39 @@ /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)) - phrases_to_return += read_memory() - - if(rounds_survived == longest_survival) - speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?") - desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]." - speak_chance = 20 //His hubris has made him more annoying/easier to justify killing - add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY) - else if(rounds_survived == longest_deathstreak) - speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!") - desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..." - add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) - else if(rounds_survived > 0) - speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!") - desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!" - else - speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") + if(length(phrases_to_return)) + return phrases_to_return + + phrases_to_return += read_memory() // must come first!!! + 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) @@ -61,6 +83,17 @@ 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(!.) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index bea7bb9606f66..00309aae3c359 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -757,21 +757,7 @@ voice_filter = "rubberband=pitch=1.5" // Use the filter to pitch up if we can't naturally pitch up. available_channels = list(":e") - Read_Memory() - if(rounds_survived == longest_survival) - speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?") - desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]." - speak_chance = 20 //His hubris has made him more annoying/easier to justify killing - add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY) - else if(rounds_survived == longest_deathstreak) - speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!") - desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..." - add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) - else if(rounds_survived > 0) - speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!") - desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!" - else - speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") + . = ..() From 295441af67d282f1829cd14dc424a2f7fe29777c Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:12:06 -0600 Subject: [PATCH 12/69] fucking uuuhhhhhh --- .../mob/living/basic/pets/parrot/parrot.dm | 10 +++++-- .../basic/pets/parrot/parrot_subtypes.dm | 28 +++++++++++++++++++ .../mob/living/simple_animal/parrot.dm | 20 ------------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 9cdeedaad4492..17a09deac0043 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -95,7 +95,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( . = ..() setup_headset() - AddElement(/datum/element/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate) + AddElement(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) @@ -171,5 +171,11 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Gets the available channels that this parrot has access to. Returns a list of the channels we can use. /mob/living/basic/parrot/proc/get_available_channels() + var/list/returnable_list = list() if(isnull(ears)) - return list() + return returnable_list + + for(var/channel in ears.channels) + GLOB.channel_tokens[channels[channel]] // will return something like ":e" or ":c" y'know + + return returnable_list diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm index d4d3a5a165c82..e5bd6b970bbb7 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm @@ -24,6 +24,34 @@ /// How many rounds in a row have we been dead for? var/longest_deathstreak = 0 +/mob/living/basic/parrot/poly/Initialize(mapload) + . = ..() + if(SStts.tts_enabled) + 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. + + REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly! + +/mob/living/simple_animal/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/simple_animal/parrot/poly/ghost/G = new(loc) // san7890 make this the transfer mob proc + if(mind) + mind.transfer_to(G) + else + G.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)) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 00309aae3c359..f92d09236542e 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -744,26 +744,6 @@ -/mob/living/simple_animal/parrot/poly/Initialize(mapload) - - if(SStts.tts_enabled) - 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. - - available_channels = list(":e") - - - . = ..() - - // Ensure 1 Poly exists - REGISTER_REQUIRED_MAP_ITEM(1, 1) - /mob/living/simple_animal/parrot/poly/Life(seconds_per_tick = SSMOBS_DT, times_fired) if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) Write_Memory(FALSE) From 40594b22c2e3fd5b987a14876912cebfacc1db45 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:16:15 -0600 Subject: [PATCH 13/69] almost time to make sure the speech works --- .../mob/living/basic/pets/parrot/parrot.dm | 20 ++ .../basic/pets/parrot/parrot_subtypes.dm | 1 - .../mob/living/basic/pets/parrot/poly.dm | 182 ++++++++++++++++++ .../mob/living/simple_animal/parrot.dm | 43 ----- tgstation.dme | 1 + 5 files changed, 203 insertions(+), 44 deletions(-) create mode 100644 code/modules/mob/living/basic/pets/parrot/poly.dm diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 17a09deac0043..47e40a85d1ca8 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -153,6 +153,26 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return TRUE return ..() +/mob/living/basic/parrot/radio(message, list/message_mods = list(), list/spans, language) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be. + . = ..() + if(.) + return + + if(message_mods[MODE_HEADSET]) + if(ears) + ears.talk_into(src, message, , spans, language, message_mods) + return ITALICS | REDUCE_RANGE + else if(message_mods[RADIO_EXTENSION] == MODE_DEPARTMENT) + if(ears) + ears.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods) + return ITALICS | REDUCE_RANGE + else if(message_mods[RADIO_EXTENSION] in GLOB.radiochannels) + if(ears) + ears.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods) + return ITALICS | REDUCE_RANGE + + return NONE + /// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. /mob/living/basic/parrot/proc/setup_headset() return diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm index e5bd6b970bbb7..1bedb4f6a7425 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm @@ -151,7 +151,6 @@ file_data["longestdeathstreak"] = longest_deathstreak var/formatted_data - #if DM_VERSION >= 515 formatted_data = json_encode(file_data, JSON_PRETTY_PRINT) #else diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm new file mode 100644 index 0000000000000..3ccd83508b6fd --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -0,0 +1,182 @@ +/// 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" + +/// 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." + speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN MODSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE") + gold_core_spawnable = NO_SPAWN + speak_chance = 3 + + /// 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(SStts.tts_enabled) + 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. + + REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly! + +/mob/living/simple_animal/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/simple_animal/parrot/poly/ghost/G = new(loc) // san7890 make this the transfer mob proc + if(mind) + mind.transfer_to(G) + else + G.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!!! + 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 + 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(!.) + return + + 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 + + var/formatted_data +#if DM_VERSION >= 515 + formatted_data = json_encode(file_data, JSON_PRETTY_PRINT) +#else + formatted_data = json_encode(file_data) +#endif + + rustg_file_write(formatted_data, file_path) + +/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" + speak_chance = 20 + 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) + +/mob/living/basic/parrot/poly/ghost/Initialize(mapload) + memory_saved = TRUE //At this point nothing is saved + return ..() + +#undef POLY_DEFAULT +#undef POLY_LONGEST_SURVIVAL +#undef POLY_BEATING_DEATHSTREAK +#undef POLY_CONSECUTIVE_ROUND diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index f92d09236542e..cfdacfdac307a 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -133,25 +133,6 @@ -/mob/living/simple_animal/parrot/radio(message, list/message_mods = list(), list/spans, language) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be. - . = ..() - if(.) - return - - if(message_mods[MODE_HEADSET]) - if(ears) - ears.talk_into(src, message, , spans, language, message_mods) - return ITALICS | REDUCE_RANGE - else if(message_mods[RADIO_EXTENSION] == MODE_DEPARTMENT) - if(ears) - ears.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods) - return ITALICS | REDUCE_RANGE - else if(message_mods[RADIO_EXTENSION] in GLOB.radiochannels) - if(ears) - ears.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods) - return ITALICS | REDUCE_RANGE - - return FALSE /* @@ -750,35 +731,11 @@ memory_saved = TRUE ..() -/mob/living/simple_animal/parrot/poly/death(gibbed) - if(HAS_TRAIT(src, TRAIT_DONT_WRITE_MEMORY)) - return ..() // Don't read memory either. - if(!memory_saved) - Write_Memory(TRUE) - if(rounds_survived == longest_survival || rounds_survived == longest_deathstreak || prob(0.666)) - var/mob/living/simple_animal/parrot/poly/ghost/G = new(loc) - if(mind) - mind.transfer_to(G) - else - G.key = key - return ..() -/mob/living/simple_animal/parrot/poly/ghost - name = "The Ghost of Poly" - desc = "Doomed to squawk the Earth." - color = "#FFFFFF77" - speak_chance = 20 - 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) -/mob/living/simple_animal/parrot/poly/ghost/Initialize(mapload) - memory_saved = TRUE //At this point nothing is saved - . = ..() /mob/living/simple_animal/parrot/poly/ghost/handle_automated_speech() if(ismob(loc)) diff --git a/tgstation.dme b/tgstation.dme index 5859cf7b2a291..79c04e26b4582 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4570,6 +4570,7 @@ #include "code\modules\mob\living\basic\pets\parrot\parrot.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" +#include "code\modules\mob\living\basic\pets\parrot\poly.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" From 28e89d4ef74624d09403075b525abcaa3fc66266 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:20:05 -0600 Subject: [PATCH 14/69] makes it compile question mark --- code/datums/components/listen_and_repeat.dm | 12 +++++------ .../mob/living/basic/pets/parrot/parrot.dm | 21 ++++++++++--------- .../pets/parrot/parrot_ai/parroting_action.dm | 4 ++-- .../mob/living/basic/pets/parrot/poly.dm | 10 +++++---- .../mob/living/simple_animal/parrot.dm | 8 +++---- code/modules/research/experimentor.dm | 2 +- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index 59ff7b1a22619..b8e0fc8c91841 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -15,10 +15,10 @@ /// List of things that we've heard and will repeat. var/list/speech_buffer = null -/datum/component/listen_and_repeat/Initialize(datum/target, list/desired_phrases, blackboard_key, probability_stat) +/datum/component/listen_and_repeat/Initialize(list/desired_phrases, blackboard_key, probability_stat) . = ..() - if(!ismovable(target)) - return ELEMENT_INCOMPATIBLE + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE if(isnull(probability_stat)) probability_stat = 50 // default for sanity @@ -27,9 +27,9 @@ LAZYADD(speech_buffer, desired_phrases) src.blackboard_key = blackboard_key - RegisterSignal(target, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) - RegisterSignal(target, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_key)) - RegisterSignal(target, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory)) + RegisterSignal(parent, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) + RegisterSignal(parent, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_key)) + RegisterSignal(parent, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory)) // register to detach when a client logs in maybe /// Called when we hear something. diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 47e40a85d1ca8..da397f76b9bf6 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -16,13 +16,13 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( maxHealth = 80 pass_flags = PASSTABLE | PASSMOB - speak = list("Hi!","Hello!","Cracker?","BAWWWWK george mellons griffing me!") - speak_emote = list("squawks","says","yells") - emote_hear = list("squawks.","bawks!") - emote_see = list("flutters their wings.") + //speak = list("Hi!","Hello!","Cracker?","BAWWWWK george mellons griffing me!") + //speak_emote = list("squawks","says","yells") + //emote_hear = list("squawks.","bawks!") + //emote_see = list("flutters their wings.") - speak_chance = 1 //1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s - turns_per_move = 5 + //speak_chance = 1 //1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s + //turns_per_move = 5 butcher_results = list(/obj/item/food/cracker = 1) melee_damage_upper = 10 melee_damage_lower = 5 @@ -33,7 +33,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( response_disarm_simple = "gently move aside" response_harm_continuous = "swats" response_harm_simple = "swat" - stop_automated_movement = 1 + //stop_automated_movement = 1 combat_mode = TRUE //parrots now start "aggressive" since only player parrots will nuzzle. attack_verb_continuous = "chomps" attack_verb_simple = "chomp" @@ -95,7 +95,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( . = ..() setup_headset() - AddElement(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate) + AddComponent(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) @@ -195,7 +195,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if(isnull(ears)) return returnable_list - for(var/channel in ears.channels) - GLOB.channel_tokens[channels[channel]] // will return something like ":e" or ":c" y'know + var/list/headset_channels = ears.channels + for(var/channel in headset_channels) + returnable_list += GLOB.channel_tokens[headset_channels[channel]] // will return something like ":e" or ":c" y'know return returnable_list diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index 0bf9c53b7ab12..13b7e02a2f048 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -1,6 +1,6 @@ /// When a parrot... parrots... /datum/ai_planning_subtree/parrot_as_in_repeat - operational_datums = list(/datum/element/listen_and_repeat) + operational_datums = list(/datum/component/listen_and_repeat) /datum/ai_planning_subtree/parrot_as_in_repeat/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() @@ -38,7 +38,7 @@ modified_speech = "[use_radio ? pick(available_channels) : ""][speech]" - speaking_pawn.say(speech, forced = "AI Controller") + speaking_pawn.say(modified_speech, forced = "AI Controller") if(speech_sound) playsound(speaking_pawn, speech_sound, 80, vary = TRUE) finish_action(controller, TRUE) diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 3ccd83508b6fd..860d572131c87 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -11,9 +11,9 @@ /mob/living/basic/parrot/poly name = "Poly" desc = "Poly the Parrot. An expert on quantum cracker theory." - speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN MODSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE") + //speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN MODSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE") gold_core_spawnable = NO_SPAWN - speak_chance = 3 + //speak_chance = 3 /// Did we write the memory to disk? var/memory_saved = FALSE @@ -37,8 +37,9 @@ voice_filter = "rubberband=pitch=1.5" // Use the filter to pitch up if we can't naturally pitch up. REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly! + update_appearance() -/mob/living/simple_animal/parrot/poly/death(gibbed) +/mob/living/basic/parrot/poly/death(gibbed) if(HAS_TRAIT(src, TRAIT_DONT_WRITE_MEMORY)) return ..() // Don't read memory either. if(!memory_saved) @@ -81,6 +82,7 @@ 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) @@ -166,7 +168,7 @@ name = "The Ghost of Poly" desc = "Doomed to squawk the Earth." color = "#FFFFFF77" - speak_chance = 20 + //speak_chance = 20 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 diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index cfdacfdac307a..0468e0d8b22a7 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -726,10 +726,10 @@ /mob/living/simple_animal/parrot/poly/Life(seconds_per_tick = SSMOBS_DT, times_fired) - if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) - Write_Memory(FALSE) - memory_saved = TRUE - ..() +// if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) +// Write_Memory(FALSE) +// memory_saved = TRUE +// ..() diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index 9d2e200b672da..4bbc9969b8272 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -644,7 +644,7 @@ /mob/living/basic/pet/dog/corgi, /mob/living/basic/pet/dog/pug, /mob/living/basic/pet/fox, - /mob/living/simple_animal/parrot/natural, + /mob/living/basic/parrot, /mob/living/simple_animal/pet/cat, ) for(var/counter in 1 to rand(1, 25)) From 9d4622c613c3b6e85bd3e664ea9008c45b47ef03 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:35:24 -0600 Subject: [PATCH 15/69] more tinkering --- .../mob/living/basic/pets/parrot/parrot.dm | 2 ++ .../pets/parrot/parrot_ai/parrot_controller.dm | 16 ++++++++++++++++ .../modules/mob/living/basic/pets/parrot/poly.dm | 7 ++++--- tgstation.dme | 1 + 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index da397f76b9bf6..4b7b4f51108c7 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -43,6 +43,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( mob_size = MOB_SIZE_SMALL gold_core_spawnable = FRIENDLY_SPAWN + ai_controller = /datum/ai_controller/basic_controller/parrot + /// Icon we use while sitting var/icon_sit = "parrot_sit" /// The number of damage we do when we decide to aggro for our lives diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm new file mode 100644 index 0000000000000..9904386c65025 --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm @@ -0,0 +1,16 @@ +/datum/ai_controller/basic_controller/parrot + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + ) + + ai_traits = STOP_MOVING_WHEN_PULLED + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + + planning_subtrees = list( + /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, + /datum/ai_planning_subtree/flee_target, + /datum/ai_planning_subtree/target_retaliate, + /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can + /datum/ai_planning_subtree/basic_melee_attack_subtree, + ) diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 860d572131c87..f93468f3bb25f 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -46,11 +46,11 @@ 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/simple_animal/parrot/poly/ghost/G = new(loc) // san7890 make this the transfer mob proc + var/mob/living/basic/parrot/poly/ghost/specter = new(loc) // san7890 make this the transfer mob proc if(mind) - mind.transfer_to(G) + mind.transfer_to(specter) else - G.key = key + 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 @@ -160,6 +160,7 @@ #endif rustg_file_write(formatted_data, file_path) + memory_saved = TRUE /mob/living/basic/parrot/poly/setup_headset() ears = new /obj/item/radio/headset/headset_eng(src) diff --git a/tgstation.dme b/tgstation.dme index 79c04e26b4582..eeed4ab08492e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4571,6 +4571,7 @@ #include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" #include "code\modules\mob\living\basic\pets\parrot\poly.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_controller.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" From cf1f53043a85817ed34ef83e82b1563c0684547b Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:42:23 -0600 Subject: [PATCH 16/69] MOAR --- code/datums/components/listen_and_repeat.dm | 4 ++-- code/modules/mob/living/basic/pets/parrot/parrot.dm | 1 + .../basic/pets/parrot/parrot_ai/parroting_action.dm | 8 +++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index b8e0fc8c91841..e10a901b71cfe 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -58,9 +58,9 @@ /datum/component/listen_and_repeat/proc/set_new_blackboard_key(datum/source) var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller - if(LAZYLEN(speech_buffer)) + if(!LAZYLEN(speech_buffer)) controller.set_blackboard_key(blackboard_key, null) - return + return NO_NEW_PHRASE_AVAILABLE var/selected_phrase = pick(speech_buffer) controller.set_blackboard_key(blackboard_key, selected_phrase) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 4b7b4f51108c7..fc3e684c81ce8 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -98,6 +98,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( setup_headset() AddComponent(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate) + AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index 13b7e02a2f048..1b70aa91249b3 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -7,11 +7,13 @@ var/atom/speaking_pawn = controller.pawn var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] var/probability = controller.blackboard[BB_PARROT_REPEAT_PROBABILITY] - var/return_value = SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) + var/return_value = SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) // we always grab a new phrase every time this fires for randomness + if(prob(probability) || return_value & NO_NEW_PHRASE_AVAILABLE) + return potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] - - if(isnull(potential_string) || prob(probability)) + if(isnull(potential_string)) + stack_trace("Parrot As In Repeat Subtree somehow is getting a null potential string while not getting `NO_NEW_PHRASE_AVAILABLE`!") return controller.queue_behavior(/datum/ai_behavior/perform_speech/parrot, potential_string) From c7954b97b231d2768777f0e81ca4831de54c47cb Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:48:58 -0600 Subject: [PATCH 17/69] WOOOO SPEECH WORKS i think --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 2 +- .../mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index fc3e684c81ce8..bae8ec49df42d 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -200,6 +200,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( var/list/headset_channels = ears.channels for(var/channel in headset_channels) - returnable_list += GLOB.channel_tokens[headset_channels[channel]] // will return something like ":e" or ":c" y'know + returnable_list += GLOB.channel_tokens[channel] // will return something like ":e" or ":c" y'know return returnable_list diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index 1b70aa91249b3..e427819444405 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -19,7 +19,7 @@ controller.queue_behavior(/datum/ai_behavior/perform_speech/parrot, potential_string) /datum/ai_behavior/perform_speech/parrot - action_cooldown = 2 SECONDS + action_cooldown = 7.5 SECONDS // gets really annoying (moreso than usual) really fast otherwise /datum/ai_behavior/perform_speech/parrot/perform(seconds_per_tick, datum/ai_controller/controller, speech, speech_sound) var/mob/living/basic/parrot/speaking_pawn = controller.pawn From 748207018000fdf5e109f6f10547b468306ad074 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:58:32 -0600 Subject: [PATCH 18/69] roundend callback to save memory --- .../mob/living/basic/pets/parrot/poly.dm | 41 ++++++++++++++----- .../mob/living/simple_animal/parrot.dm | 11 ----- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index f93468f3bb25f..385cedf66dafb 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -15,6 +15,8 @@ gold_core_spawnable = NO_SPAWN //speak_chance = 3 + /// 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? @@ -26,19 +28,31 @@ /mob/living/basic/parrot/poly/Initialize(mapload) . = ..() - if(SStts.tts_enabled) - 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. + + 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. @@ -126,9 +140,12 @@ /mob/living/basic/parrot/poly/Write_Memory(dead, gibbed) . = ..() - if(!.) + if(!. || memory_saved) // if we die, no more memory return + if(!dead && (stat != DEAD)) + dead = FALSE + var/file_path = "data/npc_saves/Poly.json" var/list/file_data = list() @@ -176,7 +193,9 @@ butcher_results = list(/obj/item/ectoplasm = 1) /mob/living/basic/parrot/poly/ghost/Initialize(mapload) - memory_saved = TRUE //At this point nothing is saved + // 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) return ..() #undef POLY_DEFAULT diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 0468e0d8b22a7..0e8ec100e60a3 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -719,17 +719,6 @@ to_chat(src, span_notice("You will now [combat_mode ? "Harm" : "Help"] others.")) return -/* - * Sub-types - */ - - - -/mob/living/simple_animal/parrot/poly/Life(seconds_per_tick = SSMOBS_DT, times_fired) -// if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) -// Write_Memory(FALSE) -// memory_saved = TRUE -// ..() From 0d30999a22fabee608fefdb532a0638c57d5c486 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 12:07:26 -0600 Subject: [PATCH 19/69] MORE BULLSHIT --- code/modules/mob/living/basic/pets/parrot/poly.dm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 385cedf66dafb..1ebb259df5802 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -35,6 +35,8 @@ REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly! update_appearance() + // poly gets to say a snappy oneliner if he gets horted + RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) if(!SStts.tts_enabled) return @@ -103,6 +105,18 @@ if(POLY_BEATING_DEATHSTREAK) add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) +/// If we get hit, queue up a snappy oneliner. +/mob/living/basic/parrot/poly/proc/on_attacked() + SIGNAL_HANDLER + if(!isnull(client) || stat != CONSCIOUS) + return + + var/return_value = SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) + if(return_value & NO_NEW_PHRASE_AVAILABLE) + return + + say(controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") + /// 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) From e81d6c476e10f8c71c7c9a8b18a03e6bf4073620 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 12:29:16 -0600 Subject: [PATCH 20/69] MUUUUUUURGGGG --- code/__DEFINES/ai/pets.dm | 4 +- code/datums/components/listen_and_repeat.dm | 34 +++++++++++---- .../mob/living/basic/pets/parrot/parrot.dm | 41 +++++++++++++++++-- .../mob/living/basic/pets/parrot/poly.dm | 2 +- .../mob/living/simple_animal/parrot.dm | 20 --------- 5 files changed, 66 insertions(+), 35 deletions(-) diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index d0c98d39ff8e3..7ffbde1d71be8 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -31,6 +31,8 @@ /// The current string that this parrot will repeat back to someone #define BB_PARROT_REPEAT_STRING "BB_parrot_repeat_string" /// The odds that this parrot will repeat back a string -#define BB_PARROT_REPEAT_PROBABILITY "BB_parrot_repeat_proability" +#define BB_PARROT_REPEAT_PROBABILITY "BB_parrot_repeat_probability" +/// The odds that this parrot will choose another string to repeat +#define BB_PARROT_PHRASE_CHANGE_PROBABILITY "BB_parrot_phrase_change_probability" /// A copy of the string buffer that we end the shift with. DO NOT ACCESS THIS DIRECTLY - YOU SHOULD USE THE ELEMENT IN MOST CASES #define BB_EXPORTABLE_STRING_BUFFER_LIST "BB_parrot_repeat_string_buffer" diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index e10a901b71cfe..b266d21a3ec1a 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -10,32 +10,38 @@ var/list/desired_phrases = null /// The AI Blackboard Key we assign the value to. var/blackboard_key = null - /// Probability we actually do anything upon hearing something - var/probability_stat = null + /// Probability we speak + var/speech_probability = null + /// Probabiliy we switch our phrase + var/switch_phrase_probability = null /// List of things that we've heard and will repeat. var/list/speech_buffer = null -/datum/component/listen_and_repeat/Initialize(list/desired_phrases, blackboard_key, probability_stat) +/datum/component/listen_and_repeat/Initialize(list/desired_phrases, blackboard_key, speech_probability, switch_phrase_probability) . = ..() if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE - if(isnull(probability_stat)) - probability_stat = 50 // default for sanity + if(isnull(speech_probability)) + src.speech_probability = 50 + + if(isnull(speech_probability)) + src.speech_probability = 20 if(!isnull(desired_phrases)) LAZYADD(speech_buffer, desired_phrases) src.blackboard_key = blackboard_key RegisterSignal(parent, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) - RegisterSignal(parent, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_key)) + RegisterSignal(parent, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_phrase)) RegisterSignal(parent, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory)) + RegisterSignals(parent, list(COMSIG_AI_BLACKBOARD_KEY_SET(BB_PARROT_REPEAT_PROBABILITY), COMSIG_AI_BLACKBOARD_KEY_SET(BB_PARROT_PHRASE_CHANGE_PROBABILITY)), PROC_REF(update_probabilities)) // register to detach when a client logs in maybe /// Called when we hear something. /datum/component/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments) SIGNAL_HANDLER - if(prob(probability_stat)) + if(!prob(speech_probability)) return var/message = passed_arguments[HEARING_MESSAGE] @@ -55,19 +61,29 @@ LAZYOR(speech_buffer, html_decode(message)) /// Called to set a new value for the blackboard key. -/datum/component/listen_and_repeat/proc/set_new_blackboard_key(datum/source) +/datum/component/listen_and_repeat/proc/set_new_blackboard_phrase(datum/source) + SIGNAL_HANDLER var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller if(!LAZYLEN(speech_buffer)) controller.set_blackboard_key(blackboard_key, null) return NO_NEW_PHRASE_AVAILABLE + if(!prob(switch_phrase_probability)) + return + var/selected_phrase = pick(speech_buffer) controller.set_blackboard_key(blackboard_key, selected_phrase) +/// Update the probabilities whenever the blackboard changes on us. assume that the controller is sending the signal to be safe +/datum/component/listen_and_repeat/proc/update_probabilities(datum/ai_controller/source) + SIGNAL_HANDLER + speech_probability = source.blackboard[BB_PARROT_REPEAT_PROBABILITY] + switch_phrase_probability = source.blackboard[BB_PARROT_PHRASE_CHANGE_PROBABILITY] + /// Exports all the speech buffer data to a dedicated blackboard key on the source. /datum/component/listen_and_repeat/proc/on_write_memory(datum/source, dead, gibbed) - var/atom/movable/atom_source = source + SIGNAL_HANDLER var/datum/ai_controller/controller = atom_source.ai_controller if(LAZYLEN(speech_buffer)) // what? well whatever let's just move on return diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index bae8ec49df42d..48462ba897f5e 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -55,10 +55,10 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Pottential bodyparts for us to attack var/parrot_dam_zone = CARBON_GENERIC_BODY_ZONES - //Headset for Poly to yell at engineers :) + ///Headset for Poly to yell at engineers :) var/obj/item/radio/headset/ears = null - //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. + ///Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. var/obj/item/held_item = null //var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. @@ -70,6 +70,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( var/speech_blackboard_key = BB_PARROT_REPEAT_STRING /// The generic probability odds we have to do a speech-related action var/speech_probability_rate = 25 + /// The generic probability odds we have to switch out our speech string + var/speech_shuffle_rate = 20 ////The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, ////mobs it wants to attack or mobs that have attacked it @@ -78,7 +80,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( //Parrots will generally sit on their perch unless something catches their eye. //These vars store their preffered perch and if they dont have one, what they can use as a perch //var/obj/parrot_perch = null - var/obj/desired_perches = list( + var/static/list/desired_perches = list( /obj/machinery/computer, /obj/machinery/dna_scannernew, /obj/machinery/nuclearbomb, @@ -96,12 +98,15 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /mob/living/basic/parrot/Initialize(mapload) . = ..() setup_headset() + update_speech_blackboards() - AddComponent(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate) + AddComponent(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate, speech_shuffle_rate) AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) + RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attack)) + /mob/living/basic/parrot/Destroy() // should have cleaned these up on death, but let's be super safe in case that didn't happen if(!QDELETED(ears)) @@ -176,6 +181,34 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return NONE +/// Handles special behavior whenever we're attacked with a special item. +/mob/living/basic/parrot/proc/on_attack(mob/living/basic/source, obj/item/thing, mob/living/attacker, params) + SIGNAL_HANDLER + if(!istype(thing, /obj/item/food/cracker)) // Poly wants a cracker + return + + qdel(thing) + if(health < maxHealth) + adjustBruteLoss(-10) + speech_probability_rate *= 1.27 // 20 crackers to go from 1% to 100% + speech_shuffle_rate += 10 + update_speech_blackboards() + to_chat(user, span_notice("[src] eagerly devours the cracker.")) + return COMPONENT_NO_AFTERATTACK + +/// Updates our speech blackboards mob-side to reflect the current speech on the controller to ensure everything is synchronized. +/mob/living/basic/parrot/proc/update_speech_blackboards() + ai_controller.set_blackboard_key(BB_PARROT_REPEAT_PROBABILITY, speech_probability_rate) + ai_controller.set_blackboard_key(BB_PARROT_PHRASE_CHANGE_PROBABILITY, speech_shuffle_rate) + +/mob/living/basic/parrot/vv_edit_var(var_name, vval) + . = ..() // give admins an easier time when it comes to fucking with poly + switch (var_name) + if (NAMEOF(src, speech_probability_rate)) + update_speech_blackboards() + if (NAMEOF(src, speech_shuffle_rate)) + update_speech_blackboards() + /// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. /mob/living/basic/parrot/proc/setup_headset() return diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 1ebb259df5802..2a6138c930295 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -36,7 +36,7 @@ REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly! update_appearance() // poly gets to say a snappy oneliner if he gets horted - RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) + RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_hurt)) // do not confuse with COMSIG_ATOM_ATTACKBY, which is registered on parent if(!SStts.tts_enabled) return diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 0e8ec100e60a3..7ffc6277aebb0 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -198,15 +198,6 @@ parrot_state |= PARROT_FLEE icon_state = icon_living drop_held_item(0) - else if(istype(O, /obj/item/food/cracker)) //Poly wants a cracker. - qdel(O) - if(health < maxHealth) - adjustBruteLoss(-10) - speak_chance *= 1.27 // 20 crackers to go from 1% to 100% - speech_shuffle_rate += 10 - to_chat(user, span_notice("[src] eagerly devours the cracker.")) - ..() - return //Bullets /mob/living/simple_animal/parrot/bullet_act(obj/projectile/Proj) @@ -240,17 +231,6 @@ return -//-----SPEECH - /* Parrot speech mimickry! - Phrases that the parrot Hear()s get added to speach_buffer. - Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. */ -/mob/living/simple_animal/parrot/handle_automated_speech() - ..() - if(speech_buffer.len && prob(speech_shuffle_rate)) //shuffle out a phrase and add in a new one - if(speak.len) - speak.Remove(pick(speak)) - - speak.Add(pick(speech_buffer)) /mob/living/simple_animal/parrot/handle_automated_movement() From 07ae0f453e774c32fa2dfd5da4a7f8cf77b18cb8 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 12:59:02 -0600 Subject: [PATCH 21/69] probably a good stopping point --- .../mob/living/basic/pets/parrot/parrot.dm | 40 ++++++++++++++++++- .../mob/living/basic/pets/parrot/poly.dm | 14 ------- .../mob/living/simple_animal/parrot.dm | 26 ------------ 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 48462ba897f5e..64f96e2eeb3bf 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -105,7 +105,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) - RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attack)) + RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attack)) // this means we could have a peaceful interaction, like getting a cracker + RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_injured)) // this means we got hurt and it's go time /mob/living/basic/parrot/Destroy() // should have cleaned these up on death, but let's be super safe in case that didn't happen @@ -196,6 +197,43 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( to_chat(user, span_notice("[src] eagerly devours the cracker.")) return COMPONENT_NO_AFTERATTACK +/// Handles special behavior whenever we are injured. +/mob/living/basic/parrot/proc/on_injured(mob/living/basic/source, mob/living/attacker, attack_flags) + SIGNAL_HANDLER + if(isnull(client) || stat == CONSCIOUS) + return + + drop_held_item(gently = FALSE) + var/return_value = SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) + if(return_value & NO_NEW_PHRASE_AVAILABLE) + return + + say(controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") + + +/// Handles dropping items we're holding. Gently is a special modifier we can use for special interactions. +/mob/living/basic/parrot/proc/drop_held_item(gently = TRUE) + if(isnull(held_item)) + balloon_alert(src, "nothing to drop!") + return + + if(stat != CONSCIOUS) // don't gotta do shit + held_item.forceMove(drop_location()) + held_item = null + return + + if(!gently && isgrenade(held_item)) + var/obj/item/grenade/bomb = held_item + balloon_alert(src, "bombs away!") // you'll likely die too so we can get away with the `!` here + bomb.forceMove(drop_location()) + held_item = null + bomb.detonate() + return + + balloon_alert(src, "dropped item") + held_item.forceMove(drop_location()) + held_item = null + /// Updates our speech blackboards mob-side to reflect the current speech on the controller to ensure everything is synchronized. /mob/living/basic/parrot/proc/update_speech_blackboards() ai_controller.set_blackboard_key(BB_PARROT_REPEAT_PROBABILITY, speech_probability_rate) diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 2a6138c930295..385cedf66dafb 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -35,8 +35,6 @@ REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly! update_appearance() - // poly gets to say a snappy oneliner if he gets horted - RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_hurt)) // do not confuse with COMSIG_ATOM_ATTACKBY, which is registered on parent if(!SStts.tts_enabled) return @@ -105,18 +103,6 @@ if(POLY_BEATING_DEATHSTREAK) add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY) -/// If we get hit, queue up a snappy oneliner. -/mob/living/basic/parrot/poly/proc/on_attacked() - SIGNAL_HANDLER - if(!isnull(client) || stat != CONSCIOUS) - return - - var/return_value = SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) - if(return_value & NO_NEW_PHRASE_AVAILABLE) - return - - say(controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") - /// 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) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 7ffc6277aebb0..efdd83588f183 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -578,17 +578,6 @@ return /mob/living/simple_animal/parrot/proc/drop_held_item(drop_gently = 1) - set name = "Drop held item" - set category = "Parrot" - set desc = "Drop the item you're holding." - - if(stat) - return -1 - - if(!held_item) - if(src == usr) //So that other mobs won't make this message appear when they're bludgeoning you. - to_chat(src, span_warning("You have nothing to drop!")) - return 0 //parrots will eat crackers instead of dropping them @@ -601,21 +590,6 @@ return 1 - if(!drop_gently) - if(isgrenade(held_item)) - var/obj/item/grenade/G = held_item - G.forceMove(drop_location()) - G.detonate() - to_chat(src, span_danger("You let go of [held_item]!")) - held_item = null - return 1 - - to_chat(src, span_notice("You drop [held_item].")) - - held_item.forceMove(drop_location()) - held_item = null - return 1 - /mob/living/simple_animal/parrot/proc/perch_player() set name = "Sit" set category = "Parrot" From 50fff74e7d4a0bdc51ab8f7cf92505e2711e1e7f Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 13:03:31 -0600 Subject: [PATCH 22/69] reworks strippable items mayb i don't know if it works on the whole uh procwise stuff but you know what i'm commiting to haing it be more dynamic than just be cringe on the strippable item --- .../living/basic/pets/parrot/parrot_items.dm | 33 ++----------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm index d13522faf4f8c..904a3de4ce1d2 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm @@ -25,7 +25,7 @@ if (!istype(radio)) return - var/mob/living/simple_animal/parrot/parrot_source = source + var/mob/living/BASIC/parrot/parrot_source = source if (!istype(parrot_source)) return @@ -36,33 +36,6 @@ to_chat(user, span_notice("You fit [radio] onto [source].")) - parrot_source.available_channels.Cut() - - for (var/channel in radio.channels) - var/channel_to_add - - switch (channel) - if (RADIO_CHANNEL_ENGINEERING) - channel_to_add = RADIO_TOKEN_ENGINEERING - if (RADIO_CHANNEL_COMMAND) - channel_to_add = RADIO_TOKEN_COMMAND - if (RADIO_CHANNEL_SECURITY) - channel_to_add = RADIO_TOKEN_SECURITY - if (RADIO_CHANNEL_SCIENCE) - channel_to_add = RADIO_TOKEN_SCIENCE - if (RADIO_CHANNEL_MEDICAL) - channel_to_add = RADIO_TOKEN_MEDICAL - if (RADIO_CHANNEL_SUPPLY) - channel_to_add = RADIO_TOKEN_SUPPLY - if (RADIO_CHANNEL_SERVICE) - channel_to_add = RADIO_TOKEN_SERVICE - - if (channel_to_add) - parrot_source.available_channels += channel_to_add - - if (radio.translate_binary) - parrot_source.available_channels.Add(MODE_TOKEN_BINARY) - /datum/strippable_item/parrot_headset/start_unequip(atom/source, mob/user) . = ..() if (!.) @@ -72,8 +45,8 @@ if (!istype(parrot_source)) return - if (!parrot_source.stat) - parrot_source.say("[parrot_source.available_channels.len ? "[pick(parrot_source.available_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + if (parrot_source.stat == CONSCIOUS) + parrot_source.say("[parrot_source.available_channels.len ? "[pick(parrot_source.available_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!", forced = "attempted headset removal") return TRUE From 3b086d5efc23c436556b00eb8859ada9fa741b25 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 13:05:30 -0600 Subject: [PATCH 23/69] it sitll won't compile but that's later issue --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 64f96e2eeb3bf..9bbfdc68121d7 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -194,7 +194,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( speech_probability_rate *= 1.27 // 20 crackers to go from 1% to 100% speech_shuffle_rate += 10 update_speech_blackboards() - to_chat(user, span_notice("[src] eagerly devours the cracker.")) + to_chat(source, span_notice("[src] eagerly devours the cracker.")) return COMPONENT_NO_AFTERATTACK /// Handles special behavior whenever we are injured. @@ -204,7 +204,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return drop_held_item(gently = FALSE) - var/return_value = SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) + var/return_value = SEND_SIGNAL(source, COMSIG_NEEDS_NEW_PHRASE) if(return_value & NO_NEW_PHRASE_AVAILABLE) return From 11e1dbee4ad1bcceb4c66c358a171d5b16cb917f Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 16:16:55 -0600 Subject: [PATCH 24/69] probably more stuff --- code/__DEFINES/ai/pets.dm | 2 + .../mob/living/basic/pets/parrot/parrot.dm | 63 +++++++++++++++++-- .../mob/living/simple_animal/parrot.dm | 42 ------------- 3 files changed, 61 insertions(+), 46 deletions(-) diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index 7ffbde1d71be8..90f76a31f86a3 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -36,3 +36,5 @@ #define BB_PARROT_PHRASE_CHANGE_PROBABILITY "BB_parrot_phrase_change_probability" /// A copy of the string buffer that we end the shift with. DO NOT ACCESS THIS DIRECTLY - YOU SHOULD USE THE ELEMENT IN MOST CASES #define BB_EXPORTABLE_STRING_BUFFER_LIST "BB_parrot_repeat_string_buffer" +/// The current perch that we have +#define BB_PARROT_PERCH "BB_parrot_perch" diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 9bbfdc68121d7..40dae7c1b3167 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -105,7 +105,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) - RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attack)) // this means we could have a peaceful interaction, like getting a cracker + RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking)) + RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attacked)) // this means we could have a peaceful interaction, like getting a cracker RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_injured)) // this means we got hurt and it's go time /mob/living/basic/parrot/Destroy() @@ -182,8 +183,47 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return NONE +/// Master proc which will determine the intent of OUR attacks on an object and summon the relevant procs accordingly. +/// This is pretty much meant for players, AI will use the task-specific procs instead. +/mob/living/basic/parrot/proc/pre_attacking(mob/living/basic/source, atom/target) + SIGNAL_HANDLER + if(isnull(client) || stat != CONSCIOUS) + return + + if(isitem(target)) + if(steal_from_ground(target)) + return COMPONENT_HOSTILE_NO_ATTACK + return + + if(ismob(target)) + + +/// Picks up an item from the ground and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise. +/mob/living/basic/parrot/proc/steal_from_ground(obj/item/target) + if(!isnull(held_item)) + balloon_alert(src, "already holding something!") + return FALSE + + if(target.w_class > WEIGHT_CLASS_SMALL) + balloon_alert(src, "too big to pick up!") + return FALSE + + var/turf/open/perch = ai_controller.blackboard[BB_PARROT_PERCH] + if(isnull(client) && target.loc == perch) // we'll leave that on our perch if we're ai controlled + return FALSE + + target.forceMove(src) + held_item = target + visible_message( + span_notice("[src] grabs [held_item]!"), + span_notice("You grab [held_item]!"), + span_hear("You hear the sounds of wings flapping furiously."), + ) + return TRUE + + /// Handles special behavior whenever we're attacked with a special item. -/mob/living/basic/parrot/proc/on_attack(mob/living/basic/source, obj/item/thing, mob/living/attacker, params) +/mob/living/basic/parrot/proc/on_attacked(mob/living/basic/source, obj/item/thing, mob/living/attacker, params) SIGNAL_HANDLER if(!istype(thing, /obj/item/food/cracker)) // Poly wants a cracker return @@ -194,7 +234,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( speech_probability_rate *= 1.27 // 20 crackers to go from 1% to 100% speech_shuffle_rate += 10 update_speech_blackboards() - to_chat(source, span_notice("[src] eagerly devours the cracker.")) + to_chat(src, span_notice("[src] eagerly devours the cracker.")) return COMPONENT_NO_AFTERATTACK /// Handles special behavior whenever we are injured. @@ -208,8 +248,23 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if(return_value & NO_NEW_PHRASE_AVAILABLE) return - say(controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") + say(ai_controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") +/// Handles picking up the item we're holding, done in its own proc because of a snowflake edge case we need to account for. No additional logic beyond that. +/// Returns TRUE if we picked it up, FALSE otherwise. +/mob/living/basic/parrot/proc/pick_up_item(obj/item/target) + target.forceMove(src) + held_item = target + + if(!istype(held_item, /obj/item/food/cracker)) + return TRUE + + qdel(held_item) + held_item = null + if(health < maxHealth) + adjustBruteLoss(-10) + manual_emote("[src] eagerly downs the cracker.") + return TRUE // yeah technically /// Handles dropping items we're holding. Gently is a special modifier we can use for special interactions. /mob/living/basic/parrot/proc/drop_held_item(gently = TRUE) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index efdd83588f183..21288464463f0 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -505,36 +505,6 @@ return null -/* - * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots. - */ -/mob/living/simple_animal/parrot/proc/steal_from_ground() - set name = "Steal from ground" - set category = "Parrot" - set desc = "Grabs a nearby item." - - if(stat) - return -1 - - if(held_item) - to_chat(src, span_warning("You are already holding [held_item]!")) - return 1 - - for(var/obj/item/I in view(1,src)) - //Make sure we're not already holding it and it's small enough - if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL) - - //If we have a perch and the item is sitting on it, continue - if(!client && parrot_perch && I.loc == parrot_perch.loc) - continue - - held_item = I - I.forceMove(src) - visible_message(span_notice("[src] grabs [held_item]!"), span_notice("You grab [held_item]!"), span_hear("You hear the sounds of wings flapping furiously.")) - return held_item - - to_chat(src, span_warning("There is nothing of interest to take!")) - return 0 /mob/living/simple_animal/parrot/proc/steal_from_mob() set name = "Steal from mob" @@ -577,18 +547,6 @@ return -/mob/living/simple_animal/parrot/proc/drop_held_item(drop_gently = 1) - - -//parrots will eat crackers instead of dropping them - if(istype(held_item, /obj/item/food/cracker) && (drop_gently)) - qdel(held_item) - held_item = null - if(health < maxHealth) - adjustBruteLoss(-10) - manual_emote("[src] eagerly downs the cracker.") - return 1 - /mob/living/simple_animal/parrot/proc/perch_player() set name = "Sit" From ef1817cf756df7d407683b7313bfcb816b0676f9 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 16:23:46 -0600 Subject: [PATCH 25/69] does the snaggy stuff --- .../mob/living/basic/pets/parrot/parrot.dm | 28 +++++++++++++++-- .../mob/living/simple_animal/parrot.dm | 30 ------------------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 40dae7c1b3167..bf1c088a441d0 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -195,8 +195,10 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return COMPONENT_HOSTILE_NO_ATTACK return - if(ismob(target)) - + if(iscarbon(target)) + if(steal_from_mob(target)) + return COMPONENT_HOSTILE_NO_ATTACK + return /// Picks up an item from the ground and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise. /mob/living/basic/parrot/proc/steal_from_ground(obj/item/target) @@ -221,6 +223,26 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( ) return TRUE +/// Looks for an item that we can snatch and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise. +/mob/living/basic/parrot/proc/steal_from_ground(mob/living/carbon/victim) + if(!isnull(held_item)) + balloon_alert(src, "already holding something!") + return FALSE + + for(var/obj/item/stealable in victim.held_items) + if(stealable.w_class <= WEIGHT_CLASS_SMALL) + victim.temporarilyRemoveItemFromInventory(stealable, force = TRUE) + visible_message( + span_notice("[src] grabs [held_item] out of [victim]'s hand!"), + span_notice("You snag [held_item] out of [victim]'s hand!"), + span_hear("You hear the sounds of wings flapping furiously."), + ) + pick_up_item(stealable) + return TRUE + + return FALSE + + /// Handles special behavior whenever we're attacked with a special item. /mob/living/basic/parrot/proc/on_attacked(mob/living/basic/source, obj/item/thing, mob/living/attacker, params) @@ -231,7 +253,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( qdel(thing) if(health < maxHealth) adjustBruteLoss(-10) - speech_probability_rate *= 1.27 // 20 crackers to go from 1% to 100% + speech_probability_rate *= 1.27 speech_shuffle_rate += 10 update_speech_blackboards() to_chat(src, span_notice("[src] eagerly devours the cracker.")) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 21288464463f0..3d2ebbd68f491 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -505,36 +505,6 @@ return null - -/mob/living/simple_animal/parrot/proc/steal_from_mob() - set name = "Steal from mob" - set category = "Parrot" - set desc = "Steals an item right out of a person's hand!" - - if(stat) - return -1 - - if(held_item) - to_chat(src, span_warning("You are already holding [held_item]!")) - return 1 - - var/obj/item/stolen_item = null - - for(var/mob/living/carbon/C in view(1,src)) - for(var/obj/item/I in C.held_items) - if(I.w_class <= WEIGHT_CLASS_SMALL) - stolen_item = I - break - - if(stolen_item) - C.transferItemToLoc(stolen_item, src, TRUE) - held_item = stolen_item - visible_message(span_notice("[src] grabs [held_item] out of [C]'s hand!"), span_notice("You snag [held_item] out of [C]'s hand!"), span_hear("You hear the sounds of wings flapping furiously.")) - return held_item - - to_chat(src, span_warning("There is nothing of interest to take!")) - return 0 - /mob/living/simple_animal/parrot/verb/drop_held_item_player() set name = "Drop held item" set category = "Parrot" From bee2d6ff573329f0e523757960763fa4653348f9 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 16:55:22 -0600 Subject: [PATCH 26/69] some perching dogshit --- .../mob/living/basic/pets/parrot/parrot.dm | 67 +++++++++++++++++++ .../mob/living/simple_animal/parrot.dm | 7 -- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index bf1c088a441d0..eb8a32faae7b6 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -106,6 +106,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( AddElement(/datum/element/simple_flying) RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking)) + RegisterSignal(src. COMSIG_MOB_CLICKON, PROC_REF(on_click)) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attacked)) // this means we could have a peaceful interaction, like getting a cracker RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_injured)) // this means we got hurt and it's go time @@ -183,6 +185,71 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return NONE +#define PARROT_PERCHED "parrot_perched" // move this later + +/mob/living/basic/parrot/update_icon(updates) + . = ..() + if(HAS_TRAIT(src, PARROT_PERCHED)) + icon_state = icon_sit + else + icon_state = icon_living + pixel_x = initial(pixel_x) + pixel_y = initial(pixel_y) + +/// Proc that we just use to see if we're rightclicking something for perch behavior or dropping the item we currently ahve +/mob/living/basic/parrot/proc/on_click(mob/living/basic/source, atom/target, params) + SIGNAL_HANDLER + if(!LAZYACCESS(modifiers, RIGHT_CLICK)) + return + + if(start_perching(target)) + return COMSIG_MOB_CANCEL_CLICKON + + if(!isnull(held_item)) + drop_held_item(gently = TRUE) + +/// Proc that ascertains the type of perch we're dealing with and starts the perching process. +/// Returns TRUE if we started perching, FALSE otherwise. +/mob/living/basic/parrot/proc/start_perching(atom/target) + if(ishuman(target)) + if(perch_on_human(target)) + return TRUE + return FALSE + + if(!isobj(target)) + return FALSE + + forceMove(get_turf(target)) + ADD_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) + drop_item(gently = TRUE) // comfy :) + update_appearance() + return TRUE + +/// Proc that will perch us on a human. Returns TRUE if we perched, FALSE otherwise. +/mob/living/basic/parrot/proc/perch_on_human(mob/living/carbon/human/target) + if(target.has_buckled_mobs() && (length(target.buckled_mobs) >= target.max_buckled_mobs)) + balloon_alert(src, "can't perch on them!") + return FALSE + + forceMove(get_turf(H)) + if(!target.buckle_mob(src, TRUE)) + return FALSE + + pixel_y = 9 + pixel_x = pick(-8,8) //pick left or right shoulder + to_chat(src, span_notice("You sit on [target]'s shoulder.")) + ADD_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) + update_appearance() + return TRUE + +/// If we move, remove the perching trait and reset our icon state. +/mob/living/basic/parrot/proc/on_move(mob/living/basic/source) + if(!HAS_TRAIT(src, PARROT_PERCHED)) + return + + REMOVE_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) + update_appearance() + /// Master proc which will determine the intent of OUR attacks on an object and summon the relevant procs accordingly. /// This is pretty much meant for players, AI will use the task-specific procs instead. /mob/living/basic/parrot/proc/pre_attacking(mob/living/basic/source, atom/target) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 3d2ebbd68f491..cd60390f0b307 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -537,13 +537,6 @@ to_chat(src, span_warning("There is no perch nearby to sit on!")) return -/mob/living/simple_animal/parrot/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) - . = ..() - if(. && !stat && client && parrot_state == PARROT_PERCH) - parrot_state = PARROT_WANDER - icon_state = icon_living - pixel_x = initial(pixel_x) - pixel_y = initial(pixel_y) /mob/living/simple_animal/parrot/proc/perch_mob_player() set name = "Sit on Human's Shoulder" From 7b155da3cf9b909071154e824bcf98cfd0f07cb0 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 17:01:21 -0600 Subject: [PATCH 27/69] more cleanup --- .../mob/living/basic/pets/parrot/parrot.dm | 10 ++- .../mob/living/simple_animal/parrot.dm | 88 ------------------- 2 files changed, 8 insertions(+), 90 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index eb8a32faae7b6..6ecf18cbf5008 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -211,6 +211,10 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Proc that ascertains the type of perch we're dealing with and starts the perching process. /// Returns TRUE if we started perching, FALSE otherwise. /mob/living/basic/parrot/proc/start_perching(atom/target) + if(HAS_TRAIT(src, PARROT_PERCHED)) + balloon_alert(src, "already perched!") + return FALSE + if(ishuman(target)) if(perch_on_human(target)) return TRUE @@ -231,7 +235,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( balloon_alert(src, "can't perch on them!") return FALSE - forceMove(get_turf(H)) + forceMove(get_turf(target)) if(!target.buckle_mob(src, TRUE)) return FALSE @@ -298,7 +302,9 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( for(var/obj/item/stealable in victim.held_items) if(stealable.w_class <= WEIGHT_CLASS_SMALL) - victim.temporarilyRemoveItemFromInventory(stealable, force = TRUE) + if(!victim.temporarilyRemoveItemFromInventory(stealable)) + continue + visible_message( span_notice("[src] grabs [held_item] out of [victim]'s hand!"), span_notice("You snag [held_item] out of [victim]'s hand!"), diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index cd60390f0b307..0278668a3ef6d 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -505,94 +505,6 @@ return null -/mob/living/simple_animal/parrot/verb/drop_held_item_player() - set name = "Drop held item" - set category = "Parrot" - set desc = "Drop the item you're holding." - - if(stat) - return - - src.drop_held_item() - - return - - -/mob/living/simple_animal/parrot/proc/perch_player() - set name = "Sit" - set category = "Parrot" - set desc = "Sit on a nice comfy perch." - - if(stat || !client) - return - - if(icon_state == icon_living) - for(var/atom/movable/AM in view(src,1)) - for(var/perch_path in desired_perches) - if(istype(AM, perch_path)) - src.forceMove(AM.loc) - icon_state = icon_sit - parrot_state = PARROT_PERCH - return - to_chat(src, span_warning("There is no perch nearby to sit on!")) - return - - -/mob/living/simple_animal/parrot/proc/perch_mob_player() - set name = "Sit on Human's Shoulder" - set category = "Parrot" - set desc = "Sit on a nice comfy human being!" - - if(stat || !client) - return - - if(!buckled) - for(var/mob/living/carbon/human/H in view(src,1)) - if(H.has_buckled_mobs() && H.buckled_mobs.len >= H.max_buckled_mobs) //Already has a parrot, or is being eaten by a slime - continue - perch_on_human(H) - return - to_chat(src, span_warning("There is nobody nearby that you can sit on!")) - else - icon_state = icon_living - parrot_state = PARROT_WANDER - if(buckled) - to_chat(src, span_notice("You are no longer sitting on [buckled]'s shoulder.")) - buckled.unbuckle_mob(src, TRUE) - buckled = null - pixel_x = initial(pixel_x) - pixel_y = initial(pixel_y) - - - -/mob/living/simple_animal/parrot/proc/perch_on_human(mob/living/carbon/human/H) - if(!H) - return - forceMove(get_turf(H)) - if(H.buckle_mob(src, TRUE)) - pixel_y = 9 - pixel_x = pick(-8,8) //pick left or right shoulder - icon_state = icon_sit - parrot_state = PARROT_PERCH - to_chat(src, span_notice("You sit on [H]'s shoulder.")) - - -/mob/living/simple_animal/parrot/proc/toggle_mode() - set name = "Toggle mode" - set category = "Parrot" - set desc = "Time to bear those claws!" - - if(stat || !client) - return - - if(combat_mode) - melee_damage_upper = 0 - set_combat_mode(FALSE) - else - melee_damage_upper = parrot_damage_upper - set_combat_mode(TRUE) - to_chat(src, span_notice("You will now [combat_mode ? "Harm" : "Help"] others.")) - return From c2b710797a1817301b844e243d3043ba755fb320 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 17:26:18 -0600 Subject: [PATCH 28/69] okay flarp this --- code/__DEFINES/ai/pets.dm | 12 ++++++++++-- code/datums/ai/generic/find_and_set.dm | 10 ++++++++++ .../mob/living/basic/pets/parrot/parrot.dm | 5 ++--- .../pets/parrot/parrot_ai/parrot_search.dm | 17 +++++++++++++++++ code/modules/mob/living/simple_animal/parrot.dm | 3 ++- tgstation.dme | 1 + 6 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index 90f76a31f86a3..20f62d3023e4e 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -36,5 +36,13 @@ #define BB_PARROT_PHRASE_CHANGE_PROBABILITY "BB_parrot_phrase_change_probability" /// A copy of the string buffer that we end the shift with. DO NOT ACCESS THIS DIRECTLY - YOU SHOULD USE THE ELEMENT IN MOST CASES #define BB_EXPORTABLE_STRING_BUFFER_LIST "BB_parrot_repeat_string_buffer" -/// The current perch that we have -#define BB_PARROT_PERCH "BB_parrot_perch" +/// The current INANIMATE perch that we have +#define BB_PARROT_INANIMATE_PERCH "BB_parrot_perch" +/// The target human that we wanna try to perch on +#define BB_PARROT_HUMAN_PERCH "BB_parrot_human_perch" +/// The types of perches we desire to use +#define BB_PARROT_PERCH_TYPES "BB_parrot_perch_types" +/// The current thing that we wanna grab and take back to our perch +#define BB_PARROT_STEAL_TARGET "BB_parrot_steal_target" +/// The person that we wanna steal from +#define BB_PARROT_STEAL_HUMAN "BB_parrot_steal_human" diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm index f54ba33e6e3a3..24261ce9cac3e 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -72,6 +72,16 @@ if(length(found)) return pick(found) +/// Like find_and_set/in_list, but we return the turf location of the item instead of the item itself. +/datum/ai_behavior/find_and_set/in_list/turf_location + +/datum/ai_behavior/find_and_set/in_list/search_tactic(datum/ai_controller/controller, locate_paths, search_range) + . = ..() + if(isnull(.)) + return null + + return get_turf(.) + /** * Variant of find and set which returns an object which can be animated with a staff of change */ diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 6ecf18cbf5008..82d9b07bf776a 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -78,8 +78,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( //var/atom/movable/parrot_interest = null //Parrots will generally sit on their perch unless something catches their eye. - //These vars store their preffered perch and if they dont have one, what they can use as a perch - //var/obj/parrot_perch = null var/static/list/desired_perches = list( /obj/machinery/computer, /obj/machinery/dna_scannernew, @@ -99,6 +97,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( . = ..() setup_headset() update_speech_blackboards() + controller.set_blackboard_key(BB_PARROT_PERCH_TYPES, desired_perches) AddComponent(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate, speech_shuffle_rate) AddElement(/datum/element/ai_retaliate) @@ -281,7 +280,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( balloon_alert(src, "too big to pick up!") return FALSE - var/turf/open/perch = ai_controller.blackboard[BB_PARROT_PERCH] + var/turf/open/perch = ai_controller.blackboard[BB_PARROT_INANIMATE_PERCH] if(isnull(client) && target.loc == perch) // we'll leave that on our perch if we're ai controlled return FALSE diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm new file mode 100644 index 0000000000000..05f7907cffcaa --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm @@ -0,0 +1,17 @@ +/// Looks for cool items, or maybe people with cool items to start hordeing stuff. +/datum/ai_planning_subtree/find_interesting_things + +/datum/ai_planning_subtree/find_interesting_things/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + . = ..() + + var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + if(!QDELETED(target)) + // Busy with something + return + + if(QDELETED(ai_controller.blackboard[BB_PARROT_INANIMATE_PERCH])) // first order is to nab a nice perch + ai_controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/turf_location, BB_PARROT_INANIMATE_PERCH, controller.blackboard[BB_PARROT_PERCH_TYPES]) + return SUBTREE_RETURN_FINISH_PLANNING + + + diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 0278668a3ef6d..1b683897386ce 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -107,7 +107,8 @@ /obj/machinery/suit_storage_unit, ) - //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. + ///Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. + ///The AI behavior is nice, but we need this to work without an AI (as well as us having a bunch of snowflakey interactions) var/obj/item/held_item = null diff --git a/tgstation.dme b/tgstation.dme index eeed4ab08492e..d2504abbbea42 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4572,6 +4572,7 @@ #include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" #include "code\modules\mob\living\basic\pets\parrot\poly.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_controller.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_search.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" From 971689ca3380bab708583d251bf4ca7f4d1fbacb Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 17:34:31 -0600 Subject: [PATCH 29/69] makes it compile although the linter is mad --- code/datums/components/listen_and_repeat.dm | 1 + .../mob/living/basic/pets/parrot/parrot.dm | 12 ++++---- .../pets/parrot/parrot_ai/parrot_search.dm | 7 +++-- .../living/basic/pets/parrot/parrot_items.dm | 2 +- .../mob/living/simple_animal/parrot.dm | 28 ++++++++++--------- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index b266d21a3ec1a..1ff7401b3799c 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -84,6 +84,7 @@ /// Exports all the speech buffer data to a dedicated blackboard key on the source. /datum/component/listen_and_repeat/proc/on_write_memory(datum/source, dead, gibbed) SIGNAL_HANDLER + var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller if(LAZYLEN(speech_buffer)) // what? well whatever let's just move on return diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 82d9b07bf776a..12e40d6c99838 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -97,7 +97,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( . = ..() setup_headset() update_speech_blackboards() - controller.set_blackboard_key(BB_PARROT_PERCH_TYPES, desired_perches) + ai_controller.set_blackboard_key(BB_PARROT_PERCH_TYPES, desired_perches) AddComponent(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate, speech_shuffle_rate) AddElement(/datum/element/ai_retaliate) @@ -105,7 +105,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( AddElement(/datum/element/simple_flying) RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking)) - RegisterSignal(src. COMSIG_MOB_CLICKON, PROC_REF(on_click)) + RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click)) RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attacked)) // this means we could have a peaceful interaction, like getting a cracker RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_injured)) // this means we got hurt and it's go time @@ -118,7 +118,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( QDEL_NULL(held_item) return ..() -/mob/living/simple_animal/parrot/death(gibbed) +/mob/living/basic/parrot/death(gibbed) if(held_item) held_item.forceMove(drop_location()) held_item = null @@ -198,7 +198,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Proc that we just use to see if we're rightclicking something for perch behavior or dropping the item we currently ahve /mob/living/basic/parrot/proc/on_click(mob/living/basic/source, atom/target, params) SIGNAL_HANDLER - if(!LAZYACCESS(modifiers, RIGHT_CLICK)) + if(!LAZYACCESS(params, RIGHT_CLICK)) return if(start_perching(target)) @@ -224,7 +224,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( forceMove(get_turf(target)) ADD_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) - drop_item(gently = TRUE) // comfy :) + drop_held_item(gently = TRUE) // comfy :) update_appearance() return TRUE @@ -294,7 +294,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return TRUE /// Looks for an item that we can snatch and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise. -/mob/living/basic/parrot/proc/steal_from_ground(mob/living/carbon/victim) +/mob/living/basic/parrot/proc/steal_from_mob(mob/living/carbon/victim) if(!isnull(held_item)) balloon_alert(src, "already holding something!") return FALSE diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm index 05f7907cffcaa..ccf4cec4efbbf 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm @@ -9,9 +9,10 @@ // Busy with something return - if(QDELETED(ai_controller.blackboard[BB_PARROT_INANIMATE_PERCH])) // first order is to nab a nice perch - ai_controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/turf_location, BB_PARROT_INANIMATE_PERCH, controller.blackboard[BB_PARROT_PERCH_TYPES]) + var/atom/perch = controller.blackboard[BB_PARROT_INANIMATE_PERCH] + if(QDELETED(perch)) // first order is to nab a nice perch + controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/turf_location, BB_PARROT_INANIMATE_PERCH, controller.blackboard[BB_PARROT_PERCH_TYPES]) return SUBTREE_RETURN_FINISH_PLANNING - + // todo, differentiate between people we wanna rob and people we wanna perch on, as well as items we wanna hoard diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm index 904a3de4ce1d2..61da64bd48d9a 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm @@ -25,7 +25,7 @@ if (!istype(radio)) return - var/mob/living/BASIC/parrot/parrot_source = source + var/mob/living/basic/parrot/parrot_source = source if (!istype(parrot_source)) return diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 1b683897386ce..fd41d9085abc9 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -116,13 +116,6 @@ . = ..() parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var - add_verb(src, list(/mob/living/simple_animal/parrot/proc/steal_from_ground, \ - /mob/living/simple_animal/parrot/proc/steal_from_mob, \ - /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ - /mob/living/simple_animal/parrot/proc/perch_player, \ - /mob/living/simple_animal/parrot/proc/toggle_mode, - /mob/living/simple_animal/parrot/proc/perch_mob_player)) - @@ -158,7 +151,7 @@ parrot_state |= PARROT_ATTACK else parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! - drop_held_item(0) +// drop_held_item(0) if(stat != DEAD && !user.combat_mode) handle_automated_speech(1) //assured speak/emote return @@ -198,7 +191,7 @@ else parrot_state |= PARROT_FLEE icon_state = icon_living - drop_held_item(0) +// drop_held_item(0) //Bullets /mob/living/simple_animal/parrot/bullet_act(obj/projectile/Proj) @@ -211,7 +204,7 @@ parrot_state = PARROT_WANDER | PARROT_FLEE //Been shot and survived! RUN LIKE HELL! //parrot_been_shot += 5 icon_state = icon_living - drop_held_item(0) +// drop_held_item(0) /* @@ -323,7 +316,7 @@ if(Adjacent(parrot_interest)) if(isliving(parrot_interest)) - steal_from_mob() +// steal_from_mob() else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) @@ -351,7 +344,7 @@ if(Adjacent(parrot_perch)) forceMove(parrot_perch.loc) - drop_held_item() +// drop_held_item() parrot_state = PARROT_PERCH icon_state = icon_sit return @@ -402,6 +395,15 @@ parrot_state = PARROT_SWOOP | PARROT_RETURN else parrot_state = PARROT_WANDER + parrot_interest = null +// if(!held_item) +// held_item = steal_from_ground() +// if(!held_item) +// held_item = //Apparently it's possible for dead mobs to hang onto items in certain circumstances. +// if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home +// parrot_state = PARROT_SWOOP | PARROT_RETURN +// else +// parrot_state = PARROT_WANDER return attack_verb_continuous = pick("claws at", "chomps") @@ -419,7 +421,7 @@ SSmove_manager.stop_looping(src) set_parrot_interest(null) parrot_perch = null - drop_held_item() + // drop_held_item() parrot_state = PARROT_WANDER return From 5a400242df18fc32e2549cb671433fb8b0b6b456 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 17:37:50 -0600 Subject: [PATCH 30/69] less linter crap --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 12e40d6c99838..9b4f533e087bd 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -342,7 +342,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if(return_value & NO_NEW_PHRASE_AVAILABLE) return - say(ai_controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") + INVOKE_ASYNC(src, PROC_REF(say), message = ai_controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") /// Handles picking up the item we're holding, done in its own proc because of a snowflake edge case we need to account for. No additional logic beyond that. /// Returns TRUE if we picked it up, FALSE otherwise. From 8411c2ce2412e2e5d017c0777301300c8b139096 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:03:31 -0600 Subject: [PATCH 31/69] fixes a subtype issue --- code/datums/ai/generic/find_and_set.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm index 24261ce9cac3e..f2dc4038a41e9 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -75,7 +75,7 @@ /// Like find_and_set/in_list, but we return the turf location of the item instead of the item itself. /datum/ai_behavior/find_and_set/in_list/turf_location -/datum/ai_behavior/find_and_set/in_list/search_tactic(datum/ai_controller/controller, locate_paths, search_range) +/datum/ai_behavior/find_and_set/in_list/turf_location/search_tactic(datum/ai_controller/controller, locate_paths, search_range) . = ..() if(isnull(.)) return null From a94983986567ee25f213b5aa576c1fe538f3f774 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:05:14 -0600 Subject: [PATCH 32/69] alphabetizes a list --- code/modules/research/experimentor.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index 4bbc9969b8272..51f6c65a85664 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -641,10 +641,10 @@ /mob/living/basic/crab, /mob/living/basic/lizard, /mob/living/basic/mouse, + /mob/living/basic/parrot, /mob/living/basic/pet/dog/corgi, /mob/living/basic/pet/dog/pug, /mob/living/basic/pet/fox, - /mob/living/basic/parrot, /mob/living/simple_animal/pet/cat, ) for(var/counter in 1 to rand(1, 25)) From 9a524a8801993f68c5471ec4f95bd622a70d4e0b Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:15:09 -0600 Subject: [PATCH 33/69] deletes useless default speech prob --- code/datums/components/listen_and_repeat.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index 1ff7401b3799c..4247a47eef7df 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -22,9 +22,6 @@ if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE - if(isnull(speech_probability)) - src.speech_probability = 50 - if(isnull(speech_probability)) src.speech_probability = 20 From aab0f0815964e72c7ba0adbe0c983bcb190c3641 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:16:39 -0600 Subject: [PATCH 34/69] early continue to `steal_from_mob()` --- .../mob/living/basic/pets/parrot/parrot.dm | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 9b4f533e087bd..73ef3f414b618 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -300,17 +300,19 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return FALSE for(var/obj/item/stealable in victim.held_items) - if(stealable.w_class <= WEIGHT_CLASS_SMALL) - if(!victim.temporarilyRemoveItemFromInventory(stealable)) - continue - - visible_message( - span_notice("[src] grabs [held_item] out of [victim]'s hand!"), - span_notice("You snag [held_item] out of [victim]'s hand!"), - span_hear("You hear the sounds of wings flapping furiously."), - ) - pick_up_item(stealable) - return TRUE + if(stealable.w_class > WEIGHT_CLASS_SMALL) + continue + + if(!victim.temporarilyRemoveItemFromInventory(stealable)) + continue + + visible_message( + span_notice("[src] grabs [held_item] out of [victim]'s hand!"), + span_notice("You snag [held_item] out of [victim]'s hand!"), + span_hear("You hear the sounds of wings flapping furiously."), + ) + pick_up_item(stealable) + return TRUE return FALSE From faca9c07cf12494e31fc3b4abbc5dc0bfc10e697 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:17:08 -0600 Subject: [PATCH 35/69] that should be inverted --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 73ef3f414b618..d5834434fd6f6 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -336,7 +336,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Handles special behavior whenever we are injured. /mob/living/basic/parrot/proc/on_injured(mob/living/basic/source, mob/living/attacker, attack_flags) SIGNAL_HANDLER - if(isnull(client) || stat == CONSCIOUS) + if(!isnull(client) || stat == CONSCIOUS) return drop_held_item(gently = FALSE) From c6c70916966ba3f71d2747c51e7f8e706c66f2c0 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:19:25 -0600 Subject: [PATCH 36/69] misc. code cleanup --- .../mob/living/basic/pets/parrot/parrot.dm | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index d5834434fd6f6..42694d3ae765c 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -21,8 +21,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( //emote_hear = list("squawks.","bawks!") //emote_see = list("flutters their wings.") - //speak_chance = 1 //1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s - //turns_per_move = 5 butcher_results = list(/obj/item/food/cracker = 1) melee_damage_upper = 10 melee_damage_lower = 5 @@ -33,7 +31,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( response_disarm_simple = "gently move aside" response_harm_continuous = "swats" response_harm_simple = "swat" - //stop_automated_movement = 1 combat_mode = TRUE //parrots now start "aggressive" since only player parrots will nuzzle. attack_verb_continuous = "chomps" attack_verb_simple = "chomp" @@ -68,7 +65,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// The blackboard key we use to store the string we're repeating var/speech_blackboard_key = BB_PARROT_REPEAT_STRING - /// The generic probability odds we have to do a speech-related action + /// The generic probability odds we have to do a speech-related action // FIXME might need to tone this down var/speech_probability_rate = 25 /// The generic probability odds we have to switch out our speech string var/speech_shuffle_rate = 20 @@ -316,8 +313,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return FALSE - - /// Handles special behavior whenever we're attacked with a special item. /mob/living/basic/parrot/proc/on_attacked(mob/living/basic/source, obj/item/thing, mob/living/attacker, params) SIGNAL_HANDLER @@ -385,11 +380,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( held_item.forceMove(drop_location()) held_item = null -/// Updates our speech blackboards mob-side to reflect the current speech on the controller to ensure everything is synchronized. -/mob/living/basic/parrot/proc/update_speech_blackboards() - ai_controller.set_blackboard_key(BB_PARROT_REPEAT_PROBABILITY, speech_probability_rate) - ai_controller.set_blackboard_key(BB_PARROT_PHRASE_CHANGE_PROBABILITY, speech_shuffle_rate) - /mob/living/basic/parrot/vv_edit_var(var_name, vval) . = ..() // give admins an easier time when it comes to fucking with poly switch (var_name) @@ -398,6 +388,11 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if (NAMEOF(src, speech_shuffle_rate)) update_speech_blackboards() +/// Updates our speech blackboards mob-side to reflect the current speech on the controller to ensure everything is synchronized. +/mob/living/basic/parrot/proc/update_speech_blackboards() + ai_controller.set_blackboard_key(BB_PARROT_REPEAT_PROBABILITY, speech_probability_rate) + ai_controller.set_blackboard_key(BB_PARROT_PHRASE_CHANGE_PROBABILITY, speech_shuffle_rate) + /// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. /mob/living/basic/parrot/proc/setup_headset() return @@ -413,7 +408,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return default_phrases - /// Gets the available channels that this parrot has access to. Returns a list of the channels we can use. /mob/living/basic/parrot/proc/get_available_channels() var/list/returnable_list = list() From 9e73e75c4871d53e1c9cc71d0ce467a73dcbc23e Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:23:05 -0600 Subject: [PATCH 37/69] adds the default poly phrases to the static list --- code/modules/mob/living/basic/pets/parrot/poly.dm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 385cedf66dafb..8b98dfa9783f0 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -11,9 +11,8 @@ /mob/living/basic/parrot/poly name = "Poly" desc = "Poly the Parrot. An expert on quantum cracker theory." - //speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN MODSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE") gold_core_spawnable = NO_SPAWN - //speak_chance = 3 + speech_probability_rate = 30 // FIXME: might need to nerf this /// Callback to save our memory at the end of the round. var/datum/callback/roundend_callback = null @@ -73,6 +72,14 @@ 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!") From c70fd6bda473bd14f11d95f4a51fcdcab862ac16 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:28:39 -0600 Subject: [PATCH 38/69] misc. code cleanup --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 42694d3ae765c..10fd56118c963 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -49,14 +49,14 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( //var/parrot_state = PARROT_WANDER //var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick. //var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down - /// Pottential bodyparts for us to attack + /// Potential bodyparts for us to attack var/parrot_dam_zone = CARBON_GENERIC_BODY_ZONES ///Headset for Poly to yell at engineers :) var/obj/item/radio/headset/ears = null ///Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. - var/obj/item/held_item = null + var/obj/item/held_item = nullz //var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. //var/parrot_lastmove = null //Updates/Stores position of the parrot while it's moving @@ -163,7 +163,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /mob/living/basic/parrot/radio(message, list/message_mods = list(), list/spans, language) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be. . = ..() - if(.) + if(. != NONE) return if(message_mods[MODE_HEADSET]) @@ -350,8 +350,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if(!istype(held_item, /obj/item/food/cracker)) return TRUE - qdel(held_item) - held_item = null + QDEL_NULL(held_item) if(health < maxHealth) adjustBruteLoss(-10) manual_emote("[src] eagerly downs the cracker.") From 290d3f680c6a40fa8bdbe91599e562ca946bbfe6 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:29:13 -0600 Subject: [PATCH 39/69] whoops --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 10fd56118c963..7d502695e0df3 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -56,7 +56,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( var/obj/item/radio/headset/ears = null ///Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. - var/obj/item/held_item = nullz + var/obj/item/held_item = null //var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. //var/parrot_lastmove = null //Updates/Stores position of the parrot while it's moving From e470e973c660b865f54ed0417fd353f0e90783ee Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:30:36 -0600 Subject: [PATCH 40/69] TYPE_PROC_REF for say --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 7d502695e0df3..c51488d8fd6c1 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -339,7 +339,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if(return_value & NO_NEW_PHRASE_AVAILABLE) return - INVOKE_ASYNC(src, PROC_REF(say), message = ai_controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") + INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, say), message = ai_controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot oneliner on attack") /// Handles picking up the item we're holding, done in its own proc because of a snowflake edge case we need to account for. No additional logic beyond that. /// Returns TRUE if we picked it up, FALSE otherwise. From b28db7428be9e00fc911d2d1e1382b6f353336fd Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Mon, 13 Nov 2023 09:53:37 +0200 Subject: [PATCH 41/69] Parrottai (#34) --- code/__DEFINES/ai/pets.dm | 17 +- .../signals_atom/signals_atom_movable.dm | 4 + code/_globalvars/phobias.dm | 2 +- .../subsystem/persistence/_persistence.dm | 5 +- .../basic_ai_behaviors/basic_attacking.dm | 11 + code/datums/ai/generic/find_and_set.dm | 13 + code/datums/components/listen_and_repeat.dm | 24 +- code/datums/components/supermatter_crystal.dm | 2 +- code/datums/diseases/parrotpossession.dm | 33 +- code/datums/memory/_memory.dm | 2 +- .../pirate/pirate_shuttle_equipment.dm | 4 +- .../traitor/objectives/kill_pet.dm | 2 +- code/modules/cargo/packs/livestock.dm | 4 +- code/modules/events/ghost_role/sentience.dm | 2 +- code/modules/events/holiday/halloween.dm | 6 +- code/modules/events/wizard/petsplosion.dm | 2 +- .../mob/living/basic/pets/parrot/parrot.dm | 130 ++--- .../parrot/parrot_ai/parrot_controller.dm | 239 +++++++- .../pets/parrot/parrot_ai/parrot_search.dm | 18 - .../pets/parrot/parrot_ai/parroting_action.dm | 15 +- .../living/basic/pets/parrot/parrot_items.dm | 9 +- .../mob/living/basic/pets/parrot/poly.dm | 42 +- .../mob/living/carbon/human/human_defines.dm | 2 +- code/modules/mob/living/living.dm | 2 +- code/modules/mob/living/living_say.dm | 2 +- .../mob/living/simple_animal/parrot.dm | 549 ------------------ code/modules/mob/transform_procs.dm | 2 +- code/modules/unit_tests/required_map_items.dm | 2 +- .../unit_tests/simple_animal_freeze.dm | 4 - html/changelogs/AutoChangeLog-pr-79666.yml | 4 + html/changelogs/AutoChangeLog-pr-79674.yml | 4 + tgstation.dme | 4 +- 32 files changed, 435 insertions(+), 726 deletions(-) delete mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm delete mode 100644 code/modules/mob/living/simple_animal/parrot.dm create mode 100644 html/changelogs/AutoChangeLog-pr-79666.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79674.yml diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index 20f62d3023e4e..3afaedfe0bcc6 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -34,15 +34,14 @@ #define BB_PARROT_REPEAT_PROBABILITY "BB_parrot_repeat_probability" /// The odds that this parrot will choose another string to repeat #define BB_PARROT_PHRASE_CHANGE_PROBABILITY "BB_parrot_phrase_change_probability" -/// A copy of the string buffer that we end the shift with. DO NOT ACCESS THIS DIRECTLY - YOU SHOULD USE THE ELEMENT IN MOST CASES +/// A copy of the string buffer that we end the shift with. DO NOT ACCESS THIS DIRECTLY - YOU SHOULD USE THE COMPONENT IN MOST CASES #define BB_EXPORTABLE_STRING_BUFFER_LIST "BB_parrot_repeat_string_buffer" -/// The current INANIMATE perch that we have -#define BB_PARROT_INANIMATE_PERCH "BB_parrot_perch" -/// The target human that we wanna try to perch on -#define BB_PARROT_HUMAN_PERCH "BB_parrot_human_perch" /// The types of perches we desire to use #define BB_PARROT_PERCH_TYPES "BB_parrot_perch_types" -/// The current thing that we wanna grab and take back to our perch -#define BB_PARROT_STEAL_TARGET "BB_parrot_steal_target" -/// The person that we wanna steal from -#define BB_PARROT_STEAL_HUMAN "BB_parrot_steal_human" +#define BB_PERCH_TARGET "perch_target" +#define BB_HOARD_ITEM_TARGET "hoard_item_target" +#define BB_THEFT_VICTIM "theft_victim" +#define BB_HOARD_LOCATION "hoard_location" +#define BB_HOARD_LOCATION_RANGE "hoard_location_range" +#define BB_IGNORE_ITEMS "ignore_items" + diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm index 9e4d1744edddf..ed0e09f433137 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -47,6 +47,10 @@ #define COMSIG_MOVABLE_THROW_LANDED "movable_throw_landed" ///from base of atom/movable/on_changed_z_level(): (turf/old_turf, turf/new_turf, same_z_layer) #define COMSIG_MOVABLE_Z_CHANGED "movable_ztransit" +///called before hearing a message froma tom/movable/Hear(): +#define COMSIG_MOVABLE_PRE_HEAR "movable_pre_hear" + ///do not proceed to hear the message + #define COMSIG_MOVABLE_CANCEL_HEARING (1<<0) ///from base of atom/movable/Hear(): (proc args list(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range)) #define COMSIG_MOVABLE_HEAR "movable_hear" #define HEARING_MESSAGE 1 diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm index 5bb8b4bf31498..c40f488c5df8d 100644 --- a/code/_globalvars/phobias.dm +++ b/code/_globalvars/phobias.dm @@ -62,7 +62,7 @@ GLOBAL_LIST_INIT(phobia_mobs, list( /mob/living/basic/chick, /mob/living/basic/chicken, /mob/living/basic/pet/penguin, - /mob/living/simple_animal/parrot, + /mob/living/basic/parrot, )), "conspiracies" = typecacheof(list( /mob/living/basic/drone, diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm index 586723c92e594..8d00c77812170 100644 --- a/code/controllers/subsystem/persistence/_persistence.dm +++ b/code/controllers/subsystem/persistence/_persistence.dm @@ -59,8 +59,9 @@ SUBSYSTEM_DEF(persistence) ///Loads up Poly's speech buffer. /datum/controller/subsystem/persistence/proc/load_poly() - for(var/mob/living/simple_animal/parrot/poly/P in GLOB.alive_mob_list) - twitterize(P.speech_buffer, "polytalk") + for(var/mob/living/basic/parrot/poly/bird in GLOB.alive_mob_list) + var/list/list_to_read = bird.get_static_list_of_phrases() + twitterize(list_to_read, "polytalk") break //Who's been duping the bird?! /// Loads up the amount of times maps appeared to alter their appearance in voting and rotation. diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm index 21b141feff801..0f7fe6ef142f2 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm @@ -1,6 +1,8 @@ /datum/ai_behavior/basic_melee_attack action_cooldown = 0.2 SECONDS // We gotta check unfortunately often because we're in a race condition with nextmove behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + ///do we finish this action after hitting once? + var/terminate_after_action = FALSE /datum/ai_behavior/basic_melee_attack/setup(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) . = ..() @@ -39,12 +41,21 @@ else basic_mob.melee_attack(target) + if(terminate_after_action) + finish_action(controller, TRUE, target_key) /datum/ai_behavior/basic_melee_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) . = ..() if(!succeeded) controller.clear_blackboard_key(target_key) +/datum/ai_behavior/basic_melee_attack/interact_once + terminate_after_action = TRUE + +/datum/ai_behavior/basic_melee_attack/interact_once/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) + . = ..() + controller.clear_blackboard_key(target_key) + /datum/ai_behavior/basic_ranged_attack action_cooldown = 0.6 SECONDS behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm index f2dc4038a41e9..d368641ce0ca0 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -158,3 +158,16 @@ return pick(customers) return null + +/datum/ai_behavior/find_and_set/nearby_friends + action_cooldown = 2 SECONDS + +/datum/ai_behavior/find_and_set/nearby_friends/search_tactic(datum/ai_controller/controller, locate_path, search_range) + var/atom/friend = locate(/mob/living/carbon/human) in oview(search_range, controller.pawn) + + if(isnull(friend)) + return null + + var/mob/living/living_pawn = controller.pawn + var/potential_friend = living_pawn.faction.Find(REF(friend)) ? friend : null + return potential_friend diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index 4247a47eef7df..0b816f722f27e 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -17,31 +17,26 @@ /// List of things that we've heard and will repeat. var/list/speech_buffer = null -/datum/component/listen_and_repeat/Initialize(list/desired_phrases, blackboard_key, speech_probability, switch_phrase_probability) +/datum/component/listen_and_repeat/Initialize(list/desired_phrases, blackboard_key) . = ..() if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE - if(isnull(speech_probability)) - src.speech_probability = 20 - if(!isnull(desired_phrases)) LAZYADD(speech_buffer, desired_phrases) + src.blackboard_key = blackboard_key - RegisterSignal(parent, COMSIG_MOVABLE_HEAR, PROC_REF(on_hear)) + RegisterSignal(parent, COMSIG_MOVABLE_PRE_HEAR, PROC_REF(on_hear)) RegisterSignal(parent, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_phrase)) RegisterSignal(parent, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory)) - RegisterSignals(parent, list(COMSIG_AI_BLACKBOARD_KEY_SET(BB_PARROT_REPEAT_PROBABILITY), COMSIG_AI_BLACKBOARD_KEY_SET(BB_PARROT_PHRASE_CHANGE_PROBABILITY)), PROC_REF(update_probabilities)) // register to detach when a client logs in maybe /// Called when we hear something. /datum/component/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments) SIGNAL_HANDLER - if(!prob(speech_probability)) - return - var/message = passed_arguments[HEARING_MESSAGE] + var/message = passed_arguments[HEARING_RAW_MESSAGE] var/speaker = passed_arguments[HEARING_SPEAKER] var/over_radio = !!passed_arguments[HEARING_RADIO_FREQ] if(speaker == source) // don't parrot ourselves @@ -63,21 +58,12 @@ var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller if(!LAZYLEN(speech_buffer)) - controller.set_blackboard_key(blackboard_key, null) + controller.clear_blackboard_key(blackboard_key) return NO_NEW_PHRASE_AVAILABLE - if(!prob(switch_phrase_probability)) - return - var/selected_phrase = pick(speech_buffer) controller.set_blackboard_key(blackboard_key, selected_phrase) -/// Update the probabilities whenever the blackboard changes on us. assume that the controller is sending the signal to be safe -/datum/component/listen_and_repeat/proc/update_probabilities(datum/ai_controller/source) - SIGNAL_HANDLER - speech_probability = source.blackboard[BB_PARROT_REPEAT_PROBABILITY] - switch_phrase_probability = source.blackboard[BB_PARROT_PHRASE_CHANGE_PROBABILITY] - /// Exports all the speech buffer data to a dedicated blackboard key on the source. /datum/component/listen_and_repeat/proc/on_write_memory(datum/source, dead, gibbed) SIGNAL_HANDLER diff --git a/code/datums/components/supermatter_crystal.dm b/code/datums/components/supermatter_crystal.dm index 3c37bba33cb8d..14a0122dcf5cf 100644 --- a/code/datums/components/supermatter_crystal.dm +++ b/code/datums/components/supermatter_crystal.dm @@ -300,7 +300,7 @@ message_admins("[atom_source] has consumed [key_name_admin(consumed_mob)] [ADMIN_JMP(atom_source)].") atom_source.investigate_log("has consumed [key_name(consumed_mob)].", INVESTIGATE_ENGINE) consumed_mob.investigate_log("has been dusted by [atom_source].", INVESTIGATE_DEATHS) - if(istype(consumed_mob, /mob/living/simple_animal/parrot/poly)) // Dusting Poly creates a power surge + if(istype(consumed_mob, /mob/living/basic/parrot/poly)) // Dusting Poly creates a power surge force_event(/datum/round_event_control/supermatter_surge/poly, "Poly's revenge") notify_ghosts( "[consumed_mob] has been dusted by [atom_source]!", diff --git a/code/datums/diseases/parrotpossession.dm b/code/datums/diseases/parrotpossession.dm index 23f68e1a42ff6..c2828fc6bb22f 100644 --- a/code/datums/diseases/parrotpossession.dm +++ b/code/datums/diseases/parrotpossession.dm @@ -13,24 +13,35 @@ severity = DISEASE_SEVERITY_MEDIUM infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC|MOB_MINERAL bypasses_immunity = TRUE //2spook - var/mob/living/simple_animal/parrot/poly/ghost/parrot + ///chance we speak + var/speak_chance = 5 + ///list of words we can use + var/list/speech_list = null /datum/disease/parrot_possession/stage_act(seconds_per_tick, times_fired) . = ..() + if(!.) return - if(QDELETED(parrot) || parrot.loc != affected_mob) - cure() - return FALSE - - if(length(parrot.speech_buffer) && SPT_PROB(parrot.speak_chance, seconds_per_tick)) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach - affected_mob.say(pick(parrot.speech_buffer), forced = "parrot possession") + if(LAZYLEN(speech_list) && SPT_PROB(speak_chance, seconds_per_tick)) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach + affected_mob.say(pick(speech_list), forced = "parrot possession") /datum/disease/parrot_possession/cure() - if(parrot && parrot.loc == affected_mob) - parrot.forceMove(affected_mob.drop_location()) - affected_mob.visible_message(span_danger("[parrot] is violently driven out of [affected_mob]!"), span_userdanger("[parrot] bursts out of your chest!")) - ..() + var/atom/movable/inside_parrot = locate(/mob/living/basic/parrot/poly/ghost) in affected_mob + if(inside_parrot) + UnregisterSignal(inside_parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED)) + inside_parrot.forceMove(affected_mob.drop_location()) + affected_mob.visible_message(span_danger("[inside_parrot] is violently driven out of [affected_mob]!"), span_userdanger("[inside_parrot] bursts out of your chest!")) + return ..() + +/datum/disease/parrot_possession/proc/set_parrot(mob/living/parrot) + speech_list = parrot.ai_controller?.blackboard[BB_EXPORTABLE_STRING_BUFFER_LIST] + RegisterSignals(parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED), PROC_REF(on_parrot_exit)) + +/datum/disease/parrot_possession/proc/on_parrot_exit(datum/source) + SIGNAL_HANDLER + UnregisterSignal(source, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED)) + cure() diff --git a/code/datums/memory/_memory.dm b/code/datums/memory/_memory.dm index 2b3e250a3fbf2..5afde7843ea30 100644 --- a/code/datums/memory/_memory.dm +++ b/code/datums/memory/_memory.dm @@ -276,7 +276,7 @@ /mob/living/basic/stickman, /mob/living/basic/stickman/dog, /mob/living/simple_animal/hostile/megafauna/dragon/lesser, - /mob/living/simple_animal/parrot, + /mob/living/basic/parrot, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/cat/cak, /obj/item/food/sausage/american, diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm index 3ea6488b2d42d..7521ff155496e 100644 --- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm +++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm @@ -404,10 +404,10 @@ /datum/export/pirate/parrot cost = 2000 unit_name = "alive parrot" - export_types = list(/mob/living/simple_animal/parrot) + export_types = list(/mob/living/basic/parrot) /datum/export/pirate/parrot/find_loot() - for(var/mob/living/simple_animal/parrot/current_parrot in GLOB.alive_mob_list) + for(var/mob/living/basic/parrot/current_parrot in GLOB.alive_mob_list) var/turf/parrot_turf = get_turf(current_parrot) if(parrot_turf && is_station_level(parrot_turf.z)) return current_parrot diff --git a/code/modules/antagonists/traitor/objectives/kill_pet.dm b/code/modules/antagonists/traitor/objectives/kill_pet.dm index 8ea89cb44d033..01ab042f11b03 100644 --- a/code/modules/antagonists/traitor/objectives/kill_pet.dm +++ b/code/modules/antagonists/traitor/objectives/kill_pet.dm @@ -24,7 +24,7 @@ ), JOB_CAPTAIN = /mob/living/basic/pet/fox/renault, JOB_CHIEF_MEDICAL_OFFICER = /mob/living/simple_animal/pet/cat/runtime, - JOB_CHIEF_ENGINEER = /mob/living/simple_animal/parrot/poly, + JOB_CHIEF_ENGINEER = /mob/living/basic/parrot/poly, JOB_QUARTERMASTER = list( /mob/living/basic/gorilla/cargorilla, /mob/living/basic/sloth/citrus, diff --git a/code/modules/cargo/packs/livestock.dm b/code/modules/cargo/packs/livestock.dm index ef9fb96182302..942b1414cf9f5 100644 --- a/code/modules/cargo/packs/livestock.dm +++ b/code/modules/cargo/packs/livestock.dm @@ -6,13 +6,13 @@ name = "Bird Crate" desc = "Contains five expert telecommunication birds." cost = CARGO_CRATE_VALUE * 8 - contains = list(/mob/living/simple_animal/parrot) + contains = list(/mob/living/basic/parrot) crate_name = "parrot crate" /datum/supply_pack/critter/parrot/generate() . = ..() for(var/i in 1 to 4) - new /mob/living/simple_animal/parrot(.) + new /mob/living/basic/parrot(.) /datum/supply_pack/critter/butterfly name = "Butterflies Crate" diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm index abc57d33a0758..f33333ddb1bd0 100644 --- a/code/modules/events/ghost_role/sentience.dm +++ b/code/modules/events/ghost_role/sentience.dm @@ -7,6 +7,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /mob/living/basic/goat, /mob/living/basic/lizard, /mob/living/basic/mouse/brown/tom, + /mob/living/basic/parrot, /mob/living/basic/pet, /mob/living/basic/pig, /mob/living/basic/rabbit, @@ -16,7 +17,6 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /mob/living/basic/spider/giant/sgt_araneus, /mob/living/simple_animal/bot/secbot/beepsky, /mob/living/simple_animal/hostile/retaliate/goose/vomit, - /mob/living/simple_animal/parrot, /mob/living/simple_animal/pet, ))) diff --git a/code/modules/events/holiday/halloween.dm b/code/modules/events/holiday/halloween.dm index d52dd1b9c508a..8eb29fa706c45 100644 --- a/code/modules/events/holiday/halloween.dm +++ b/code/modules/events/holiday/halloween.dm @@ -18,9 +18,9 @@ for(var/mob/living/basic/pet/dog/corgi/ian/Ian in GLOB.mob_living_list) Ian.place_on_head(new /obj/item/bedsheet(Ian)) - for(var/mob/living/simple_animal/parrot/poly/Poly in GLOB.mob_living_list) - new /mob/living/simple_animal/parrot/poly/ghost(Poly.loc) - qdel(Poly) + for(var/mob/living/basic/parrot/poly/bird in GLOB.mob_living_list) + new /mob/living/basic/parrot/poly/ghost(bird.loc) + qdel(bird) /datum/round_event/spooky/announce(fake) priority_announce(pick("RATTLE ME BONES!","THE RIDE NEVER ENDS!", "A SKELETON POPS OUT!", "SPOOKY SCARY SKELETONS!", "CREWMEMBERS BEWARE, YOU'RE IN FOR A SCARE!") , "THE CALL IS COMING FROM INSIDE THE HOUSE") diff --git a/code/modules/events/wizard/petsplosion.dm b/code/modules/events/wizard/petsplosion.dm index e670fa910a1f0..9de7fffcc3aea 100644 --- a/code/modules/events/wizard/petsplosion.dm +++ b/code/modules/events/wizard/petsplosion.dm @@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(petsplosion_candidates, typecacheof(list( /mob/living/basic/lizard, /mob/living/basic/mothroach, /mob/living/basic/mouse/brown/tom, + /mob/living/basic/parrot, /mob/living/basic/pet, /mob/living/basic/pig, /mob/living/basic/rabbit, @@ -17,7 +18,6 @@ GLOBAL_LIST_INIT(petsplosion_candidates, typecacheof(list( /mob/living/basic/snake, /mob/living/basic/spider/giant/sgt_araneus, /mob/living/simple_animal/hostile/retaliate/goose/vomit, - /mob/living/simple_animal/parrot, /mob/living/simple_animal/pet, ))) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index c51488d8fd6c1..5d9c2e395e037 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -16,11 +16,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( maxHealth = 80 pass_flags = PASSTABLE | PASSMOB - //speak = list("Hi!","Hello!","Cracker?","BAWWWWK george mellons griffing me!") - //speak_emote = list("squawks","says","yells") - //emote_hear = list("squawks.","bawks!") - //emote_see = list("flutters their wings.") - butcher_results = list(/obj/item/food/cracker = 1) melee_damage_upper = 10 melee_damage_lower = 5 @@ -46,9 +41,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( var/icon_sit = "parrot_sit" /// The number of damage we do when we decide to aggro for our lives var/parrot_damage_upper = 10 - //var/parrot_state = PARROT_WANDER - //var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick. - //var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down /// Potential bodyparts for us to attack var/parrot_dam_zone = CARBON_GENERIC_BODY_ZONES @@ -58,21 +50,12 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( ///Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. var/obj/item/held_item = null - //var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. - //var/parrot_lastmove = null //Updates/Stores position of the parrot while it's moving - //var/parrot_stuck = 0 //If parrot_lastmove hasn't changed, this will increment until it reaches parrot_stuck_threshold - //var/parrot_stuck_threshold = 10 //if this == parrot_stuck, it'll force the parrot back to wandering - /// The blackboard key we use to store the string we're repeating var/speech_blackboard_key = BB_PARROT_REPEAT_STRING /// The generic probability odds we have to do a speech-related action // FIXME might need to tone this down - var/speech_probability_rate = 25 + var/speech_probability_rate = 5 /// The generic probability odds we have to switch out our speech string - var/speech_shuffle_rate = 20 - - ////The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, - ////mobs it wants to attack or mobs that have attacked it - //var/atom/movable/parrot_interest = null + var/speech_shuffle_rate = 30 //Parrots will generally sit on their perch unless something catches their eye. var/static/list/desired_perches = list( @@ -88,6 +71,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /obj/structure/filingcabinet, /obj/structure/frame/computer, ) + ///items we wont pick up + var/static/list/ignore_items = typecacheof(list(/obj/item/radio)) /mob/living/basic/parrot/Initialize(mapload) @@ -95,15 +80,17 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( setup_headset() update_speech_blackboards() ai_controller.set_blackboard_key(BB_PARROT_PERCH_TYPES, desired_perches) - - AddComponent(/datum/component/listen_and_repeat, get_static_list_of_phrases(), speech_blackboard_key, speech_probability_rate, speech_shuffle_rate) + ai_controller.set_blackboard_key(BB_IGNORE_ITEMS, ignore_items) + AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cracker), tame_chance = 100, bonus_tame_chance = 0, after_tame = CALLBACK(src, PROC_REF(tamed))) + AddComponent(/datum/component/listen_and_repeat, desired_phrases = get_static_list_of_phrases(), blackboard_key = BB_PARROT_REPEAT_STRING) AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) + AddElement(/datum/element/basic_eating, food_types = list(/obj/item/food/cracker)) + RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking)) RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click)) - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attacked)) // this means we could have a peaceful interaction, like getting a cracker RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_injured)) // this means we got hurt and it's go time @@ -183,25 +170,19 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( #define PARROT_PERCHED "parrot_perched" // move this later -/mob/living/basic/parrot/update_icon(updates) +/mob/living/basic/parrot/update_icon_state() . = ..() if(HAS_TRAIT(src, PARROT_PERCHED)) icon_state = icon_sit else icon_state = icon_living - pixel_x = initial(pixel_x) - pixel_y = initial(pixel_y) /// Proc that we just use to see if we're rightclicking something for perch behavior or dropping the item we currently ahve /mob/living/basic/parrot/proc/on_click(mob/living/basic/source, atom/target, params) SIGNAL_HANDLER - if(!LAZYACCESS(params, RIGHT_CLICK)) + if(!LAZYACCESS(params, RIGHT_CLICK) || !CanReach(target)) return - - if(start_perching(target)) - return COMSIG_MOB_CANCEL_CLICKON - - if(!isnull(held_item)) + if(start_perching(target) && !isnull(held_item)) drop_held_item(gently = TRUE) /// Proc that ascertains the type of perch we're dealing with and starts the perching process. @@ -212,22 +193,26 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return FALSE if(ishuman(target)) - if(perch_on_human(target)) - return TRUE - return FALSE + return perch_on_human(target) - if(!isobj(target)) + if(!is_type_in_list(target, desired_perches)) return FALSE forceMove(get_turf(target)) - ADD_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) drop_held_item(gently = TRUE) // comfy :) - update_appearance() + toggle_perched(perched = TRUE) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(after_move)) return TRUE +/mob/living/basic/parrot/proc/after_move(atom/source) + SIGNAL_HANDLER + + UnregisterSignal(src, COMSIG_MOVABLE_MOVED) + toggle_perched(perched = FALSE) + /// Proc that will perch us on a human. Returns TRUE if we perched, FALSE otherwise. /mob/living/basic/parrot/proc/perch_on_human(mob/living/carbon/human/target) - if(target.has_buckled_mobs() && (length(target.buckled_mobs) >= target.max_buckled_mobs)) + if(LAZYLEN(target.buckled_mobs) >= target.max_buckled_mobs) balloon_alert(src, "can't perch on them!") return FALSE @@ -235,37 +220,38 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if(!target.buckle_mob(src, TRUE)) return FALSE - pixel_y = 9 - pixel_x = pick(-8,8) //pick left or right shoulder to_chat(src, span_notice("You sit on [target]'s shoulder.")) - ADD_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) - update_appearance() + toggle_perched(perched = TRUE) + RegisterSignal(src, COMSIG_LIVING_SET_BUCKLED, PROC_REF(on_unbuckle)) return TRUE -/// If we move, remove the perching trait and reset our icon state. -/mob/living/basic/parrot/proc/on_move(mob/living/basic/source) - if(!HAS_TRAIT(src, PARROT_PERCHED)) +/mob/living/basic/parrot/proc/on_unbuckle(mob/living/source, atom/movable/new_buckled) + SIGNAL_HANDLER + + if(new_buckled) return + UnregisterSignal(src, COMSIG_LIVING_SET_BUCKLED) + toggle_perched(perched = FALSE) - REMOVE_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) - update_appearance() +/mob/living/basic/parrot/proc/toggle_perched(perched) + if(!perched) + REMOVE_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) + else + ADD_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) + update_appearance(UPDATE_ICON_STATE) /// Master proc which will determine the intent of OUR attacks on an object and summon the relevant procs accordingly. /// This is pretty much meant for players, AI will use the task-specific procs instead. /mob/living/basic/parrot/proc/pre_attacking(mob/living/basic/source, atom/target) SIGNAL_HANDLER - if(isnull(client) || stat != CONSCIOUS) + if(stat != CONSCIOUS) return - if(isitem(target)) - if(steal_from_ground(target)) - return COMPONENT_HOSTILE_NO_ATTACK - return + if(isitem(target) && steal_from_ground(target)) + return COMPONENT_HOSTILE_NO_ATTACK - if(iscarbon(target)) - if(steal_from_mob(target)) - return COMPONENT_HOSTILE_NO_ATTACK - return + if(iscarbon(target) && steal_from_mob(target)) + return COMPONENT_HOSTILE_NO_ATTACK /// Picks up an item from the ground and puts it in our claws. Returns TRUE if we picked it up, FALSE otherwise. /mob/living/basic/parrot/proc/steal_from_ground(obj/item/target) @@ -277,10 +263,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( balloon_alert(src, "too big to pick up!") return FALSE - var/turf/open/perch = ai_controller.blackboard[BB_PARROT_INANIMATE_PERCH] - if(isnull(client) && target.loc == perch) // we'll leave that on our perch if we're ai controlled - return FALSE - target.forceMove(src) held_item = target visible_message( @@ -347,15 +329,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( target.forceMove(src) held_item = target - if(!istype(held_item, /obj/item/food/cracker)) - return TRUE - - QDEL_NULL(held_item) - if(health < maxHealth) - adjustBruteLoss(-10) - manual_emote("[src] eagerly downs the cracker.") - return TRUE // yeah technically - /// Handles dropping items we're holding. Gently is a special modifier we can use for special interactions. /mob/living/basic/parrot/proc/drop_held_item(gently = TRUE) if(isnull(held_item)) @@ -363,28 +336,30 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return if(stat != CONSCIOUS) // don't gotta do shit - held_item.forceMove(drop_location()) - held_item = null return if(!gently && isgrenade(held_item)) var/obj/item/grenade/bomb = held_item balloon_alert(src, "bombs away!") // you'll likely die too so we can get away with the `!` here bomb.forceMove(drop_location()) - held_item = null bomb.detonate() return balloon_alert(src, "dropped item") held_item.forceMove(drop_location()) - held_item = null + +/mob/living/basic/parrot/Exited(atom/movable/gone, direction) + . = ..() + if(gone != held_item) + return + held_item= null /mob/living/basic/parrot/vv_edit_var(var_name, vval) . = ..() // give admins an easier time when it comes to fucking with poly - switch (var_name) - if (NAMEOF(src, speech_probability_rate)) + switch(var_name) + if(NAMEOF(src, speech_probability_rate)) update_speech_blackboards() - if (NAMEOF(src, speech_shuffle_rate)) + if(NAMEOF(src, speech_shuffle_rate)) update_speech_blackboards() /// Updates our speech blackboards mob-side to reflect the current speech on the controller to ensure everything is synchronized. @@ -418,3 +393,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( returnable_list += GLOB.channel_tokens[channel] // will return something like ":e" or ":c" y'know return returnable_list + +/mob/living/basic/parrot/proc/tamed() + new /obj/effect/temp_visual/heart(drop_location()) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm index 9904386c65025..63f2ed7ed5bac 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm @@ -1,16 +1,245 @@ /datum/ai_controller/basic_controller/parrot blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, + BB_HOARD_LOCATION_RANGE = 9, ) ai_traits = STOP_MOVING_WHEN_PULLED ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk + idle_behavior = /datum/idle_behavior/idle_random_walk/parrot planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can + /datum/ai_planning_subtree/perch_on_target, /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/hoard_items, + /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can + ) + +/datum/idle_behavior/idle_random_walk/parrot + ///chance of us moving while perched + var/walk_chance_when_perched = 5 + +/datum/idle_behavior/idle_random_walk/parrot/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + walk_chance = HAS_TRAIT(living_pawn, PARROT_PERCHED) ? walk_chance_when_perched : initial(walk_chance) + return ..() + +///subtree to steal items +/datum/ai_planning_subtree/hoard_items/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + + var/turf/myspace = controller.blackboard[BB_HOARD_LOCATION] + + if(isnull(myspace) || myspace.is_blocked_turf(source_atom = controller.pawn) || get_dist(myspace, controller.pawn) > controller.blackboard[BB_HOARD_LOCATION_RANGE]) + controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_location, BB_HOARD_LOCATION, /turf/open) + return + + //we have an item, go drop! + var/list/our_contents = living_pawn.contents - typecache_filter_list(living_pawn.contents, controller.blackboard[BB_IGNORE_ITEMS]) + if(length(our_contents)) + controller.queue_behavior(/datum/ai_behavior/travel_towards/and_drop, BB_HOARD_LOCATION) + return SUBTREE_RETURN_FINISH_PLANNING + + if(controller.blackboard_key_exists(BB_HOARD_ITEM_TARGET)) + controller.queue_behavior(/datum/ai_behavior/basic_melee_attack/interact_once, BB_HOARD_ITEM_TARGET, BB_TARGETING_STRATEGY) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_item, BB_HOARD_ITEM_TARGET) + + +/datum/ai_behavior/find_and_set/hoard_location/search_tactic(datum/ai_controller/controller, locate_path, search_range) + for(var/turf/open/candidate in oview(search_range, controller.pawn)) + if(isspaceturf(candidate) || isopenspaceturf(candidate)) + continue + if(candidate.is_blocked_turf(source_atom = controller.pawn)) + continue + return candidate + + return null + +/datum/ai_behavior/find_and_set/hoard_item + action_cooldown = 5 SECONDS + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/find_and_set/hoard_item/search_tactic(datum/ai_controller/controller, locate_path, search_range) + if(!controller.blackboard_key_exists(BB_HOARD_LOCATION)) + return null + var/turf/nest_turf = controller.blackboard[BB_HOARD_LOCATION] + var/mob/living/living_pawn = controller.pawn + for(var/atom/potential_victim in oview(search_range, controller.pawn)) + if(is_type_in_typecache(potential_victim, controller.blackboard[BB_IGNORE_ITEMS])) + continue + if(potential_victim.loc == nest_turf) + continue + if(isitem(potential_victim)) + var/obj/item/item_steal = potential_victim + if(item_steal.w_class <= WEIGHT_CLASS_SMALL) + return potential_victim + if(!ishuman(potential_victim)) + continue + if(living_pawn.faction.Find(REF(potential_victim))) + continue //dont steal from friends + if(holding_valuable(controller, potential_victim)) + controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, TRUE) + return potential_victim + + return null + +/datum/ai_behavior/find_and_set/hoard_item/proc/holding_valuable(datum/ai_controller/controller, mob/living/human_target) + for(var/obj/item/potential_item in human_target.held_items) + if(is_type_in_typecache(potential_item, controller.blackboard[BB_IGNORE_ITEMS])) + continue + if(potential_item.w_class <= WEIGHT_CLASS_SMALL) + return TRUE + return FALSE + +/datum/ai_behavior/travel_towards/and_drop + +/datum/ai_behavior/travel_towards/and_drop/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + var/mob/living/living_mob = controller.pawn + var/obj/drop_item = locate(/obj/item) in (living_mob.contents - typecache_filter_list(living_mob.contents, controller.blackboard[BB_IGNORE_ITEMS])) + drop_item?.forceMove(get_turf(living_mob)) + +/datum/ai_behavior/basic_melee_attack/interact_once/parrot + +/datum/ai_behavior/basic_melee_attack/interact_once/parrot/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, FALSE) + +///subtree to perch on targets +/datum/ai_planning_subtree/perch_on_target + ///perchance... + var/perch_chance = 5 + ///chance we unbuckle + var/unperch_chance = 15 + + +/datum/ai_planning_subtree/perch_on_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + var/atom/buckled_too = living_pawn.buckled + + //do we have a current target or is chance to unbuckle has passed? then unbuckle! + if(buckled_too) + if((SPT_PROB(unperch_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))) + controller.queue_behavior(/datum/ai_behavior/unbuckle_mob) + return + return SUBTREE_RETURN_FINISH_PLANNING + + //if we are perched, we can go find something else to perch too + var/final_chance = HAS_TRAIT(living_pawn, PARROT_PERCHED) ? unperch_chance : perch_chance + + if(!SPT_PROB(final_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + return + + if(controller.blackboard_key_exists(BB_PERCH_TARGET)) + controller.queue_behavior(/datum/ai_behavior/perch_on_target, BB_PERCH_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + //50 50 chance to look for an object, or a friend + if(prob(50)) + controller.queue_behavior(/datum/ai_behavior/find_and_set/nearby_friends, BB_PERCH_TARGET) + return + + controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_PERCH_TARGET, controller.blackboard[BB_PARROT_PERCH_TYPES]) + + +/datum/ai_behavior/perch_on_target + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/perch_on_target/setup(datum/ai_controller/controller, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + + set_movement_target(controller, target) + +/datum/ai_behavior/perch_on_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + finish_action(controller, FALSE, target_key) + return + + var/mob/living/basic/parrot/living_pawn = controller.pawn + + if(!ishuman(target)) + living_pawn.start_perching(target) + finish_action(controller, TRUE, target_key) + return + + if(!check_human_conditions(target)) + finish_action(controller, FALSE, target_key) + return + + living_pawn.start_perching(target) + finish_action(controller, TRUE, target_key) + +/datum/ai_behavior/perch_on_target/proc/check_human_conditions(mob/living/living_human) + if(living_human.stat == DEAD || LAZYLEN(living_human.buckled_mobs) >= living_human.max_buckled_mobs) + return FALSE + + return TRUE + +/datum/ai_behavior/perch_on_target/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + controller.clear_blackboard_key(target_key) + +/datum/ai_behavior/unbuckle_mob + +/datum/ai_behavior/unbuckle_mob/perform(seconds_per_tick, datum/ai_controller/controller) + . = ..() + + var/mob/living/living_pawn = controller.pawn + var/atom/movable/buckled_too = living_pawn.buckled + + if(isnull(buckled_too)) + finish_action(controller, FALSE) + return + + buckled_too.unbuckle_mob(living_pawn) + finish_action(controller, TRUE) + + +//ghost poly + +/datum/ai_controller/basic_controller/parrot/ghost + planning_subtrees = list( + /datum/ai_planning_subtree/parrot_as_in_repeat, + /datum/ai_planning_subtree/possess_humans, + /datum/ai_planning_subtree/hoard_items, ) + +///subtree to possess humans +/datum/ai_planning_subtree/possess_humans + ///chance we go possess humans + var/possess_chance = 2 + +/datum/ai_planning_subtree/possess_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + + if(controller.blackboard_key_exists(BB_PERCH_TARGET)) + controller.queue_behavior(/datum/ai_behavior/perch_on_target/haunt, BB_PERCH_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + + if(!SPT_PROB(possess_chance, seconds_per_tick)) + if(ishuman(living_pawn.loc)) + return SUBTREE_RETURN_FINISH_PLANNING + return + + if(ishuman(living_pawn.loc)) + controller.set_blackboard_key(living_pawn.loc) + return + + controller.queue_behavior(/datum/ai_behavior/find_and_set/conscious_person, BB_PERCH_TARGET) + + +/datum/ai_behavior/perch_on_target/haunt + +/datum/ai_behavior/perch_on_target/haunt/check_human_conditions(mob/living/living_human) + if(living_human.stat == DEAD) + return FALSE + return TRUE diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm deleted file mode 100644 index ccf4cec4efbbf..0000000000000 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_search.dm +++ /dev/null @@ -1,18 +0,0 @@ -/// Looks for cool items, or maybe people with cool items to start hordeing stuff. -/datum/ai_planning_subtree/find_interesting_things - -/datum/ai_planning_subtree/find_interesting_things/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(!QDELETED(target)) - // Busy with something - return - - var/atom/perch = controller.blackboard[BB_PARROT_INANIMATE_PERCH] - if(QDELETED(perch)) // first order is to nab a nice perch - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/turf_location, BB_PARROT_INANIMATE_PERCH, controller.blackboard[BB_PARROT_PERCH_TYPES]) - return SUBTREE_RETURN_FINISH_PLANNING - - // todo, differentiate between people we wanna rob and people we wanna perch on, as well as items we wanna hoard - diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index e427819444405..10e680e03ac0e 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -3,15 +3,16 @@ operational_datums = list(/datum/component/listen_and_repeat) /datum/ai_planning_subtree/parrot_as_in_repeat/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - var/atom/speaking_pawn = controller.pawn - var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] - var/probability = controller.blackboard[BB_PARROT_REPEAT_PROBABILITY] - var/return_value = SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) // we always grab a new phrase every time this fires for randomness - if(prob(probability) || return_value & NO_NEW_PHRASE_AVAILABLE) + if(!SPT_PROB(controller.blackboard[BB_PARROT_REPEAT_PROBABILITY], seconds_per_tick)) return - potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] + var/atom/speaking_pawn = controller.pawn + var/switch_up_probability = controller.blackboard[BB_PARROT_PHRASE_CHANGE_PROBABILITY] + if(SPT_PROB(switch_up_probability, seconds_per_tick) || isnull(controller.blackboard[BB_PARROT_REPEAT_STRING])) + if(SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) & NO_NEW_PHRASE_AVAILABLE) + return + + var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] if(isnull(potential_string)) stack_trace("Parrot As In Repeat Subtree somehow is getting a null potential string while not getting `NO_NEW_PHRASE_AVAILABLE`!") return diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm index 61da64bd48d9a..c071bf7fdbe62 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_items.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_items.dm @@ -2,7 +2,7 @@ key = STRIPPABLE_ITEM_PARROT_HEADSET /datum/strippable_item/parrot_headset/get_item(atom/source) - var/mob/living/simple_animal/parrot/parrot_source = source + var/mob/living/basic/parrot/poly/parrot_source = source return istype(parrot_source) ? parrot_source.ears : null /datum/strippable_item/parrot_headset/try_equip(atom/source, obj/item/equipping, mob/user) @@ -41,17 +41,18 @@ if (!.) return FALSE - var/mob/living/simple_animal/parrot/parrot_source = source + var/mob/living/basic/parrot/parrot_source = source if (!istype(parrot_source)) return if (parrot_source.stat == CONSCIOUS) - parrot_source.say("[parrot_source.available_channels.len ? "[pick(parrot_source.available_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!", forced = "attempted headset removal") + var/list/list_of_channels = parrot_source.get_available_channels() + parrot_source.say("[list_of_channels ? "[pick(list_of_channels)] " : null]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!", forced = "attempted headset removal") return TRUE /datum/strippable_item/parrot_headset/finish_unequip(atom/source, mob/user) - var/mob/living/simple_animal/parrot/parrot_source = source + var/mob/living/basic/parrot/parrot_source = source if (!istype(parrot_source)) return diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 8b98dfa9783f0..f6788006b1c54 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -6,6 +6,10 @@ #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 @@ -193,18 +197,54 @@ name = "The Ghost of Poly" desc = "Doomed to squawk the Earth." color = "#FFFFFF77" - //speak_chance = 20 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 /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 diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 88e5941b5e51e..ba2f545ac097b 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -67,7 +67,7 @@ var/list/datum/bioware/biowares /// What types of mobs are allowed to ride/buckle to this mob - var/static/list/can_ride_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot)) + var/static/list/can_ride_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/basic/parrot)) var/lastpuke = 0 var/account_id diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index b9b9d1e3b1b6b..6664ace55cfd7 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1439,6 +1439,7 @@ /mob/living/basic/morph, /mob/living/basic/mouse, /mob/living/basic/mushroom, + /mob/living/basic/parrot, /mob/living/basic/pet/dog/breaddog, /mob/living/basic/pet/dog/corgi, /mob/living/basic/pet/dog/pug, @@ -1449,7 +1450,6 @@ /mob/living/basic/stickman, /mob/living/basic/stickman/dog, /mob/living/simple_animal/hostile/megafauna/dragon/lesser, - /mob/living/simple_animal/parrot, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/cat/cak, ) diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 0cb895d01ed51..6cf82771444ca 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -250,7 +250,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( /mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range=0) - if(!GET_CLIENT(src)) + if((SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_HEAR, args) & COMSIG_MOVABLE_CANCEL_HEARING) || !GET_CLIENT(src)) return FALSE var/deaf_message diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm deleted file mode 100644 index fd41d9085abc9..0000000000000 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ /dev/null @@ -1,549 +0,0 @@ -/* Parrots! - * Contains - * Defines - * Inventory (headset stuff) - * Attack responces - * AI - * Procs / Verbs (usable by players) - * Sub-types - * Hear & say (the things we do for gimmicks) - */ - -/* - * Defines - */ - -//Only a maximum of one action and one intent should be active at any given time. -//Actions -#define PARROT_PERCH (1<<0) //Sitting/sleeping, not moving -#define PARROT_SWOOP (1<<1) //Moving towards or away from a target -#define PARROT_WANDER (1<<2) //Moving without a specific target in mind - -//Intents -#define PARROT_STEAL (1<<3) //Flying towards a target to steal it/from it -#define PARROT_ATTACK (1<<4) //Flying towards a target to attack it -#define PARROT_RETURN (1<<5) //Flying towards its perch -#define PARROT_FLEE (1<<6) //Flying away from its attacker - - -/mob/living/simple_animal/parrot - name = "parrot" - desc = "The parrot squawks, \"They're a Parrot! BAWWK!\"" //' - icon = 'icons/mob/simple/animal.dmi' - icon_state = "parrot_fly" - icon_living = "parrot_fly" - icon_dead = "parrot_dead" - var/icon_sit = "parrot_sit" - density = FALSE - health = 80 - maxHealth = 80 - pass_flags = PASSTABLE | PASSMOB - - speak = list("Hi!","Hello!","Cracker?","BAWWWWK george mellons griffing me!") - speak_emote = list("squawks","says","yells") - emote_hear = list("squawks.","bawks!") - emote_see = list("flutters their wings.") - - speak_chance = 1 //1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s - turns_per_move = 5 - butcher_results = list(/obj/item/food/cracker = 1) - melee_damage_upper = 10 - melee_damage_lower = 5 - - response_help_continuous = "pets" - response_help_simple = "pet" - response_disarm_continuous = "gently moves aside" - response_disarm_simple = "gently move aside" - response_harm_continuous = "swats" - response_harm_simple = "swat" - stop_automated_movement = 1 - combat_mode = TRUE //parrots now start "aggressive" since only player parrots will nuzzle. - attack_verb_continuous = "chomps" - attack_verb_simple = "chomp" - attack_vis_effect = ATTACK_EFFECT_BITE - friendly_verb_continuous = "grooms" - friendly_verb_simple = "groom" - mob_size = MOB_SIZE_SMALL - gold_core_spawnable = FRIENDLY_SPAWN - - var/parrot_damage_upper = 10 - var/parrot_state = PARROT_WANDER //Hunt for a perch when created - var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick. - var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down - var/parrot_dam_zone = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_ARM, BODY_ZONE_R_LEG) //For humans, select a bodypart to attack - - var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. - var/parrot_lastmove = null //Updates/Stores position of the parrot while it's moving - var/parrot_stuck = 0 //If parrot_lastmove hasn't changed, this will increment until it reaches parrot_stuck_threshold - var/parrot_stuck_threshold = 10 //if this == parrot_stuck, it'll force the parrot back to wandering - - var/list/speech_buffer = list() - var/speech_shuffle_rate = 20 - var/list/available_channels = list() - - //Headset for Poly to yell at engineers :) - var/obj/item/radio/headset/ears = null - - //Wheter the Parrot should come with a headset - var/spawn_headset = TRUE - - //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, - //mobs it wants to attack or mobs that have attacked it - var/atom/movable/parrot_interest = null - - //Parrots will generally sit on their perch unless something catches their eye. - //These vars store their preffered perch and if they dont have one, what they can use as a perch - var/obj/parrot_perch = null - var/obj/desired_perches = list(/obj/structure/frame/computer, - /obj/structure/displaycase, - /obj/structure/filingcabinet, - /obj/machinery/teleport, - /obj/machinery/dna_scannernew, - /obj/machinery/telecomms, - /obj/machinery/nuclearbomb, - /obj/machinery/recharge_station, - /obj/machinery/smartfridge, - /obj/machinery/computer, - /obj/machinery/suit_storage_unit, - ) - - ///Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. - ///The AI behavior is nice, but we need this to work without an AI (as well as us having a bunch of snowflakey interactions) - var/obj/item/held_item = null - - -/mob/living/simple_animal/parrot/Initialize(mapload) - . = ..() - parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var - - - - - - - - - - - - - - -/* - * Attack responces - */ -//Humans, monkeys, aliens -/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/user, list/modifiers) - ..() - if(client) - return - if(!stat && user.combat_mode) - - icon_state = icon_living //It is going to be flying regardless of whether it flees or attacks - - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - set_parrot_interest(user) - parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction.. - - if(health > 30) //Let's get in there and squawk it up! - parrot_state |= PARROT_ATTACK - else - parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! -// drop_held_item(0) - if(stat != DEAD && !user.combat_mode) - handle_automated_speech(1) //assured speak/emote - return - -/mob/living/simple_animal/parrot/attack_paw(mob/living/carbon/human/user, list/modifiers) - return attack_hand(user, modifiers) - -/mob/living/simple_animal/parrot/attack_alien(mob/living/carbon/alien/user, list/modifiers) - return attack_hand(user, modifiers) - -//Simple animals -/mob/living/simple_animal/parrot/attack_animal(mob/living/simple_animal/user, list/modifiers) - . = ..() //goodbye immortal parrots - - if(client) - return - - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - if(user.melee_damage_upper > 0 && !stat) - set_parrot_interest(user) - parrot_state = PARROT_SWOOP | PARROT_ATTACK //Attack other animals regardless - icon_state = icon_living - -//Mobs with objects -/mob/living/simple_animal/parrot/attackby(obj/item/O, mob/living/user, params) - if(!stat && !client && !istype(O, /obj/item/stack/medical) && !istype(O, /obj/item/food/cracker)) - if(O.force) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - set_parrot_interest(user) - parrot_state = PARROT_SWOOP - if(health > 30) //Let's get in there and squawk it up! - parrot_state |= PARROT_ATTACK - else - parrot_state |= PARROT_FLEE - icon_state = icon_living -// drop_held_item(0) - -//Bullets -/mob/living/simple_animal/parrot/bullet_act(obj/projectile/Proj) - . = ..() - if(!stat && !client) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - set_parrot_interest(null) - parrot_state = PARROT_WANDER | PARROT_FLEE //Been shot and survived! RUN LIKE HELL! - //parrot_been_shot += 5 - icon_state = icon_living -// drop_held_item(0) - - -/* - * AI - Not really intelligent, but I'm calling it AI anyway. - */ -/mob/living/simple_animal/parrot/Life(seconds_per_tick = SSMOBS_DT, times_fired) - ..() - - //Sprite update for when a parrot gets pulled - if(pulledby && !stat && parrot_state != PARROT_WANDER) - if(buckled) - buckled.unbuckle_mob(src, TRUE) - buckled = null - icon_state = icon_living - parrot_state = PARROT_WANDER - pixel_x = initial(pixel_x) - pixel_y = initial(pixel_y) - return - - - - -/mob/living/simple_animal/parrot/handle_automated_movement() - if(!isturf(src.loc) || !(mobility_flags & MOBILITY_MOVE) || buckled) - return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove) - - if(client && stat == CONSCIOUS && parrot_state != icon_living) - icon_state = icon_living - -//-----SLEEPING - if(parrot_state == PARROT_PERCH) - if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasn't moved our perch on us - if(parrot_perch in view(src)) - parrot_state = PARROT_SWOOP | PARROT_RETURN - icon_state = icon_living - return - else - parrot_state = PARROT_WANDER - icon_state = icon_living - return - - parrot_sleep_dur-- - if(parrot_sleep_dur) //Zzz - return - - else - //This way we only call the stuff below once every [sleep_max] ticks. - parrot_sleep_dur = parrot_sleep_max - - - //Search for item to steal - set_parrot_interest(search_for_item()) - if(parrot_interest) - manual_emote("looks in [parrot_interest]'s direction and takes flight.") - parrot_state = PARROT_SWOOP | PARROT_STEAL - icon_state = icon_living - return - -//-----WANDERING - This is basically a 'I dont know what to do yet' state - else if(parrot_state == PARROT_WANDER) - //Stop movement, we'll set it later - SSmove_manager.stop_looping(src) - set_parrot_interest(null) - - //Wander around aimlessly. This will help keep the loops from searches down - //and possibly move the mob into a new are in view of something they can use - if(prob(90)) - step(src, pick(GLOB.cardinals)) - return - - if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do. - var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item - if(AM) - if(isitem(AM) || isliving(AM)) //If stealable item - set_parrot_interest(AM) - manual_emote("turns and flies towards [parrot_interest].") - parrot_state = PARROT_SWOOP | PARROT_STEAL - return - else //Else it's a perch - parrot_perch = AM - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - return - - if(parrot_interest && (parrot_interest in view(src))) - parrot_state = PARROT_SWOOP | PARROT_STEAL - return - - if(parrot_perch && (parrot_perch in view(src))) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - else //Have an item but no perch? Find one! - parrot_perch = search_for_perch() - if(parrot_perch) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return -//-----STEALING - else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL)) - SSmove_manager.stop_looping(src) - if(!parrot_interest || held_item) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - if(!(parrot_interest in view(src))) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - if(Adjacent(parrot_interest)) - - if(isliving(parrot_interest)) -// steal_from_mob() - - else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch - if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) - held_item = parrot_interest - parrot_interest.forceMove(src) - visible_message(span_notice("[src] grabs [held_item]!"), span_notice("You grab [held_item]!"), span_hear("You hear the sounds of wings flapping furiously.")) - - set_parrot_interest(null) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - SSmove_manager.move_to(src, parrot_interest, 1, parrot_speed) - if(isStuck()) - return - - return - -//-----RETURNING TO PERCH - else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN)) - SSmove_manager.stop_looping(src) - if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isn't inside of something else. - parrot_perch = null - parrot_state = PARROT_WANDER - return - - if(Adjacent(parrot_perch)) - forceMove(parrot_perch.loc) -// drop_held_item() - parrot_state = PARROT_PERCH - icon_state = icon_sit - return - - SSmove_manager.move_to(src, parrot_perch, 1, parrot_speed) - if(isStuck()) - return - - return - -//-----FLEEING - else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE)) - SSmove_manager.stop_looping(src) - if(!parrot_interest || !isliving(parrot_interest)) //Sanity - parrot_state = PARROT_WANDER - - SSmove_manager.move_away(src, parrot_interest, 1, parrot_speed) - if(isStuck()) - return - - return - -//-----ATTACKING - else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) - - //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander - if(!parrot_interest || !isliving(parrot_interest)) - set_parrot_interest(null) - parrot_state = PARROT_WANDER - return - - var/mob/living/L = parrot_interest - if(melee_damage_upper == 0) - melee_damage_upper = parrot_damage_upper - set_combat_mode(TRUE) - - //If the mob is close enough to interact with - if(Adjacent(parrot_interest)) - - //If the mob we've been chasing/attacking dies or falls into crit, check for loot! - if(L.stat) - set_parrot_interest(null) - if(!held_item) - held_item = steal_from_ground() - if(!held_item) - held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. - if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home - parrot_state = PARROT_SWOOP | PARROT_RETURN - else - parrot_state = PARROT_WANDER - parrot_interest = null -// if(!held_item) -// held_item = steal_from_ground() -// if(!held_item) -// held_item = //Apparently it's possible for dead mobs to hang onto items in certain circumstances. -// if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home -// parrot_state = PARROT_SWOOP | PARROT_RETURN -// else -// parrot_state = PARROT_WANDER - return - - attack_verb_continuous = pick("claws at", "chomps") - attack_verb_simple = pick("claw at", "chomp") - L.attack_animal(src)//Time for the hurt to begin! - //Otherwise, fly towards the mob! - else - SSmove_manager.move_to(src, parrot_interest, 1, parrot_speed) - if(isStuck()) - return - - return -//-----STATE MISHAP - else //This should not happen. If it does lets reset everything and try again - SSmove_manager.stop_looping(src) - set_parrot_interest(null) - parrot_perch = null - // drop_held_item() - parrot_state = PARROT_WANDER - return - -/* - * Procs - */ - -/mob/living/simple_animal/parrot/proc/set_parrot_interest(atom/movable/shiny) - if(parrot_interest) - UnregisterSignal(parrot_interest, COMSIG_QDELETING) - parrot_interest = shiny - if(parrot_interest) - RegisterSignal(parrot_interest, COMSIG_QDELETING, PROC_REF(shiny_deleted)) - -/mob/living/simple_animal/parrot/proc/shiny_deleted(datum/source) - SIGNAL_HANDLER - set_parrot_interest(null) - -/mob/living/simple_animal/parrot/proc/isStuck() - //Check to see if the parrot is stuck due to things like windows or doors or windowdoors - if(parrot_lastmove) - if(parrot_lastmove == src.loc) - if(parrot_stuck_threshold >= ++parrot_stuck) //If it has been stuck for a while, go back to wander. - parrot_state = PARROT_WANDER - parrot_stuck = 0 - parrot_lastmove = null - return TRUE - else - parrot_lastmove = null - else - parrot_lastmove = src.loc - return FALSE - -/mob/living/simple_animal/parrot/proc/search_for_item() - var/item - for(var/atom/movable/AM in view(src)) - //Skip items we already stole or are wearing or are too big - if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) - continue - if(isitem(AM)) - var/obj/item/I = AM - if(I.w_class < WEIGHT_CLASS_SMALL) - item = I - else if(iscarbon(AM)) - var/mob/living/carbon/C = AM - for(var/obj/item/I in C.held_items) - if(I.w_class <= WEIGHT_CLASS_SMALL) - item = I - break - if(item) - if(!length(get_path_to(src, item))) // WHY DO WE DISREGARD THE PATH AHHHHHH - item = null - continue - return item - -/mob/living/simple_animal/parrot/proc/search_for_perch() - for(var/obj/O in view(src)) - for(var/path in desired_perches) - if(istype(O, path)) - return O - return null - -//This proc was made to save on doing two 'in view' loops seperatly -/mob/living/simple_animal/parrot/proc/search_for_perch_and_item() - for(var/atom/movable/AM in view(src)) - for(var/perch_path in desired_perches) - if(istype(AM, perch_path)) - return AM - - //Skip items we already stole or are wearing or are too big - if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) - continue - - if(isitem(AM)) - var/obj/item/I = AM - if(I.w_class <= WEIGHT_CLASS_SMALL) - return I - - if(iscarbon(AM)) - var/mob/living/carbon/C = AM - for(var/obj/item/I in C.held_items) - if(I.w_class <= WEIGHT_CLASS_SMALL) - return C - return null - - - - - - - - - - -/mob/living/simple_animal/parrot/poly/ghost/handle_automated_speech() - if(ismob(loc)) - return - ..() - -/mob/living/simple_animal/parrot/poly/ghost/handle_automated_movement() - if(isliving(parrot_interest)) - if(!ishuman(parrot_interest)) - set_parrot_interest(null) - else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK) && Adjacent(parrot_interest)) - SSmove_manager.move_to(src, parrot_interest, 0, parrot_speed) - Possess(parrot_interest) - ..() - -/mob/living/simple_animal/parrot/poly/ghost/proc/Possess(mob/living/carbon/human/H) - if(!ishuman(H)) - return - var/datum/disease/parrot_possession/P = new - P.parrot = src - forceMove(H) - H.ForceContractDisease(P, FALSE) - set_parrot_interest(null) - H.visible_message(span_danger("[src] dive bombs into [H]'s chest and vanishes!"), span_userdanger("[src] dive bombs into your chest, vanishing! This can't be good!")) - -#undef PARROT_PERCH -#undef PARROT_SWOOP -#undef PARROT_WANDER -#undef PARROT_STEAL -#undef PARROT_ATTACK -#undef PARROT_RETURN -#undef PARROT_FLEE diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 790f9312ea170..6136f8c818964 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -398,7 +398,7 @@ return TRUE if(ispath(MP, /mob/living/basic/bear)) return TRUE - if(ispath(MP, /mob/living/simple_animal/parrot)) + if(ispath(MP, /mob/living/basic/parrot)) return TRUE //Parrots are no longer unfinished! -Nodrak //Not in here? Must be untested! diff --git a/code/modules/unit_tests/required_map_items.dm b/code/modules/unit_tests/required_map_items.dm index 39930afd822c2..1f03295f00e79 100644 --- a/code/modules/unit_tests/required_map_items.dm +++ b/code/modules/unit_tests/required_map_items.dm @@ -18,7 +18,7 @@ expected_types += /obj/machinery/computer/communications expected_types += /mob/living/carbon/human/species/monkey/punpun expected_types += /mob/living/basic/pet/dog/corgi/ian - expected_types += /mob/living/simple_animal/parrot/poly + expected_types += /mob/living/basic/parrot/poly expected_types += /obj/machinery/drone_dispenser /datum/unit_test/required_map_items/Run() diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 5bc4e7d82b468..3066ed2f49888 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -92,10 +92,6 @@ /mob/living/simple_animal/hostile/retaliate/goose/vomit, /mob/living/simple_animal/hostile/vatbeast, /mob/living/simple_animal/hostile/zombie, - /mob/living/simple_animal/parrot, - /mob/living/simple_animal/parrot/natural, - /mob/living/simple_animal/parrot/poly, - /mob/living/simple_animal/parrot/poly/ghost, /mob/living/simple_animal/pet, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/cat/_proc, diff --git a/html/changelogs/AutoChangeLog-pr-79666.yml b/html/changelogs/AutoChangeLog-pr-79666.yml new file mode 100644 index 0000000000000..a75ac32bfea57 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79666.yml @@ -0,0 +1,4 @@ +author: "distributivgesetz" +delete-after: True +changes: + - bugfix: "Removes some roundstart active turfs." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79674.yml b/html/changelogs/AutoChangeLog-pr-79674.yml new file mode 100644 index 0000000000000..969832148b5b0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79674.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - bugfix: "Fixes some potential exploits and issues involving shielded equipment." \ No newline at end of file diff --git a/tgstation.dme b/tgstation.dme index d2504abbbea42..cd7b6cbc605e2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1075,8 +1075,8 @@ #include "code\datums\components\leash.dm" #include "code\datums\components\life_link.dm" #include "code\datums\components\light_eater.dm" -#include "code\datums\components\listen_and_repeat.dm" #include "code\datums\components\ling_decoy_brain.dm" +#include "code\datums\components\listen_and_repeat.dm" #include "code\datums\components\lock_on_cursor.dm" #include "code\datums\components\magnet.dm" #include "code\datums\components\manual_blinking.dm" @@ -4572,7 +4572,6 @@ #include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" #include "code\modules\mob\living\basic\pets\parrot\poly.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_controller.dm" -#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_search.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" @@ -4819,7 +4818,6 @@ #include "code\modules\mob\living\silicon\robot\robot_say.dm" #include "code\modules\mob\living\simple_animal\animal_defense.dm" #include "code\modules\mob\living\simple_animal\damage_procs.dm" -#include "code\modules\mob\living\simple_animal\parrot.dm" #include "code\modules\mob\living\simple_animal\simple_animal.dm" #include "code\modules\mob\living\simple_animal\bot\bot.dm" #include "code\modules\mob\living\simple_animal\bot\bot_announcement.dm" From af3678cdbea8c35b1eadfa1104cb44ca70055cec Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 13 Nov 2023 00:55:49 -0700 Subject: [PATCH 42/69] fixes alphabetizations --- code/_globalvars/phobias.dm | 2 +- code/datums/memory/_memory.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm index c40f488c5df8d..3aa6c41de4f7f 100644 --- a/code/_globalvars/phobias.dm +++ b/code/_globalvars/phobias.dm @@ -61,8 +61,8 @@ GLOBAL_LIST_INIT(phobia_mobs, list( "birds" = typecacheof(list( /mob/living/basic/chick, /mob/living/basic/chicken, - /mob/living/basic/pet/penguin, /mob/living/basic/parrot, + /mob/living/basic/pet/penguin, )), "conspiracies" = typecacheof(list( /mob/living/basic/drone, diff --git a/code/datums/memory/_memory.dm b/code/datums/memory/_memory.dm index 5afde7843ea30..dd571c85746c8 100644 --- a/code/datums/memory/_memory.dm +++ b/code/datums/memory/_memory.dm @@ -266,6 +266,7 @@ /mob/living/basic/morph, /mob/living/basic/mouse, /mob/living/basic/mushroom, + /mob/living/basic/parrot, /mob/living/basic/pet/dog/breaddog, /mob/living/basic/pet/dog/corgi, /mob/living/basic/pet/dog/pug, @@ -276,7 +277,6 @@ /mob/living/basic/stickman, /mob/living/basic/stickman/dog, /mob/living/simple_animal/hostile/megafauna/dragon/lesser, - /mob/living/basic/parrot, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/cat/cak, /obj/item/food/sausage/american, From 43e0c71e6df8dd93bc15dd8dab373883b0294bed Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 13 Nov 2023 00:58:22 -0700 Subject: [PATCH 43/69] multilines that addcomponent --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 5d9c2e395e037..99a4550e11cba 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -81,12 +81,18 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( update_speech_blackboards() ai_controller.set_blackboard_key(BB_PARROT_PERCH_TYPES, desired_perches) ai_controller.set_blackboard_key(BB_IGNORE_ITEMS, ignore_items) - AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cracker), tame_chance = 100, bonus_tame_chance = 0, after_tame = CALLBACK(src, PROC_REF(tamed))) - AddComponent(/datum/component/listen_and_repeat, desired_phrases = get_static_list_of_phrases(), blackboard_key = BB_PARROT_REPEAT_STRING) AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) AddElement(/datum/element/basic_eating, food_types = list(/obj/item/food/cracker)) + AddComponent(/datum/component/listen_and_repeat, desired_phrases = get_static_list_of_phrases(), blackboard_key = BB_PARROT_REPEAT_STRING) + AddComponent(\ + /datum/component/tameable,\ + food_types = list(/obj/item/food/cracker),\ + tame_chance = 100,\ + bonus_tame_chance = 0,\ + after_tame = CALLBACK(src, PROC_REF(tamed)),\ + ) RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking)) From 75dcc58e2f3bf69bf27a8bbe3d21efdb8aecd4eb Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 13 Nov 2023 00:59:49 -0700 Subject: [PATCH 44/69] removes the movemanager thing --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 99a4550e11cba..27c2af0bbf8b9 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -94,7 +94,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( after_tame = CALLBACK(src, PROC_REF(tamed)),\ ) - RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking)) RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click)) RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attacked)) // this means we could have a peaceful interaction, like getting a cracker @@ -117,8 +116,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( ears.forceMove(drop_location()) ears = null - //SSmove_manager.stop_looping(src) - if(!isnull(buckled)) buckled.unbuckle_mob(src, force = TRUE) buckled = null From ff12c7b1386f160e55412a9c05003286827b822d Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 13 Nov 2023 01:06:53 -0700 Subject: [PATCH 45/69] qdels on login --- code/datums/components/listen_and_repeat.dm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index 0b816f722f27e..64d220ea5bc80 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -3,7 +3,7 @@ /// Tendency we have to ignore radio chatter #define RADIO_IGNORE_CHANCE 10 -/// Simple element that will deterministically set a value based on stuff that the source has heard and will then compel the source to repeat it.area +/// Simple element that will deterministically set a value based on stuff that the source has heard and will then compel the source to repeat it. /// Requires a valid AI Blackboard. /datum/component/listen_and_repeat /// List of things that we start out having in our speech buffer @@ -30,7 +30,12 @@ RegisterSignal(parent, COMSIG_MOVABLE_PRE_HEAR, PROC_REF(on_hear)) RegisterSignal(parent, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_phrase)) RegisterSignal(parent, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory)) - // register to detach when a client logs in maybe + RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(on_login)) + +/// Called if a client logs in- don't want to be forced speaking while under their control (sadly) +/datum/component/listen_and_repeat/proc/on_login(datum/source) + SIGNAL_HANDLER + qdel(src) /// Called when we hear something. /datum/component/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments) From 36c31863d6232e04be39d26a1d700bdfe9f50942 Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Wed, 15 Nov 2023 06:40:17 +0200 Subject: [PATCH 46/69] Oojo (#35) * ppokp * Update parrot.dm --- code/datums/components/listen_and_repeat.dm | 6 ------ code/datums/diseases/parrotpossession.dm | 13 +++++++------ code/modules/mob/living/basic/pets/parrot/parrot.dm | 7 +++++++ .../pets/parrot/parrot_ai/parrot_controller.dm | 8 +++++++- .../basic/pets/parrot/parrot_ai/parroting_action.dm | 7 ++++--- html/changelogs/AutoChangeLog-pr-79508.yml | 6 ++++++ html/changelogs/AutoChangeLog-pr-79521.yml | 6 ++++++ html/changelogs/AutoChangeLog-pr-79564.yml | 10 ++++++++++ html/changelogs/AutoChangeLog-pr-79592.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-79615.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-79656.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-79665.yml | 5 +++++ html/changelogs/AutoChangeLog-pr-79667.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-79677.yml | 5 +++++ html/changelogs/AutoChangeLog-pr-79681.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-79685.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-79689.yml | 4 ++++ html/changelogs/AutoChangeLog-pr-79696.yml | 4 ++++ 18 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 html/changelogs/AutoChangeLog-pr-79508.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79521.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79564.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79592.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79615.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79656.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79665.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79667.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79677.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79681.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79685.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79689.yml create mode 100644 html/changelogs/AutoChangeLog-pr-79696.yml diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index 64d220ea5bc80..093557ec03b82 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -30,12 +30,6 @@ RegisterSignal(parent, COMSIG_MOVABLE_PRE_HEAR, PROC_REF(on_hear)) RegisterSignal(parent, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_phrase)) RegisterSignal(parent, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory)) - RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(on_login)) - -/// Called if a client logs in- don't want to be forced speaking while under their control (sadly) -/datum/component/listen_and_repeat/proc/on_login(datum/source) - SIGNAL_HANDLER - qdel(src) /// Called when we hear something. /datum/component/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments) diff --git a/code/datums/diseases/parrotpossession.dm b/code/datums/diseases/parrotpossession.dm index c2828fc6bb22f..2041810581305 100644 --- a/code/datums/diseases/parrotpossession.dm +++ b/code/datums/diseases/parrotpossession.dm @@ -15,18 +15,18 @@ bypasses_immunity = TRUE //2spook ///chance we speak var/speak_chance = 5 - ///list of words we can use - var/list/speech_list = null + ///controller we speak from + var/datum/ai_controller/basic_controller/parrot_controller /datum/disease/parrot_possession/stage_act(seconds_per_tick, times_fired) . = ..() - if(!.) + if(!. || isnull(parrot_controller)) return - if(LAZYLEN(speech_list) && SPT_PROB(speak_chance, seconds_per_tick)) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach - affected_mob.say(pick(speech_list), forced = "parrot possession") + if(SPT_PROB(speak_chance, seconds_per_tick) && !isnull(parrot_controller.blackboard[BB_PARROT_REPEAT_STRING])) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach + affected_mob.say(parrot_controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot possession") /datum/disease/parrot_possession/cure() @@ -35,10 +35,11 @@ UnregisterSignal(inside_parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED)) inside_parrot.forceMove(affected_mob.drop_location()) affected_mob.visible_message(span_danger("[inside_parrot] is violently driven out of [affected_mob]!"), span_userdanger("[inside_parrot] bursts out of your chest!")) + parrot_controller = null return ..() /datum/disease/parrot_possession/proc/set_parrot(mob/living/parrot) - speech_list = parrot.ai_controller?.blackboard[BB_EXPORTABLE_STRING_BUFFER_LIST] + parrot_controller = parrot.ai_controller RegisterSignals(parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED), PROC_REF(on_parrot_exit)) /datum/disease/parrot_possession/proc/on_parrot_exit(datum/source) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 27c2af0bbf8b9..fd3509a8122c7 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -98,6 +98,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click)) RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attacked)) // this means we could have a peaceful interaction, like getting a cracker RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_injured)) // this means we got hurt and it's go time + RegisterSignal(src, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(drop_item_on_signal)) /mob/living/basic/parrot/Destroy() // should have cleaned these up on death, but let's be super safe in case that didn't happen @@ -399,3 +400,9 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /mob/living/basic/parrot/proc/tamed() new /obj/effect/temp_visual/heart(drop_location()) + +/mob/living/basic/parrot/proc/drop_item_on_signal(mob/living/user) + SIGNAL_HANDLER + + drop_held_item() + return COMSIG_KB_ACTIVATED diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm index 63f2ed7ed5bac..43f6de54ad8a7 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm @@ -26,6 +26,9 @@ return ..() ///subtree to steal items +/datum/ai_planning_subtree/hoard_items + var/theft_chance = 5 + /datum/ai_planning_subtree/hoard_items/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn @@ -45,8 +48,11 @@ controller.queue_behavior(/datum/ai_behavior/basic_melee_attack/interact_once, BB_HOARD_ITEM_TARGET, BB_TARGETING_STRATEGY) return SUBTREE_RETURN_FINISH_PLANNING + if(!SPT_PROB(theft_chance, seconds_per_tick)) + return controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_item, BB_HOARD_ITEM_TARGET) +/datum/ai_behavior/find_and_set/hoard_location /datum/ai_behavior/find_and_set/hoard_location/search_tactic(datum/ai_controller/controller, locate_path, search_range) for(var/turf/open/candidate in oview(search_range, controller.pawn)) @@ -215,7 +221,7 @@ ///subtree to possess humans /datum/ai_planning_subtree/possess_humans ///chance we go possess humans - var/possess_chance = 2 + var/possess_chance = 80 /datum/ai_planning_subtree/possess_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm index 10e680e03ac0e..d1488a60b3bb9 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parroting_action.dm @@ -3,15 +3,16 @@ operational_datums = list(/datum/component/listen_and_repeat) /datum/ai_planning_subtree/parrot_as_in_repeat/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if(!SPT_PROB(controller.blackboard[BB_PARROT_REPEAT_PROBABILITY], seconds_per_tick)) - return - var/atom/speaking_pawn = controller.pawn + var/switch_up_probability = controller.blackboard[BB_PARROT_PHRASE_CHANGE_PROBABILITY] if(SPT_PROB(switch_up_probability, seconds_per_tick) || isnull(controller.blackboard[BB_PARROT_REPEAT_STRING])) if(SEND_SIGNAL(speaking_pawn, COMSIG_NEEDS_NEW_PHRASE) & NO_NEW_PHRASE_AVAILABLE) return + if(!SPT_PROB(controller.blackboard[BB_PARROT_REPEAT_PROBABILITY], seconds_per_tick)) + return + var/potential_string = controller.blackboard[BB_PARROT_REPEAT_STRING] if(isnull(potential_string)) stack_trace("Parrot As In Repeat Subtree somehow is getting a null potential string while not getting `NO_NEW_PHRASE_AVAILABLE`!") diff --git a/html/changelogs/AutoChangeLog-pr-79508.yml b/html/changelogs/AutoChangeLog-pr-79508.yml new file mode 100644 index 0000000000000..f7de748793093 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79508.yml @@ -0,0 +1,6 @@ +author: "Ben10Omintrix" +delete-after: True +changes: + - refactor: "gutlunches have been refactored into basic mobs. please report any bugs" + - rscadd: "ashwalkers have a small ranch they can manage" + - bugfix: "wall tearer compnent wont runtime when interacting with mineral walls" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79521.yml b/html/changelogs/AutoChangeLog-pr-79521.yml new file mode 100644 index 0000000000000..7e4e76de895b4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79521.yml @@ -0,0 +1,6 @@ +author: "MTandi" +delete-after: True +changes: + - bugfix: "fixed pipe painter not applying pipe color properly" + - qol: "made spraycans work also as pipe painters" + - qol: "spraycans now have basic color presets for quick selection" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79564.yml b/html/changelogs/AutoChangeLog-pr-79564.yml new file mode 100644 index 0000000000000..a19bd6ed69634 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79564.yml @@ -0,0 +1,10 @@ +author: "larentoun" +delete-after: True +changes: + - qol: "Gunpoint: Examining the target will show who is holding them at gunpoint" + - qol: "Gunpoint: Examining the shooter will show who they are holding at gunpoint" + - balance: "Gunpoint: If the target tries to grab, they will trigger the shot" + - balance: "Gunpoint: If the target or the shooter are shoved, it will cancel the gunpoint" + - balance: "Gunpoint: If the target is pulled, it will cancel the gunpoint" + - balance: "Both the target and the shooter can't be bumped anymore to avoid cheesing charged shot or removing the gunpoint by just moving around" + - bugfix: "Clicking the alert button of the shooter will now correctly remove gunpoint" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79592.yml b/html/changelogs/AutoChangeLog-pr-79592.yml new file mode 100644 index 0000000000000..ca0ecbf0d454a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79592.yml @@ -0,0 +1,4 @@ +author: "SethLafuente" +delete-after: True +changes: + - bugfix: "fixes half-covering glassware protecting eyes from chemicals" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79615.yml b/html/changelogs/AutoChangeLog-pr-79615.yml new file mode 100644 index 0000000000000..f8ef5a37b7668 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79615.yml @@ -0,0 +1,4 @@ +author: "san7890" +delete-after: True +changes: + - bugfix: "Gorillas and Regal Rats will no longer show up in the ghost-control menu if they died without anyone ever taking control of them." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79656.yml b/html/changelogs/AutoChangeLog-pr-79656.yml new file mode 100644 index 0000000000000..e679c3e393155 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79656.yml @@ -0,0 +1,4 @@ +author: "1393F" +delete-after: True +changes: + - bugfix: "The Sleeping Carp scroll no longer says deflect using throw mode." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79665.yml b/html/changelogs/AutoChangeLog-pr-79665.yml new file mode 100644 index 0000000000000..74678e3bf963e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79665.yml @@ -0,0 +1,5 @@ +author: "MTandi" +delete-after: True +changes: + - image: "Crates got new sprites" + - image: "Added more crate styles" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79667.yml b/html/changelogs/AutoChangeLog-pr-79667.yml new file mode 100644 index 0000000000000..1b029aab0d314 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79667.yml @@ -0,0 +1,4 @@ +author: "distributivgesetz" +delete-after: True +changes: + - code_imp: "Atoms no longer break again after they are hit when broken, making them hopefully more stable in the future." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79677.yml b/html/changelogs/AutoChangeLog-pr-79677.yml new file mode 100644 index 0000000000000..798824d1d5471 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79677.yml @@ -0,0 +1,5 @@ +author: "Danny Boy" +delete-after: True +changes: + - bugfix: "Fixed Signer eyebrow raising/lowering indicators and emotes" + - bugfix: "Fixed Signer RuneChat punctuation" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79681.yml b/html/changelogs/AutoChangeLog-pr-79681.yml new file mode 100644 index 0000000000000..eacf2cdfc8090 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79681.yml @@ -0,0 +1,4 @@ +author: "OrionTheFox" +delete-after: True +changes: + - image: "Resprites the Reactive Anomaly Armor" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79685.yml b/html/changelogs/AutoChangeLog-pr-79685.yml new file mode 100644 index 0000000000000..6a5eb7f2642a1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79685.yml @@ -0,0 +1,4 @@ +author: "Danny Boy" +delete-after: True +changes: + - bugfix: "Signers no longer sign with their species' tongue" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79689.yml b/html/changelogs/AutoChangeLog-pr-79689.yml new file mode 100644 index 0000000000000..8bbdcbd46705a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79689.yml @@ -0,0 +1,4 @@ +author: "Majkl-J" +delete-after: True +changes: + - bugfix: "You can now eject blank IDs from modular computers" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79696.yml b/html/changelogs/AutoChangeLog-pr-79696.yml new file mode 100644 index 0000000000000..98141a649030c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-79696.yml @@ -0,0 +1,4 @@ +author: "Rhials" +delete-after: True +changes: + - bugfix: "The full mining lockers in the Lavaland Mafia map have been replaced with (empty) mining carts." \ No newline at end of file From 55216cd4035fcdd7e703c8fb441953cc37c528db Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 21:41:35 -0700 Subject: [PATCH 47/69] deletes the autochangelogs --- html/changelogs/AutoChangeLog-pr-79508.yml | 6 ------ html/changelogs/AutoChangeLog-pr-79521.yml | 6 ------ html/changelogs/AutoChangeLog-pr-79564.yml | 10 ---------- html/changelogs/AutoChangeLog-pr-79592.yml | 4 ---- html/changelogs/AutoChangeLog-pr-79615.yml | 4 ---- html/changelogs/AutoChangeLog-pr-79656.yml | 4 ---- html/changelogs/AutoChangeLog-pr-79665.yml | 5 ----- html/changelogs/AutoChangeLog-pr-79667.yml | 4 ---- html/changelogs/AutoChangeLog-pr-79677.yml | 5 ----- html/changelogs/AutoChangeLog-pr-79681.yml | 4 ---- html/changelogs/AutoChangeLog-pr-79685.yml | 4 ---- html/changelogs/AutoChangeLog-pr-79689.yml | 4 ---- html/changelogs/AutoChangeLog-pr-79696.yml | 4 ---- 13 files changed, 64 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-79508.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79521.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79564.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79592.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79615.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79656.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79665.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79667.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79677.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79681.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79685.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79689.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79696.yml diff --git a/html/changelogs/AutoChangeLog-pr-79508.yml b/html/changelogs/AutoChangeLog-pr-79508.yml deleted file mode 100644 index f7de748793093..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79508.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: "Ben10Omintrix" -delete-after: True -changes: - - refactor: "gutlunches have been refactored into basic mobs. please report any bugs" - - rscadd: "ashwalkers have a small ranch they can manage" - - bugfix: "wall tearer compnent wont runtime when interacting with mineral walls" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79521.yml b/html/changelogs/AutoChangeLog-pr-79521.yml deleted file mode 100644 index 7e4e76de895b4..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79521.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: "MTandi" -delete-after: True -changes: - - bugfix: "fixed pipe painter not applying pipe color properly" - - qol: "made spraycans work also as pipe painters" - - qol: "spraycans now have basic color presets for quick selection" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79564.yml b/html/changelogs/AutoChangeLog-pr-79564.yml deleted file mode 100644 index a19bd6ed69634..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79564.yml +++ /dev/null @@ -1,10 +0,0 @@ -author: "larentoun" -delete-after: True -changes: - - qol: "Gunpoint: Examining the target will show who is holding them at gunpoint" - - qol: "Gunpoint: Examining the shooter will show who they are holding at gunpoint" - - balance: "Gunpoint: If the target tries to grab, they will trigger the shot" - - balance: "Gunpoint: If the target or the shooter are shoved, it will cancel the gunpoint" - - balance: "Gunpoint: If the target is pulled, it will cancel the gunpoint" - - balance: "Both the target and the shooter can't be bumped anymore to avoid cheesing charged shot or removing the gunpoint by just moving around" - - bugfix: "Clicking the alert button of the shooter will now correctly remove gunpoint" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79592.yml b/html/changelogs/AutoChangeLog-pr-79592.yml deleted file mode 100644 index ca0ecbf0d454a..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79592.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SethLafuente" -delete-after: True -changes: - - bugfix: "fixes half-covering glassware protecting eyes from chemicals" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79615.yml b/html/changelogs/AutoChangeLog-pr-79615.yml deleted file mode 100644 index f8ef5a37b7668..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79615.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "san7890" -delete-after: True -changes: - - bugfix: "Gorillas and Regal Rats will no longer show up in the ghost-control menu if they died without anyone ever taking control of them." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79656.yml b/html/changelogs/AutoChangeLog-pr-79656.yml deleted file mode 100644 index e679c3e393155..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79656.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "1393F" -delete-after: True -changes: - - bugfix: "The Sleeping Carp scroll no longer says deflect using throw mode." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79665.yml b/html/changelogs/AutoChangeLog-pr-79665.yml deleted file mode 100644 index 74678e3bf963e..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79665.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "MTandi" -delete-after: True -changes: - - image: "Crates got new sprites" - - image: "Added more crate styles" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79667.yml b/html/changelogs/AutoChangeLog-pr-79667.yml deleted file mode 100644 index 1b029aab0d314..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79667.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "distributivgesetz" -delete-after: True -changes: - - code_imp: "Atoms no longer break again after they are hit when broken, making them hopefully more stable in the future." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79677.yml b/html/changelogs/AutoChangeLog-pr-79677.yml deleted file mode 100644 index 798824d1d5471..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79677.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Danny Boy" -delete-after: True -changes: - - bugfix: "Fixed Signer eyebrow raising/lowering indicators and emotes" - - bugfix: "Fixed Signer RuneChat punctuation" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79681.yml b/html/changelogs/AutoChangeLog-pr-79681.yml deleted file mode 100644 index eacf2cdfc8090..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79681.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "OrionTheFox" -delete-after: True -changes: - - image: "Resprites the Reactive Anomaly Armor" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79685.yml b/html/changelogs/AutoChangeLog-pr-79685.yml deleted file mode 100644 index 6a5eb7f2642a1..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79685.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Danny Boy" -delete-after: True -changes: - - bugfix: "Signers no longer sign with their species' tongue" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79689.yml b/html/changelogs/AutoChangeLog-pr-79689.yml deleted file mode 100644 index 8bbdcbd46705a..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79689.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Majkl-J" -delete-after: True -changes: - - bugfix: "You can now eject blank IDs from modular computers" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79696.yml b/html/changelogs/AutoChangeLog-pr-79696.yml deleted file mode 100644 index 98141a649030c..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79696.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Rhials" -delete-after: True -changes: - - bugfix: "The full mining lockers in the Lavaland Mafia map have been replaced with (empty) mining carts." \ No newline at end of file From 09961e5e8cfd6043edf32b20e7c15cf07d7f629f Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:21:09 -0700 Subject: [PATCH 48/69] caches code + code opt --- code/datums/diseases/parrotpossession.dm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/code/datums/diseases/parrotpossession.dm b/code/datums/diseases/parrotpossession.dm index 2041810581305..2645ef8e19173 100644 --- a/code/datums/diseases/parrotpossession.dm +++ b/code/datums/diseases/parrotpossession.dm @@ -25,8 +25,10 @@ if(!. || isnull(parrot_controller)) return - if(SPT_PROB(speak_chance, seconds_per_tick) && !isnull(parrot_controller.blackboard[BB_PARROT_REPEAT_STRING])) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach - affected_mob.say(parrot_controller.blackboard[BB_PARROT_REPEAT_STRING], forced = "parrot possession") + var/potential_phrase = parrot_controller.blackboard[BB_PARROT_REPEAT_STRING] + + if(SPT_PROB(speak_chance, seconds_per_tick) && !isnull(potential_phrase)) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach + affected_mob.say(potential_phrase, forced = "parrot possession") /datum/disease/parrot_possession/cure() @@ -34,7 +36,10 @@ if(inside_parrot) UnregisterSignal(inside_parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED)) inside_parrot.forceMove(affected_mob.drop_location()) - affected_mob.visible_message(span_danger("[inside_parrot] is violently driven out of [affected_mob]!"), span_userdanger("[inside_parrot] bursts out of your chest!")) + affected_mob.visible_message( + span_danger("[inside_parrot] is violently driven out of [affected_mob]!"), + span_userdanger("[inside_parrot] bursts out of your chest!"), + ) parrot_controller = null return ..() From 4c2dca3fb1a490abc7cb37d61294ed90af64cdee Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:28:22 -0700 Subject: [PATCH 49/69] more changelog cleaning --- html/changelogs/AutoChangeLog-pr-79666.yml | 4 ---- html/changelogs/AutoChangeLog-pr-79674.yml | 4 ---- 2 files changed, 8 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-79666.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-79674.yml diff --git a/html/changelogs/AutoChangeLog-pr-79666.yml b/html/changelogs/AutoChangeLog-pr-79666.yml deleted file mode 100644 index a75ac32bfea57..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79666.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "distributivgesetz" -delete-after: True -changes: - - bugfix: "Removes some roundstart active turfs." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-79674.yml b/html/changelogs/AutoChangeLog-pr-79674.yml deleted file mode 100644 index 969832148b5b0..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-79674.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - bugfix: "Fixes some potential exploits and issues involving shielded equipment." \ No newline at end of file From 4485ffffb25abbd0474c0e0616ea494d1e7356b8 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:29:11 -0700 Subject: [PATCH 50/69] oh i fucked up that merge conflict --- .../basic/pets/parrot/parrot_subtypes.dm | 164 +----------------- 1 file changed, 1 insertion(+), 163 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm index 1bedb4f6a7425..0469fea35a476 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm @@ -1,166 +1,4 @@ -/// 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" - -/// 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." - speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN MODSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE") - gold_core_spawnable = NO_SPAWN - speak_chance = 3 - - /// 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(SStts.tts_enabled) - 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. - - REGISTER_REQUIRED_MAP_ITEM(1, 1) // every map needs a poly! - -/mob/living/simple_animal/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/simple_animal/parrot/poly/ghost/G = new(loc) // san7890 make this the transfer mob proc - if(mind) - mind.transfer_to(G) - else - G.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!!! - 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 - 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(!.) - return - - 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 - - var/formatted_data -#if DM_VERSION >= 515 - formatted_data = json_encode(file_data, JSON_PRETTY_PRINT) -#else - formatted_data = json_encode(file_data) -#endif - - rustg_file_write(formatted_data, file_path) - -/mob/living/basic/parrot/poly/setup_headset() - ears = new /obj/item/radio/headset/headset_eng(src) +// this file is for parrots that aren't poly /// Parrot that will just randomly spawn with a headset. Nothing too special beyond that. /mob/living/basic/parrot/headsetted From c665f83bc9f727a1434f46357dc68a7b6d5313f0 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:47:00 -0700 Subject: [PATCH 51/69] moves traits + splits up some fiels --- code/__DEFINES/traits.dm | 2 + .../basic_ai_behaviors/unbuckle_mob.dm | 14 + .../mob/living/basic/pets/parrot/parrot.dm | 12 +- .../parrot/parrot_ai/_parrot_controller.dm | 40 +++ .../parrot_ai/ghost_parrot_controller.dm | 39 +++ .../parrot/parrot_ai/parrot_controller.dm | 251 ------------------ .../pets/parrot/parrot_ai/parrot_hoarding.dm | 74 ++++++ .../pets/parrot/parrot_ai/parrot_perching.dm | 78 ++++++ tgstation.dme | 4 + 9 files changed, 256 insertions(+), 258 deletions(-) create mode 100644 code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm delete mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm create mode 100644 code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 36ed6e816e23c..8311faf69118e 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -204,6 +204,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NO_TRANSFORM "block_transformations" /// Tracks whether we're gonna be a baby alien's mummy. #define TRAIT_XENO_HOST "xeno_host" +/// This parrot is currently perched +#define TRAIT_TRAIT_PARROT_PERCHED "TRAIT_PARROT_PERCHED" /// This mob is immune to stun causing status effects and stamcrit. /// Prefer to use [/mob/living/proc/check_stun_immunity] over checking for this trait exactly. #define TRAIT_STUNIMMUNE "stun_immunity" diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm new file mode 100644 index 0000000000000..d0fb7e3e8c707 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/unbuckle_mob.dm @@ -0,0 +1,14 @@ +/datum/ai_behavior/unbuckle_mob + +/datum/ai_behavior/unbuckle_mob/perform(seconds_per_tick, datum/ai_controller/controller) + . = ..() + + var/mob/living/living_pawn = controller.pawn + var/atom/movable/buckled_too = living_pawn.buckled + + if(isnull(buckled_too)) + finish_action(controller, FALSE) + return + + buckled_too.unbuckle_mob(living_pawn) + finish_action(controller, TRUE) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index fd3509a8122c7..5433ee51cf6df 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -172,11 +172,9 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( return NONE -#define PARROT_PERCHED "parrot_perched" // move this later - /mob/living/basic/parrot/update_icon_state() . = ..() - if(HAS_TRAIT(src, PARROT_PERCHED)) + if(HAS_TRAIT(src, TRAIT_PARROT_PERCHED)) icon_state = icon_sit else icon_state = icon_living @@ -192,7 +190,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Proc that ascertains the type of perch we're dealing with and starts the perching process. /// Returns TRUE if we started perching, FALSE otherwise. /mob/living/basic/parrot/proc/start_perching(atom/target) - if(HAS_TRAIT(src, PARROT_PERCHED)) + if(HAS_TRAIT(src, TRAIT_PARROT_PERCHED)) balloon_alert(src, "already perched!") return FALSE @@ -239,9 +237,9 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /mob/living/basic/parrot/proc/toggle_perched(perched) if(!perched) - REMOVE_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) + REMOVE_TRAIT(src, TRAIT_PARROT_PERCHED, TRAIT_GENERIC) else - ADD_TRAIT(src, PARROT_PERCHED, TRAIT_GENERIC) + ADD_TRAIT(src, TRAIT_PARROT_PERCHED, TRAIT_GENERIC) update_appearance(UPDATE_ICON_STATE) /// Master proc which will determine the intent of OUR attacks on an object and summon the relevant procs accordingly. @@ -356,7 +354,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( . = ..() if(gone != held_item) return - held_item= null + held_item = null /mob/living/basic/parrot/vv_edit_var(var_name, vval) . = ..() // give admins an easier time when it comes to fucking with poly diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm new file mode 100644 index 0000000000000..ea0b9a7101173 --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/_parrot_controller.dm @@ -0,0 +1,40 @@ +/datum/ai_controller/basic_controller/parrot + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, + BB_HOARD_LOCATION_RANGE = 9, + ) + + ai_traits = STOP_MOVING_WHEN_PULLED + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk/parrot + + planning_subtrees = list( + /datum/ai_planning_subtree/target_retaliate, + /datum/ai_planning_subtree/perch_on_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/hoard_items, + /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can + ) + +/datum/idle_behavior/idle_random_walk/parrot + ///chance of us moving while perched + var/walk_chance_when_perched = 5 + +/datum/idle_behavior/idle_random_walk/parrot/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) + var/mob/living/living_pawn = controller.pawn + walk_chance = HAS_TRAIT(living_pawn, TRAIT_PARROT_PERCHED) ? walk_chance_when_perched : initial(walk_chance) + return ..() + +/datum/ai_behavior/travel_towards/and_drop + +/datum/ai_behavior/travel_towards/and_drop/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + var/mob/living/living_mob = controller.pawn + var/obj/drop_item = locate(/obj/item) in (living_mob.contents - typecache_filter_list(living_mob.contents, controller.blackboard[BB_IGNORE_ITEMS])) + drop_item?.forceMove(get_turf(living_mob)) + +/datum/ai_behavior/basic_melee_attack/interact_once/parrot + +/datum/ai_behavior/basic_melee_attack/interact_once/parrot/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, FALSE) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm new file mode 100644 index 0000000000000..add30bbd26ff4 --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm @@ -0,0 +1,39 @@ +/// Used for ghost poly. +/datum/ai_controller/basic_controller/parrot/ghost + planning_subtrees = list( + /datum/ai_planning_subtree/parrot_as_in_repeat, + /datum/ai_planning_subtree/possess_humans, + /datum/ai_planning_subtree/hoard_items, + ) + +///subtree to possess humans +/datum/ai_planning_subtree/possess_humans + ///chance we go possess humans + var/possess_chance = 80 + +/datum/ai_planning_subtree/possess_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + + if(controller.blackboard_key_exists(BB_PERCH_TARGET)) + controller.queue_behavior(/datum/ai_behavior/perch_on_target/haunt, BB_PERCH_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + + if(!SPT_PROB(possess_chance, seconds_per_tick)) + if(ishuman(living_pawn.loc)) + return SUBTREE_RETURN_FINISH_PLANNING + return + + if(ishuman(living_pawn.loc)) + controller.set_blackboard_key(living_pawn.loc) + return + + controller.queue_behavior(/datum/ai_behavior/find_and_set/conscious_person, BB_PERCH_TARGET) + + +/datum/ai_behavior/perch_on_target/haunt + +/datum/ai_behavior/perch_on_target/haunt/check_human_conditions(mob/living/living_human) + if(living_human.stat == DEAD) + return FALSE + return TRUE diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm deleted file mode 100644 index 43f6de54ad8a7..0000000000000 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm +++ /dev/null @@ -1,251 +0,0 @@ -/datum/ai_controller/basic_controller/parrot - blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, - BB_HOARD_LOCATION_RANGE = 9, - ) - - ai_traits = STOP_MOVING_WHEN_PULLED - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk/parrot - - planning_subtrees = list( - /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/perch_on_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree, - /datum/ai_planning_subtree/hoard_items, - /datum/ai_planning_subtree/parrot_as_in_repeat, // always get a witty oneliner in when you can - ) - -/datum/idle_behavior/idle_random_walk/parrot - ///chance of us moving while perched - var/walk_chance_when_perched = 5 - -/datum/idle_behavior/idle_random_walk/parrot/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) - var/mob/living/living_pawn = controller.pawn - walk_chance = HAS_TRAIT(living_pawn, PARROT_PERCHED) ? walk_chance_when_perched : initial(walk_chance) - return ..() - -///subtree to steal items -/datum/ai_planning_subtree/hoard_items - var/theft_chance = 5 - -/datum/ai_planning_subtree/hoard_items/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - - var/turf/myspace = controller.blackboard[BB_HOARD_LOCATION] - - if(isnull(myspace) || myspace.is_blocked_turf(source_atom = controller.pawn) || get_dist(myspace, controller.pawn) > controller.blackboard[BB_HOARD_LOCATION_RANGE]) - controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_location, BB_HOARD_LOCATION, /turf/open) - return - - //we have an item, go drop! - var/list/our_contents = living_pawn.contents - typecache_filter_list(living_pawn.contents, controller.blackboard[BB_IGNORE_ITEMS]) - if(length(our_contents)) - controller.queue_behavior(/datum/ai_behavior/travel_towards/and_drop, BB_HOARD_LOCATION) - return SUBTREE_RETURN_FINISH_PLANNING - - if(controller.blackboard_key_exists(BB_HOARD_ITEM_TARGET)) - controller.queue_behavior(/datum/ai_behavior/basic_melee_attack/interact_once, BB_HOARD_ITEM_TARGET, BB_TARGETING_STRATEGY) - return SUBTREE_RETURN_FINISH_PLANNING - - if(!SPT_PROB(theft_chance, seconds_per_tick)) - return - controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_item, BB_HOARD_ITEM_TARGET) - -/datum/ai_behavior/find_and_set/hoard_location - -/datum/ai_behavior/find_and_set/hoard_location/search_tactic(datum/ai_controller/controller, locate_path, search_range) - for(var/turf/open/candidate in oview(search_range, controller.pawn)) - if(isspaceturf(candidate) || isopenspaceturf(candidate)) - continue - if(candidate.is_blocked_turf(source_atom = controller.pawn)) - continue - return candidate - - return null - -/datum/ai_behavior/find_and_set/hoard_item - action_cooldown = 5 SECONDS - behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/find_and_set/hoard_item/search_tactic(datum/ai_controller/controller, locate_path, search_range) - if(!controller.blackboard_key_exists(BB_HOARD_LOCATION)) - return null - var/turf/nest_turf = controller.blackboard[BB_HOARD_LOCATION] - var/mob/living/living_pawn = controller.pawn - for(var/atom/potential_victim in oview(search_range, controller.pawn)) - if(is_type_in_typecache(potential_victim, controller.blackboard[BB_IGNORE_ITEMS])) - continue - if(potential_victim.loc == nest_turf) - continue - if(isitem(potential_victim)) - var/obj/item/item_steal = potential_victim - if(item_steal.w_class <= WEIGHT_CLASS_SMALL) - return potential_victim - if(!ishuman(potential_victim)) - continue - if(living_pawn.faction.Find(REF(potential_victim))) - continue //dont steal from friends - if(holding_valuable(controller, potential_victim)) - controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, TRUE) - return potential_victim - - return null - -/datum/ai_behavior/find_and_set/hoard_item/proc/holding_valuable(datum/ai_controller/controller, mob/living/human_target) - for(var/obj/item/potential_item in human_target.held_items) - if(is_type_in_typecache(potential_item, controller.blackboard[BB_IGNORE_ITEMS])) - continue - if(potential_item.w_class <= WEIGHT_CLASS_SMALL) - return TRUE - return FALSE - -/datum/ai_behavior/travel_towards/and_drop - -/datum/ai_behavior/travel_towards/and_drop/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - var/mob/living/living_mob = controller.pawn - var/obj/drop_item = locate(/obj/item) in (living_mob.contents - typecache_filter_list(living_mob.contents, controller.blackboard[BB_IGNORE_ITEMS])) - drop_item?.forceMove(get_turf(living_mob)) - -/datum/ai_behavior/basic_melee_attack/interact_once/parrot - -/datum/ai_behavior/basic_melee_attack/interact_once/parrot/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, FALSE) - -///subtree to perch on targets -/datum/ai_planning_subtree/perch_on_target - ///perchance... - var/perch_chance = 5 - ///chance we unbuckle - var/unperch_chance = 15 - - -/datum/ai_planning_subtree/perch_on_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - var/atom/buckled_too = living_pawn.buckled - - //do we have a current target or is chance to unbuckle has passed? then unbuckle! - if(buckled_too) - if((SPT_PROB(unperch_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))) - controller.queue_behavior(/datum/ai_behavior/unbuckle_mob) - return - return SUBTREE_RETURN_FINISH_PLANNING - - //if we are perched, we can go find something else to perch too - var/final_chance = HAS_TRAIT(living_pawn, PARROT_PERCHED) ? unperch_chance : perch_chance - - if(!SPT_PROB(final_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) - return - - if(controller.blackboard_key_exists(BB_PERCH_TARGET)) - controller.queue_behavior(/datum/ai_behavior/perch_on_target, BB_PERCH_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - //50 50 chance to look for an object, or a friend - if(prob(50)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/nearby_friends, BB_PERCH_TARGET) - return - - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_PERCH_TARGET, controller.blackboard[BB_PARROT_PERCH_TYPES]) - - -/datum/ai_behavior/perch_on_target - behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - -/datum/ai_behavior/perch_on_target/setup(datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - return FALSE - - set_movement_target(controller, target) - -/datum/ai_behavior/perch_on_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) - finish_action(controller, FALSE, target_key) - return - - var/mob/living/basic/parrot/living_pawn = controller.pawn - - if(!ishuman(target)) - living_pawn.start_perching(target) - finish_action(controller, TRUE, target_key) - return - - if(!check_human_conditions(target)) - finish_action(controller, FALSE, target_key) - return - - living_pawn.start_perching(target) - finish_action(controller, TRUE, target_key) - -/datum/ai_behavior/perch_on_target/proc/check_human_conditions(mob/living/living_human) - if(living_human.stat == DEAD || LAZYLEN(living_human.buckled_mobs) >= living_human.max_buckled_mobs) - return FALSE - - return TRUE - -/datum/ai_behavior/perch_on_target/finish_action(datum/ai_controller/controller, succeeded, target_key) - . = ..() - controller.clear_blackboard_key(target_key) - -/datum/ai_behavior/unbuckle_mob - -/datum/ai_behavior/unbuckle_mob/perform(seconds_per_tick, datum/ai_controller/controller) - . = ..() - - var/mob/living/living_pawn = controller.pawn - var/atom/movable/buckled_too = living_pawn.buckled - - if(isnull(buckled_too)) - finish_action(controller, FALSE) - return - - buckled_too.unbuckle_mob(living_pawn) - finish_action(controller, TRUE) - - -//ghost poly - -/datum/ai_controller/basic_controller/parrot/ghost - planning_subtrees = list( - /datum/ai_planning_subtree/parrot_as_in_repeat, - /datum/ai_planning_subtree/possess_humans, - /datum/ai_planning_subtree/hoard_items, - ) - -///subtree to possess humans -/datum/ai_planning_subtree/possess_humans - ///chance we go possess humans - var/possess_chance = 80 - -/datum/ai_planning_subtree/possess_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/living_pawn = controller.pawn - - if(controller.blackboard_key_exists(BB_PERCH_TARGET)) - controller.queue_behavior(/datum/ai_behavior/perch_on_target/haunt, BB_PERCH_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING - - - if(!SPT_PROB(possess_chance, seconds_per_tick)) - if(ishuman(living_pawn.loc)) - return SUBTREE_RETURN_FINISH_PLANNING - return - - if(ishuman(living_pawn.loc)) - controller.set_blackboard_key(living_pawn.loc) - return - - controller.queue_behavior(/datum/ai_behavior/find_and_set/conscious_person, BB_PERCH_TARGET) - - -/datum/ai_behavior/perch_on_target/haunt - -/datum/ai_behavior/perch_on_target/haunt/check_human_conditions(mob/living/living_human) - if(living_human.stat == DEAD) - return FALSE - return TRUE diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm new file mode 100644 index 0000000000000..7484cbfe6751c --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_hoarding.dm @@ -0,0 +1,74 @@ +///subtree to steal items +/datum/ai_planning_subtree/hoard_items + var/theft_chance = 5 + +/datum/ai_planning_subtree/hoard_items/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + + var/turf/myspace = controller.blackboard[BB_HOARD_LOCATION] + + if(isnull(myspace) || myspace.is_blocked_turf(source_atom = controller.pawn) || get_dist(myspace, controller.pawn) > controller.blackboard[BB_HOARD_LOCATION_RANGE]) + controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_location, BB_HOARD_LOCATION, /turf/open) + return + + //we have an item, go drop! + var/list/our_contents = living_pawn.contents - typecache_filter_list(living_pawn.contents, controller.blackboard[BB_IGNORE_ITEMS]) + if(length(our_contents)) + controller.queue_behavior(/datum/ai_behavior/travel_towards/and_drop, BB_HOARD_LOCATION) + return SUBTREE_RETURN_FINISH_PLANNING + + if(controller.blackboard_key_exists(BB_HOARD_ITEM_TARGET)) + controller.queue_behavior(/datum/ai_behavior/basic_melee_attack/interact_once, BB_HOARD_ITEM_TARGET, BB_TARGETING_STRATEGY) + return SUBTREE_RETURN_FINISH_PLANNING + + if(!SPT_PROB(theft_chance, seconds_per_tick)) + return + controller.queue_behavior(/datum/ai_behavior/find_and_set/hoard_item, BB_HOARD_ITEM_TARGET) + +/datum/ai_behavior/find_and_set/hoard_location + +/datum/ai_behavior/find_and_set/hoard_location/search_tactic(datum/ai_controller/controller, locate_path, search_range) + for(var/turf/open/candidate in oview(search_range, controller.pawn)) + if(isspaceturf(candidate) || isopenspaceturf(candidate)) + continue + if(candidate.is_blocked_turf(source_atom = controller.pawn)) + continue + return candidate + + return null + +/datum/ai_behavior/find_and_set/hoard_item + action_cooldown = 5 SECONDS + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/find_and_set/hoard_item/search_tactic(datum/ai_controller/controller, locate_path, search_range) + if(!controller.blackboard_key_exists(BB_HOARD_LOCATION)) + return null + var/turf/nest_turf = controller.blackboard[BB_HOARD_LOCATION] + var/mob/living/living_pawn = controller.pawn + for(var/atom/potential_victim in oview(search_range, controller.pawn)) + if(is_type_in_typecache(potential_victim, controller.blackboard[BB_IGNORE_ITEMS])) + continue + if(potential_victim.loc == nest_turf) + continue + if(isitem(potential_victim)) + var/obj/item/item_steal = potential_victim + if(item_steal.w_class <= WEIGHT_CLASS_SMALL) + return potential_victim + if(!ishuman(potential_victim)) + continue + if(living_pawn.faction.Find(REF(potential_victim))) + continue //dont steal from friends + if(holding_valuable(controller, potential_victim)) + controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, TRUE) + return potential_victim + + return null + +/datum/ai_behavior/find_and_set/hoard_item/proc/holding_valuable(datum/ai_controller/controller, mob/living/human_target) + for(var/obj/item/potential_item in human_target.held_items) + if(is_type_in_typecache(potential_item, controller.blackboard[BB_IGNORE_ITEMS])) + continue + if(potential_item.w_class <= WEIGHT_CLASS_SMALL) + return TRUE + return FALSE diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm new file mode 100644 index 0000000000000..1bb5c6fc960a4 --- /dev/null +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_perching.dm @@ -0,0 +1,78 @@ +///subtree to perch on targets +/datum/ai_planning_subtree/perch_on_target + ///perchance... + var/perch_chance = 5 + ///chance we unbuckle + var/unperch_chance = 15 + + +/datum/ai_planning_subtree/perch_on_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + var/atom/buckled_too = living_pawn.buckled + + //do we have a current target or is chance to unbuckle has passed? then unbuckle! + if(buckled_too) + if((SPT_PROB(unperch_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))) + controller.queue_behavior(/datum/ai_behavior/unbuckle_mob) + return + return SUBTREE_RETURN_FINISH_PLANNING + + //if we are perched, we can go find something else to perch too + var/final_chance = HAS_TRAIT(living_pawn, TRAIT_PARROT_PERCHED) ? unperch_chance : perch_chance + + if(!SPT_PROB(final_chance, seconds_per_tick) || controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + return + + if(controller.blackboard_key_exists(BB_PERCH_TARGET)) + controller.queue_behavior(/datum/ai_behavior/perch_on_target, BB_PERCH_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + //50 50 chance to look for an object, or a friend + if(prob(50)) + controller.queue_behavior(/datum/ai_behavior/find_and_set/nearby_friends, BB_PERCH_TARGET) + return + + controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_PERCH_TARGET, controller.blackboard[BB_PARROT_PERCH_TYPES]) + +/// Parrot behavior that allows them to perch on a target. +/datum/ai_behavior/perch_on_target + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/perch_on_target/setup(datum/ai_controller/controller, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + + set_movement_target(controller, target) + +/datum/ai_behavior/perch_on_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + finish_action(controller, FALSE, target_key) + return + + var/mob/living/basic/parrot/living_pawn = controller.pawn + + if(!ishuman(target)) + living_pawn.start_perching(target) + finish_action(controller, TRUE, target_key) + return + + if(!check_human_conditions(target)) + finish_action(controller, FALSE, target_key) + return + + living_pawn.start_perching(target) + finish_action(controller, TRUE, target_key) + +/datum/ai_behavior/perch_on_target/proc/check_human_conditions(mob/living/living_human) + if(living_human.stat == DEAD || LAZYLEN(living_human.buckled_mobs) >= living_human.max_buckled_mobs) + return FALSE + + return TRUE + +/datum/ai_behavior/perch_on_target/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + controller.clear_blackboard_key(target_key) diff --git a/tgstation.dme b/tgstation.dme index cd7b6cbc605e2..1af18b17f35de 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -851,6 +851,7 @@ #include "code\datums\ai\basic_mobs\basic_ai_behaviors\targeting.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\travel_towards.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\unbuckle_mob.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\ventcrawling.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\wounded_targeting.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\write_on_paper.dm" @@ -4564,6 +4565,9 @@ #include "code\modules\mob\living\basic\pets\dog\corgi.dm" #include "code\modules\mob\living\basic\pets\dog\dog_subtypes.dm" #include "code\modules\mob\living\basic\pets\dog\strippable_items.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\ghost_parrot_controller.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_hoarding.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_perching.dm" #include "code\modules\mob\living\basic\ruin_defender\flesh.dm" #include "code\modules\mob\living\basic\ruin_defender\living_floor.dm" #include "code\modules\mob\living\basic\ruin_defender\skeleton.dm" From b1c10b1a8b00796470dedfa359bc11380e4af99d Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:47:28 -0700 Subject: [PATCH 52/69] deletes needless documentation --- code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm index 0469fea35a476..a0e8a4b529455 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_subtypes.dm @@ -3,7 +3,6 @@ /// Parrot that will just randomly spawn with a headset. Nothing too special beyond that. /mob/living/basic/parrot/headsetted -/// Will simply set up the headset for the parrot to use. Stub, implemented on subtypes. /mob/living/basic/parrot/headsetted/setup_headset() var/headset = pick( /obj/item/radio/headset/headset_cargo, From d3fc9fab82875f1ab569fe3285c0fb7f02ea1fce Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:47:47 -0700 Subject: [PATCH 53/69] undefs --- code/modules/mob/living/basic/pets/parrot/poly.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index f6788006b1c54..69b7813264729 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -249,3 +249,5 @@ #undef POLY_LONGEST_SURVIVAL #undef POLY_BEATING_DEATHSTREAK #undef POLY_CONSECUTIVE_ROUND +#undef POLY_POSSESS_FILTER +#undef POLY_POSSESS_GLOW From 53005d9fb0b38fe7e837695ddad7461b0f091e9c Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:48:17 -0700 Subject: [PATCH 54/69] alphabetizes the unit test thing --- code/modules/unit_tests/required_map_items.dm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/modules/unit_tests/required_map_items.dm b/code/modules/unit_tests/required_map_items.dm index 1f03295f00e79..5cbef64539109 100644 --- a/code/modules/unit_tests/required_map_items.dm +++ b/code/modules/unit_tests/required_map_items.dm @@ -15,10 +15,11 @@ /datum/unit_test/required_map_items/proc/setup_expected_types() expected_types += subtypesof(/obj/item/stamp/head) expected_types += subtypesof(/obj/machinery/computer/department_orders) - expected_types += /obj/machinery/computer/communications - expected_types += /mob/living/carbon/human/species/monkey/punpun - expected_types += /mob/living/basic/pet/dog/corgi/ian + expected_types += /mob/living/basic/parrot/poly + expected_types += /mob/living/basic/pet/dog/corgi/ian + expected_types += /mob/living/carbon/human/species/monkey/punpun + expected_types += /obj/machinery/computer/communications expected_types += /obj/machinery/drone_dispenser /datum/unit_test/required_map_items/Run() From 970cf9edc4117a9695c683a57c1c20488b91a2b1 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:48:45 -0700 Subject: [PATCH 55/69] alphabetizes the ridable typecache --- code/modules/mob/living/carbon/human/human_defines.dm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index ba2f545ac097b..9f1517962e96d 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -67,7 +67,11 @@ var/list/datum/bioware/biowares /// What types of mobs are allowed to ride/buckle to this mob - var/static/list/can_ride_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/basic/parrot)) + var/static/list/can_ride_typecache = typecacheof(list( + /mob/living/basic/parrot, + /mob/living/carbon/human, + /mob/living/simple_animal/slime, + )) var/lastpuke = 0 var/account_id From d17b0e6ce778619ab2abf17d84d50844d518efe6 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:49:36 -0700 Subject: [PATCH 56/69] whoops trait broke --- code/__DEFINES/traits.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 8311faf69118e..35395b2a579db 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -205,7 +205,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Tracks whether we're gonna be a baby alien's mummy. #define TRAIT_XENO_HOST "xeno_host" /// This parrot is currently perched -#define TRAIT_TRAIT_PARROT_PERCHED "TRAIT_PARROT_PERCHED" +#define TRAIT_PARROT_PERCHED "parrot_perched" /// This mob is immune to stun causing status effects and stamcrit. /// Prefer to use [/mob/living/proc/check_stun_immunity] over checking for this trait exactly. #define TRAIT_STUNIMMUNE "stun_immunity" From ec7596f0274bbe837c823563df2cf12aba1b81d4 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:49:51 -0700 Subject: [PATCH 57/69] MORE UNDEFS --- code/datums/components/listen_and_repeat.dm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index 093557ec03b82..3d9f03c6cc2c7 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -72,3 +72,6 @@ return controller.set_blackboard_key(BB_EXPORTABLE_STRING_BUFFER_LIST, speech_buffer.Copy()) + +#undef MAX_SPEECH_BUFFER_SIZE +#undef RADIO_IGNORE_CHANCE From 96a554184e5dd299b52521bda41f9f3169df3505 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:50:42 -0700 Subject: [PATCH 58/69] epic DME fail --- tgstation.dme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgstation.dme b/tgstation.dme index 1af18b17f35de..b8ff1d6e25ceb 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4565,6 +4565,7 @@ #include "code\modules\mob\living\basic\pets\dog\corgi.dm" #include "code\modules\mob\living\basic\pets\dog\dog_subtypes.dm" #include "code\modules\mob\living\basic\pets\dog\strippable_items.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\_parrot_controller.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\ghost_parrot_controller.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_hoarding.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_perching.dm" @@ -4575,7 +4576,6 @@ #include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" #include "code\modules\mob\living\basic\pets\parrot\poly.dm" -#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_controller.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" From 956ce9a71dd104c1b84c0c3956f0308d623ac254 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:59:48 -0700 Subject: [PATCH 59/69] should fix TFE? okay fixes the alphabetize DME for real this time --- tgstation.dme | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tgstation.dme b/tgstation.dme index b8ff1d6e25ceb..0f82cbcd21be3 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4565,18 +4565,18 @@ #include "code\modules\mob\living\basic\pets\dog\corgi.dm" #include "code\modules\mob\living\basic\pets\dog\dog_subtypes.dm" #include "code\modules\mob\living\basic\pets\dog\strippable_items.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" +#include "code\modules\mob\living\basic\pets\parrot\poly.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\_parrot_controller.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\ghost_parrot_controller.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_hoarding.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_perching.dm" +#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" #include "code\modules\mob\living\basic\ruin_defender\flesh.dm" #include "code\modules\mob\living\basic\ruin_defender\living_floor.dm" #include "code\modules\mob\living\basic\ruin_defender\skeleton.dm" -#include "code\modules\mob\living\basic\pets\parrot\parrot.dm" -#include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm" -#include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" -#include "code\modules\mob\living\basic\pets\parrot\poly.dm" -#include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm" #include "code\modules\mob\living\basic\ruin_defender\wizard\wizard_ai.dm" From 821b8cfef208a5fee308182fb2f1f7914f818ea6 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 14 Nov 2023 23:51:39 -0700 Subject: [PATCH 60/69] adds updatepaths --- tools/UpdatePaths/Scripts/simple_to_basic_parrots.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tools/UpdatePaths/Scripts/simple_to_basic_parrots.txt diff --git a/tools/UpdatePaths/Scripts/simple_to_basic_parrots.txt b/tools/UpdatePaths/Scripts/simple_to_basic_parrots.txt new file mode 100644 index 0000000000000..194b2d7ace38d --- /dev/null +++ b/tools/UpdatePaths/Scripts/simple_to_basic_parrots.txt @@ -0,0 +1,5 @@ +/mob/living/simple_animal/parrot : /mob/living/basic/parrot{@OLD} +/mob/living/simple_animal/parrot/natural : /mob/living/basic/parrot{@OLD} +/mob/living/simple_animal/parrot/poly : /mob/living/basic/poly{@OLD} + + From 992569090b500a3e0e77ede5fac80849accc5e8b Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 00:10:51 -0700 Subject: [PATCH 61/69] runs updatepaths --- _maps/RandomRuins/SpaceRuins/russian_derelict.dmm | 6 +----- _maps/map_files/Birdshot/birdshot.dmm | 2 +- _maps/map_files/Deltastation/DeltaStation2.dmm | 2 +- _maps/map_files/IceBoxStation/IceBoxStation.dmm | 2 +- _maps/map_files/MetaStation/MetaStation.dmm | 2 +- _maps/map_files/NorthStar/north_star.dmm | 2 +- _maps/map_files/tramstation/tramstation.dmm | 2 +- _maps/shuttles/emergency_hugcage.dmm | 2 +- _maps/shuttles/ruin_pirate_cutter.dmm | 2 +- _maps/virtual_domains/pirates.dmm | 2 +- 10 files changed, 10 insertions(+), 14 deletions(-) diff --git a/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm b/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm index e1671448e3486..45eee5cbe8b46 100644 --- a/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm +++ b/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm @@ -4876,10 +4876,6 @@ /obj/structure/cable, /turf/open/floor/plating/airless, /area/ruin/space/ks13/engineering/singulo) -"IZ" = ( -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/iron/airless, -/area/ruin/space/ks13/engineering/singulo) "Jc" = ( /obj/effect/mapping_helpers/broken_floor, /obj/effect/mapping_helpers/broken_floor, @@ -11718,7 +11714,7 @@ PI Ex pi FI -IZ +FI FI FI zv diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index e6af579a236c8..0b82ba735b3b3 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -16085,7 +16085,7 @@ /obj/structure/chair/office/light{ dir = 4 }, -/mob/living/simple_animal/parrot/poly, +/mob/living/basic/poly, /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index bb4f8c4c16b49..ad7e0886d41dc 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -34717,7 +34717,7 @@ /obj/structure/filingcabinet/chestdrawer, /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/machinery/newscaster/directional/north, -/mob/living/simple_animal/parrot/poly, +/mob/living/basic/poly, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/ce) "iDq" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 2b92201ed4a93..dd6a382b60e29 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -4587,7 +4587,7 @@ "bup" = ( /obj/structure/filingcabinet/chestdrawer, /obj/effect/turf_decal/tile/neutral/fourcorners, -/mob/living/simple_animal/parrot/poly, +/mob/living/basic/poly, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/ce) "buv" = ( diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 234b5fd4ab63b..92923f5026625 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -3127,7 +3127,7 @@ "bdV" = ( /obj/structure/filingcabinet/chestdrawer, /obj/effect/turf_decal/tile/neutral/fourcorners, -/mob/living/simple_animal/parrot/poly, +/mob/living/basic/poly, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/ce) "beo" = ( diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index 3c4c0a57d714b..1b85cd7804d28 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -6814,7 +6814,7 @@ /obj/machinery/computer/security/telescreen/engine{ name = "Engineering and atmospherics monitor" }, -/mob/living/simple_animal/parrot/poly, +/mob/living/basic/poly, /turf/open/floor/catwalk_floor/iron_dark, /area/station/command/heads_quarters/ce) "bIQ" = ( diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 09f7fb52e4cda..e560f7b87c43e 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -47552,7 +47552,7 @@ }, /obj/machinery/power/apc/auto_name/directional/north, /obj/structure/cable, -/mob/living/simple_animal/parrot/poly, +/mob/living/basic/poly, /turf/open/floor/iron, /area/station/command/heads_quarters/ce) "pWt" = ( diff --git a/_maps/shuttles/emergency_hugcage.dmm b/_maps/shuttles/emergency_hugcage.dmm index 80211ed6092ea..fc47476440151 100644 --- a/_maps/shuttles/emergency_hugcage.dmm +++ b/_maps/shuttles/emergency_hugcage.dmm @@ -31,7 +31,7 @@ /turf/open/floor/mineral/titanium/blue, /area/shuttle/escape) "dz" = ( -/mob/living/simple_animal/parrot/natural{ +/mob/living/basic/parrot{ melee_damage_upper = 5 }, /turf/open/floor/mineral/titanium/blue, diff --git a/_maps/shuttles/ruin_pirate_cutter.dmm b/_maps/shuttles/ruin_pirate_cutter.dmm index 7dd4be59ba7ca..77c4e37bc0326 100644 --- a/_maps/shuttles/ruin_pirate_cutter.dmm +++ b/_maps/shuttles/ruin_pirate_cutter.dmm @@ -702,7 +702,7 @@ "Ry" = ( /obj/structure/rack, /obj/item/storage/bag/money/vault, -/mob/living/simple_animal/parrot{ +/mob/living/basic/parrot{ faction = list("pirate"); name = "Pegwing" }, diff --git a/_maps/virtual_domains/pirates.dmm b/_maps/virtual_domains/pirates.dmm index 9868ab8e9b32e..5eddef7828938 100644 --- a/_maps/virtual_domains/pirates.dmm +++ b/_maps/virtual_domains/pirates.dmm @@ -791,7 +791,7 @@ /area/virtual_domain/powered) "Pz" = ( /obj/structure/table/wood, -/mob/living/simple_animal/parrot{ +/mob/living/basic/parrot{ name = "pepper" }, /turf/open/floor/carpet/blue, From c64b7bc0342326e65aa381ec0d7ddcfcca762c2f Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 16:00:39 -0700 Subject: [PATCH 62/69] Kobsa Code Review Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> --- code/__DEFINES/ai/pets.dm | 6 ++++++ code/modules/mob/living/basic/pets/parrot/parrot.dm | 5 ++--- .../basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm | 6 ++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index 3afaedfe0bcc6..e41c9ac0c3ffe 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -38,10 +38,16 @@ #define BB_EXPORTABLE_STRING_BUFFER_LIST "BB_parrot_repeat_string_buffer" /// The types of perches we desire to use #define BB_PARROT_PERCH_TYPES "BB_parrot_perch_types" +/// key that holds our perch target #define BB_PERCH_TARGET "perch_target" +/// key that holds our theft item target #define BB_HOARD_ITEM_TARGET "hoard_item_target" +/// key that holds the mob we will steal from #define BB_THEFT_VICTIM "theft_victim" +/// key that holds the turf we will be hauling stolen items to #define BB_HOARD_LOCATION "hoard_location" +/// key that holds the minimum range we must be from the hoard spot #define BB_HOARD_LOCATION_RANGE "hoard_location_range" +/// key that holds items we arent interested in hoarding #define BB_IGNORE_ITEMS "ignore_items" diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 5433ee51cf6df..7fd6eb696aad7 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -197,7 +197,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if(ishuman(target)) return perch_on_human(target) - if(!is_type_in_list(target, desired_perches)) + if(!is_type_in_typecache(target, desired_perches)) return FALSE forceMove(get_turf(target)) @@ -265,8 +265,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( balloon_alert(src, "too big to pick up!") return FALSE - target.forceMove(src) - held_item = target + pick_up_item(target) visible_message( span_notice("[src] grabs [held_item]!"), span_notice("You grab [held_item]!"), diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm index add30bbd26ff4..58bdef408a5ee 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot_ai/ghost_parrot_controller.dm @@ -9,7 +9,7 @@ ///subtree to possess humans /datum/ai_planning_subtree/possess_humans ///chance we go possess humans - var/possess_chance = 80 + var/possess_chance = 2 /datum/ai_planning_subtree/possess_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/living_pawn = controller.pawn @@ -34,6 +34,4 @@ /datum/ai_behavior/perch_on_target/haunt /datum/ai_behavior/perch_on_target/haunt/check_human_conditions(mob/living/living_human) - if(living_human.stat == DEAD) - return FALSE - return TRUE + return (living_human.stat != DEAD) From 6a6cdef01ce1bfb442897f50585a7b13519e50ac Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 16:01:41 -0700 Subject: [PATCH 63/69] `desired_perches` is typecache --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 7fd6eb696aad7..7c7b162dc32e2 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -58,7 +58,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( var/speech_shuffle_rate = 30 //Parrots will generally sit on their perch unless something catches their eye. - var/static/list/desired_perches = list( + var/static/list/desired_perches = typecacheof(list( /obj/machinery/computer, /obj/machinery/dna_scannernew, /obj/machinery/nuclearbomb, @@ -70,7 +70,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /obj/structure/displaycase, /obj/structure/filingcabinet, /obj/structure/frame/computer, - ) + )) ///items we wont pick up var/static/list/ignore_items = typecacheof(list(/obj/item/radio)) From 89ae9ac66118f5b1a441b7fd670d259771de2979 Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 16:20:41 -0700 Subject: [PATCH 64/69] fixes the DCS issue and centralizes edibles --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 7c7b162dc32e2..e3b556bf83f01 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -74,6 +74,11 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( ///items we wont pick up var/static/list/ignore_items = typecacheof(list(/obj/item/radio)) + /// Food that Poly loves to eat (spoiler alert it's just crackers) + var/static/list/edibles = list( + /obj/item/food/cracker, + ) + /mob/living/basic/parrot/Initialize(mapload) . = ..() @@ -84,11 +89,11 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/strippable, GLOB.strippable_parrot_items) AddElement(/datum/element/simple_flying) - AddElement(/datum/element/basic_eating, food_types = list(/obj/item/food/cracker)) + AddElement(/datum/element/basic_eating, food_types = edibles) AddComponent(/datum/component/listen_and_repeat, desired_phrases = get_static_list_of_phrases(), blackboard_key = BB_PARROT_REPEAT_STRING) AddComponent(\ /datum/component/tameable,\ - food_types = list(/obj/item/food/cracker),\ + food_types = edibles,\ tame_chance = 100,\ bonus_tame_chance = 0,\ after_tame = CALLBACK(src, PROC_REF(tamed)),\ From 9b218ad07b51b11edc6f40d1e0f485af25886853 Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 16:23:27 -0700 Subject: [PATCH 65/69] `CARBON_GENERIC_BODY_ZONES` ended up being useless --- code/__DEFINES/combat.dm | 9 --------- code/modules/mob/living/basic/pets/parrot/parrot.dm | 2 -- 2 files changed, 11 deletions(-) diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 46f32cbcd3577..116b933c6ae22 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -279,15 +279,6 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) -#define CARBON_GENERIC_BODY_ZONES list(\ - BODY_ZONE_HEAD,\ - BODY_ZONE_CHEST,\ - BODY_ZONE_L_ARM,\ - BODY_ZONE_R_ARM,\ - BODY_ZONE_L_LEG,\ - BODY_ZONE_R_LEG,\ -) - #define BODY_ZONE_PRECISE_EYES "eyes" #define BODY_ZONE_PRECISE_MOUTH "mouth" #define BODY_ZONE_PRECISE_GROIN "groin" diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index e3b556bf83f01..952f8d071b325 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -41,8 +41,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( var/icon_sit = "parrot_sit" /// The number of damage we do when we decide to aggro for our lives var/parrot_damage_upper = 10 - /// Potential bodyparts for us to attack - var/parrot_dam_zone = CARBON_GENERIC_BODY_ZONES ///Headset for Poly to yell at engineers :) var/obj/item/radio/headset/ears = null From 6ea5dad28b38e3e8e1152039e4bcd8a0b04e01ed Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 16:23:47 -0700 Subject: [PATCH 66/69] deletes `parrot_damage_upper` --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 952f8d071b325..c159915ad7b7b 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -39,8 +39,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// Icon we use while sitting var/icon_sit = "parrot_sit" - /// The number of damage we do when we decide to aggro for our lives - var/parrot_damage_upper = 10 ///Headset for Poly to yell at engineers :) var/obj/item/radio/headset/ears = null From 804da3c459cb6893f2ff5c6ba8f19f51e0a99398 Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 16:26:20 -0700 Subject: [PATCH 67/69] more parrot code cleaning also adds an examine to see what it's holding --- code/modules/mob/living/basic/pets/parrot/parrot.dm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index c159915ad7b7b..7eecc5794cd73 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -48,12 +48,12 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// The blackboard key we use to store the string we're repeating var/speech_blackboard_key = BB_PARROT_REPEAT_STRING - /// The generic probability odds we have to do a speech-related action // FIXME might need to tone this down + /// The generic probability odds we have to do a speech-related action var/speech_probability_rate = 5 /// The generic probability odds we have to switch out our speech string var/speech_shuffle_rate = 30 - //Parrots will generally sit on their perch unless something catches their eye. + /// Contains all of the perches that parrots will generally sit on until something catches their eye. var/static/list/desired_perches = typecacheof(list( /obj/machinery/computer, /obj/machinery/dna_scannernew, @@ -128,6 +128,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /mob/living/basic/parrot/examine(mob/user) . = ..() + . += "It appears to [isnull(held_item) ? "not be holding anything." : "be holding \a [held_item]."]" + if(stat != DEAD) return From ecde906f57456ba598a38c85d0768c04f2ef882c Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 16:26:47 -0700 Subject: [PATCH 68/69] oh we should rename parrot.dm too huh --- .../mob/living/basic/pets/parrot/{parrot.dm => _parrot.dm} | 0 tgstation.dme | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename code/modules/mob/living/basic/pets/parrot/{parrot.dm => _parrot.dm} (100%) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/_parrot.dm similarity index 100% rename from code/modules/mob/living/basic/pets/parrot/parrot.dm rename to code/modules/mob/living/basic/pets/parrot/_parrot.dm diff --git a/tgstation.dme b/tgstation.dme index 0f82cbcd21be3..cf650de35ab4d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4565,7 +4565,7 @@ #include "code\modules\mob\living\basic\pets\dog\corgi.dm" #include "code\modules\mob\living\basic\pets\dog\dog_subtypes.dm" #include "code\modules\mob\living\basic\pets\dog\strippable_items.dm" -#include "code\modules\mob\living\basic\pets\parrot\parrot.dm" +#include "code\modules\mob\living\basic\pets\parrot\_parrot.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_items.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_subtypes.dm" #include "code\modules\mob\living\basic\pets\parrot\poly.dm" From 81accc4835be4b15d3e3866e55eef8141d588822 Mon Sep 17 00:00:00 2001 From: san7890 Date: Wed, 15 Nov 2023 16:28:47 -0700 Subject: [PATCH 69/69] patches up `Write_Memory()` to be nicer --- code/modules/mob/living/basic/pets/parrot/poly.dm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 69b7813264729..54d9d212918e2 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -152,7 +152,7 @@ /mob/living/basic/parrot/poly/Write_Memory(dead, gibbed) . = ..() if(!. || memory_saved) // if we die, no more memory - return + return FALSE if(!dead && (stat != DEAD)) dead = FALSE @@ -161,7 +161,7 @@ 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)) + if(!!length(exportable_speech_buffer)) file_data["phrases"] = exportable_speech_buffer if(dead) @@ -189,6 +189,7 @@ rustg_file_write(formatted_data, file_path) memory_saved = TRUE + return TRUE /mob/living/basic/parrot/poly/setup_headset() ears = new /obj/item/radio/headset/headset_eng(src)