From 9f7eb9d8d3959af1b66d4e917ca4b00f55d361da Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 4 Sep 2023 23:18:34 -0600 Subject: [PATCH 01/45] 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 4c8e407e8f439..14f36b4879569 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -270,6 +270,15 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define BODY_ZONE_L_LEG "l_leg" #define BODY_ZONE_R_LEG "r_leg" +#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 5e6d7491d9423..909d0aa771817 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 d857a12c6cb56..79454d71f865c 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 a3b7c06dffcf0..e80cb3f77c8d1 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4206,6 +4206,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\dog\_dog.dm" From e6bdf6ea02de7168ada61ccb176bcbaad17f4b36 Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 4 Sep 2023 23:40:44 -0600 Subject: [PATCH 02/45] 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 79454d71f865c..af23dda65019d 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 e80cb3f77c8d1..78efce95be11d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1297,6 +1297,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_killed_tally.dm" @@ -4206,13 +4207,13 @@ #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\dog\_dog.dm" #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\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\space_fauna\ant.dm" #include "code\modules\mob\living\basic\space_fauna\cat_surgeon.dm" From 2c2258b02b06d4e8225fff77fd58ead0d89a9b86 Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 4 Sep 2023 23:49:49 -0600 Subject: [PATCH 03/45] 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 ed17ad42b3ad1..4fa7cae8ad9b6 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -512,7 +512,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 af23dda65019d..5bc1019d64ca5 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 78efce95be11d..4c1f9caad50e6 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4214,6 +4214,9 @@ #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_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\space_fauna\ant.dm" #include "code\modules\mob\living\basic\space_fauna\cat_surgeon.dm" From fe13aaf893a9f1f5175f99f5db1a9cc5955bc9f5 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 00:08:16 -0600 Subject: [PATCH 04/45] 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 e7bfe7a7c60c9..72207398b0806 100644 --- a/code/datums/ai/generic/generic_behaviors.dm +++ b/code/datums/ai/generic/generic_behaviors.dm @@ -297,6 +297,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 4c1f9caad50e6..5fe14b1540099 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4214,9 +4214,9 @@ #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_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\space_fauna\ant.dm" #include "code\modules\mob\living\basic\space_fauna\cat_surgeon.dm" From 83a8eec789c90cd54ad5b1bfb609c871ea9c0cdb Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 00:21:02 -0600 Subject: [PATCH 05/45] 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 1fc158fc8c6bb0215ebe54b13b6d64bfe3236740 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 00:36:55 -0600 Subject: [PATCH 06/45] 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 5bc1019d64ca5..b88efd64ca0eb 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 parrot_interest = search_for_item() From ffc459bcac685b25bb9d8c57399c07fb102ca2b9 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 00:45:23 -0600 Subject: [PATCH 07/45] 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 2e2e237f227fd9a1d41859dc7643d83b059ac1a4 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 01:07:36 -0600 Subject: [PATCH 08/45] 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..36f62dcc30bcd 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/simple_animal/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 b88efd64ca0eb..38a6037d7f693 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -727,26 +727,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) @@ -759,20 +747,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!") . = ..() @@ -798,50 +786,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 89300970c11f64b10aac67e39612be0c0f5afe3c Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 01:07:36 -0600 Subject: [PATCH 09/45] 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 b88efd64ca0eb..38a6037d7f693 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -727,26 +727,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) @@ -759,20 +747,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!") . = ..() @@ -798,50 +786,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 0d65e7d020f1e9702ceeb1a05ff309ff6a605316 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 10:47:50 -0600 Subject: [PATCH 10/45] 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 a188d8ae11dc0..4b0957129ff6f 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 @@ -96,7 +96,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 38a6037d7f693..45debcdad32bb 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -747,20 +747,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 5fe14b1540099..99b5da6b248b8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1002,6 +1002,7 @@ #include "code\datums\components\label.dm" #include "code\datums\components\leash.dm" #include "code\datums\components\light_eater.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" @@ -1297,7 +1298,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_killed_tally.dm" From d507aaae0bcc5e76d2fe4f82e045d4bdf700f350 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:00:02 -0600 Subject: [PATCH 11/45] 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 4b0957129ff6f..3e9f6e061036a 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 45debcdad32bb..86c6c1bd1fe27 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -746,21 +746,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 0c02c236b4ac365bd0d273dae79822c7f84103be Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:12:06 -0600 Subject: [PATCH 12/45] 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 3e9f6e061036a..1bedb4f6a7425 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 86c6c1bd1fe27..4751b4f84038b 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -733,26 +733,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 bd217c694c2c8c5a62981ceaedfb6d77a37a12d3 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:16:15 -0600 Subject: [PATCH 13/45] almost time to make sure the speech works --- .../mob/living/basic/pets/parrot/parrot.dm | 20 ++ .../basic/pets/parrot/parrot_subtypes.dm | 164 +--------------- .../mob/living/basic/pets/parrot/poly.dm | 182 ++++++++++++++++++ .../mob/living/simple_animal/parrot.dm | 43 ----- tgstation.dme | 1 + 5 files changed, 204 insertions(+), 206 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 1bedb4f6a7425..c654f4cfa51ad 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) +// file just for other parrot subtypes that aren't poly /// Parrot that will just randomly spawn with a headset. Nothing too special beyond that. /mob/living/basic/parrot/headsetted 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 4751b4f84038b..c045b458b64b6 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 /* @@ -739,35 +720,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 99b5da6b248b8..6448ba7b382b0 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4216,6 +4216,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\space_fauna\ant.dm" From d7bf1dc6c8381996e8efab501846172d80247c8a Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:20:05 -0600 Subject: [PATCH 14/45] 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 c045b458b64b6..ccbf1f25d98cb 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -715,10 +715,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 155985ad3a0c1..96c85ba744b24 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -649,7 +649,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 cda854b5cdb487292d81c71a170115e76576d089 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:35:24 -0600 Subject: [PATCH 15/45] 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 6448ba7b382b0..3fb3d7fe4dc94 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4217,6 +4217,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\space_fauna\ant.dm" From f65f225d0ffa98784f5bfe32476c4c74ad4d85c8 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:42:23 -0600 Subject: [PATCH 16/45] 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 c5d04b87d7b6c74109e9979cb76920fb2b0500e1 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:48:58 -0600 Subject: [PATCH 17/45] 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 493f308089fcc22c5edb3075c24bbbc3a0ec4530 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 11:58:32 -0600 Subject: [PATCH 18/45] 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 ccbf1f25d98cb..8d46a13932080 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -708,17 +708,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 ea2858dedeb2354ead0184ad038eae63739f3e5f Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 12:07:26 -0600 Subject: [PATCH 19/45] 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 296f9f9127ef43b6851ddf8a022ea890969b192f Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 12:29:16 -0600 Subject: [PATCH 20/45] 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 8d46a13932080..5fcd3ea62c013 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 b8e44de06814a6b06519334511dc4c42fe963215 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 12:59:02 -0600 Subject: [PATCH 21/45] 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 5fcd3ea62c013..6127ece8adf6d 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -567,17 +567,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 @@ -590,21 +579,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 b4630af4196b408931c12ceed5ffcbabd5d80eb7 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 13:03:31 -0600 Subject: [PATCH 22/45] 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 3554f75d2b896d5323524d5618656a349d41280c Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 13:05:30 -0600 Subject: [PATCH 23/45] 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 c362a02c9063bfc0dcbfc71193546f3490e6cec2 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 16:16:55 -0600 Subject: [PATCH 24/45] 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 6127ece8adf6d..0c2b352a01f17 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -494,36 +494,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" @@ -566,18 +536,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 88502de138a82a83908b779ea9fdbbcc56eb2049 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 16:23:46 -0600 Subject: [PATCH 25/45] 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 0c2b352a01f17..6ed1ca8faa501 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -494,36 +494,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 2b97cc517b7a3be9ee37d21af9d4843f336f8098 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 16:55:22 -0600 Subject: [PATCH 26/45] 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 6ed1ca8faa501..0629792dfdd6a 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -526,13 +526,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 d122cdf1f3c2750c264bb077bcfb17338b35f4c0 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 17:01:21 -0600 Subject: [PATCH 27/45] 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 0629792dfdd6a..9d2fab0d8b2f8 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -494,94 +494,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 0018c77ad5a391d9ae27c48c262353e9bb3578b7 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 17:26:18 -0600 Subject: [PATCH 28/45] 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 443febae2a0ea..9fa1cc11b8cd5 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -76,6 +76,16 @@ if(found.len) 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 9d2fab0d8b2f8..85cf1390a030d 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 3fb3d7fe4dc94..934cffb4f13df 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4218,6 +4218,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\space_fauna\ant.dm" From cf9796a3e5e58548a112cbfee9f2a7f7c1084092 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 17:34:31 -0600 Subject: [PATCH 29/45] 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 | 35 ++++++++----------- 5 files changed, 26 insertions(+), 31 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 85cf1390a030d..b03f1ede2e56c 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 @@ -394,14 +387,14 @@ //If the mob we've been chasing/attacking dies or falls into crit, check for loot! if(L.stat) 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 +// 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 +412,7 @@ SSmove_manager.stop_looping(src) parrot_interest = null parrot_perch = null - drop_held_item() + // drop_held_item() parrot_state = PARROT_WANDER return From 53eb1b675e5411fba8d3a18010cda61d3cce5b58 Mon Sep 17 00:00:00 2001 From: san7890 Date: Tue, 5 Sep 2023 17:37:50 -0600 Subject: [PATCH 30/45] 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 6e981fff3ebd2f6318808d9dab7c86cbd3d03d2d Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:03:31 -0600 Subject: [PATCH 31/45] 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 fa4a8108258d5..e61d4f1e6deca 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -78,7 +78,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 388beebac3dcdd1b3f22b7bc35410f0d7b439ed0 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:05:14 -0600 Subject: [PATCH 32/45] 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 c9295f847dd6d..4b47d3b39a60a 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -647,10 +647,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 8e26f0171d48c00cd22d7a29ecd27c3af6bc9882 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:15:09 -0600 Subject: [PATCH 33/45] 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 04413360e88cfed1f7e05bd5e5ce9930df11478e Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:16:39 -0600 Subject: [PATCH 34/45] 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 9c7efc6a5f6992a2b81f5f5b4a5b12f2035d34fa Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:17:08 -0600 Subject: [PATCH 35/45] 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 a10c37803fd6e6e87e15da75c1e3b51bd186b950 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:19:25 -0600 Subject: [PATCH 36/45] 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 b9dbf3e724c71438932302bd90a86cc688748753 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:23:05 -0600 Subject: [PATCH 37/45] 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 a8c4fdb4dca95a1e6027b299599564019d1658c6 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:28:39 -0600 Subject: [PATCH 38/45] 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 66544ebba3cba5f283a7a8fdcf95ff9f2bcfaec1 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:29:13 -0600 Subject: [PATCH 39/45] 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 f3f919f6da4d06fd6027de7ca2f31df0cce53673 Mon Sep 17 00:00:00 2001 From: san7890 Date: Thu, 12 Oct 2023 15:30:36 -0600 Subject: [PATCH 40/45] 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 c4e0af929fbe4df8bb831155478c78565cac2145 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/45] Parrottai (#34) --- .../RandomRuins/AnywhereRuins/golem_ship.dmm | 23 +- .../IceRuins/icemoon_surface_mining_site.dmm | 10 +- 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/shielded.dm | 8 +- code/datums/components/supermatter_crystal.dm | 2 +- code/datums/diseases/parrotpossession.dm | 33 +- code/datums/memory/_memory.dm | 2 +- code/modules/antagonists/cult/cult_items.dm | 11 +- .../pirate/pirate_shuttle_equipment.dm | 4 +- .../traitor/objectives/kill_pet.dm | 2 +- .../modules/capture_the_flag/ctf_equipment.dm | 16 +- code/modules/cargo/packs/livestock.dm | 4 +- code/modules/clothing/suits/_suits.dm | 13 - 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 | 540 ------------------ 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 +- 38 files changed, 476 insertions(+), 757 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/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm index 051aaad259a16..61311b3f036c8 100644 --- a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm +++ b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm @@ -197,9 +197,6 @@ }, /turf/open/floor/wood, /area/ruin/powered/golem_ship) -"mE" = ( -/turf/open/water/jungle, -/area/ruin/powered/golem_ship) "mU" = ( /obj/machinery/power/shuttle_engine/propulsion{ dir = 4 @@ -503,6 +500,10 @@ }, /turf/open/floor/mineral/titanium, /area/ruin/powered/golem_ship) +"Cx" = ( +/obj/machinery/fishing_portal_generator, +/turf/open/floor/mineral/titanium, +/area/ruin/powered/golem_ship) "CI" = ( /obj/effect/mob_spawn/ghost_role/human/golem/adamantine, /obj/structure/sink/directional/west, @@ -980,8 +981,8 @@ wN (10,1,1) = {" wN wN -mE -mE +wN +wN Df Df Df @@ -997,13 +998,13 @@ wN "} (11,1,1) = {" wN -mE -mE +wN +wN Df Df vr QX -sS +Cx sS sS At @@ -1015,8 +1016,8 @@ Df "} (12,1,1) = {" wN -mE -mE +wN +wN Df bO XR @@ -1034,7 +1035,7 @@ Df (13,1,1) = {" wN wN -mE +wN lF bO ir diff --git a/_maps/RandomRuins/IceRuins/icemoon_surface_mining_site.dmm b/_maps/RandomRuins/IceRuins/icemoon_surface_mining_site.dmm index 218900ffdee58..79126e4fdd2e3 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_surface_mining_site.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_surface_mining_site.dmm @@ -180,9 +180,6 @@ }, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) -"Bv" = ( -/turf/open/misc/snow, -/area/icemoon/surface/outdoors/nospawn) "Dd" = ( /turf/closed/mineral/random/snow, /area/icemoon/surface/outdoors/nospawn) @@ -265,9 +262,6 @@ /obj/structure/railing/corner, /turf/open/floor/plating/snowed/smoothed/icemoon, /area/icemoon/surface/outdoors/nospawn) -"PT" = ( -/turf/template_noop, -/area/icemoon/surface/outdoors/nospawn) "Qi" = ( /obj/effect/turf_decal/stripes/corner, /obj/item/flashlight/glowstick/red{ @@ -414,7 +408,7 @@ fl fl "} (4,1,1) = {" -Bv +lH eu jQ vJ @@ -532,7 +526,7 @@ fl (8,1,1) = {" Dd Dd -PT +Dd nT kd yA 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 91a68590c0f0c..b0d34f68c2244 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -162,3 +162,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/shielded.dm b/code/datums/components/shielded.dm index b33f3d986fda3..0265148c8b538 100644 --- a/code/datums/components/shielded.dm +++ b/code/datums/components/shielded.dm @@ -3,6 +3,7 @@ */ /datum/component/shielded + dupe_mode = COMPONENT_DUPE_UNIQUE /// The person currently wearing us var/mob/living/wearer /// How many charges we can have max, and how many we start with @@ -113,7 +114,7 @@ if((slot & ITEM_SLOT_HANDS) && !shield_inhand) lost_wearer(source, user) return - set_wearer(source, user) + set_wearer(user) /// Either we've been dropped or our wearer has been QDEL'd. Either way, they're no longer our problem /datum/component/shielded/proc/lost_wearer(datum/source, mob/user) @@ -125,6 +126,11 @@ wearer = null /datum/component/shielded/proc/set_wearer(mob/user) + if(wearer == user) + return + if(!isnull(wearer)) + CRASH("[type] called set_wearer with [user] but [wearer] was already the wearer!") + wearer = user RegisterSignal(wearer, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays)) RegisterSignal(wearer, COMSIG_QDELETING, PROC_REF(lost_wearer)) 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/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index f5d8bf2fa33c8..dc58fc8d6ae61 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -377,8 +377,15 @@ Striking a noncultist, however, will tear their flesh."} fire = 50 acid = 60 -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/setup_shielding() - AddComponent(/datum/component/shielded, recharge_start_delay = 0 SECONDS, shield_icon_file = 'icons/effects/cult/effects.dmi', shield_icon = "shield-cult", run_hit_callback = CALLBACK(src, PROC_REF(shield_damaged))) +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/Initialize(mapload) + . = ..() + AddComponent( \ + /datum/component/shielded, \ + recharge_start_delay = 0 SECONDS, \ + shield_icon_file = 'icons/effects/cult/effects.dmi', \ + shield_icon = "shield-cult", \ + run_hit_callback = CALLBACK(src, PROC_REF(shield_damaged)), \ + ) /// A proc for callback when the shield breaks, since cult robes are stupid and have different effects /obj/item/clothing/suit/hooded/cultrobes/cult_shield/proc/shield_damaged(mob/living/wearer, attack_text, new_current_charges) 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/capture_the_flag/ctf_equipment.dm b/code/modules/capture_the_flag/ctf_equipment.dm index 798bfa49be922..dccf875a55a95 100644 --- a/code/modules/capture_the_flag/ctf_equipment.dm +++ b/code/modules/capture_the_flag/ctf_equipment.dm @@ -207,12 +207,18 @@ var/lose_multiple_charges = TRUE var/show_charge_as_alpha = TRUE -/obj/item/clothing/suit/armor/vest/ctf/equipped(mob/user, slot) +/obj/item/clothing/suit/armor/vest/ctf/Initialize(mapload) . = ..() - if(!slot || slot & ITEM_SLOT_HANDS) - return - AddComponent(/datum/component/shielded, max_charges = max_charges, recharge_start_delay = recharge_start_delay, charge_increment_delay = charge_increment_delay, \ - charge_recovery = charge_recovery, lose_multiple_charges = lose_multiple_charges, show_charge_as_alpha = show_charge_as_alpha, shield_icon = team_shield_icon) + AddComponent( \ + /datum/component/shielded, \ + max_charges = max_charges, \ + recharge_start_delay = recharge_start_delay, \ + charge_increment_delay = charge_increment_delay, \ + charge_recovery = charge_recovery, \ + lose_multiple_charges = lose_multiple_charges, \ + show_charge_as_alpha = show_charge_as_alpha, \ + shield_icon = team_shield_icon, \ + ) // LIGHT SHIELDED VEST 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/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm index 17ac11f76ece4..ecae343f68506 100644 --- a/code/modules/clothing/suits/_suits.dm +++ b/code/modules/clothing/suits/_suits.dm @@ -17,10 +17,6 @@ var/blood_overlay_type = "suit" limb_integrity = 0 // disabled for most exo-suits -/obj/item/clothing/suit/Initialize(mapload) - . = ..() - setup_shielding() - /obj/item/clothing/suit/worn_overlays(mutable_appearance/standing, isinhands = FALSE) . = ..() if(isinhands) @@ -47,12 +43,3 @@ if(ismob(loc)) var/mob/M = loc M.update_worn_oversuit() - -/** - * Wrapper proc to apply shielding through AddComponent(). - * Called in /obj/item/clothing/Initialize(). - * Override with an AddComponent(/datum/component/shielded, args) call containing the desired shield statistics. - * See /datum/component/shielded documentation for a description of the arguments - **/ -/obj/item/clothing/suit/proc/setup_shielding() - return 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 4f37d19976e83..c7216d060187f 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1436,6 +1436,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, @@ -1446,7 +1447,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 1c56e070cc35a..e941f87880d9d 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -251,7 +251,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 51b42bb003d96..0000000000000 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ /dev/null @@ -1,540 +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 = //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 c692f2e54d4e3..6a7e64bff1ac8 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -96,10 +96,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 3ebc9dc647c7a..aa876a1d5cca7 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1074,8 +1074,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" @@ -4561,7 +4561,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\flesh.dm" #include "code\modules\mob\living\basic\ruin_defender\living_floor.dm" @@ -4811,7 +4810,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 480bb6a2e9175decc09706bb93af88b9b0b59666 Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 13 Nov 2023 00:55:49 -0700 Subject: [PATCH 42/45] 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 51f8075f67978cc5970934d486f76ea588e0af4d Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 13 Nov 2023 00:58:22 -0700 Subject: [PATCH 43/45] 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 3951d39c0d5d08e2aec741c74e9b646782739e47 Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 13 Nov 2023 00:59:49 -0700 Subject: [PATCH 44/45] 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 f68dd72d44465e94ae69ae6536b1147e2a35c8ac Mon Sep 17 00:00:00 2001 From: san7890 Date: Mon, 13 Nov 2023 01:06:53 -0700 Subject: [PATCH 45/45] 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)