From 5e93652d22d36e5813405f79028ed506404e38e3 Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Fri, 10 Nov 2023 02:27:01 +0200 Subject: [PATCH 1/7] parrots --- code/__DEFINES/ai/pets.dm | 2 +- .../signals_atom/signals_atom_movable.dm | 2 + code/datums/components/listen_and_repeat.dm | 27 +++----- .../mob/living/basic/pets/parrot/parrot.dm | 64 ++++++++++--------- .../parrot/parrot_ai/parrot_controller.dm | 6 +- .../pets/parrot/parrot_ai/parroting_action.dm | 15 +++-- .../mob/living/carbon/human/human_defines.dm | 2 +- code/modules/mob/living/living_say.dm | 1 + 8 files changed, 60 insertions(+), 59 deletions(-) diff --git a/code/__DEFINES/ai/pets.dm b/code/__DEFINES/ai/pets.dm index 20f62d3023e4e..d054918dee369 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -34,7 +34,7 @@ #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" 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..ae0c411a68c3f 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,8 @@ #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" ///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/datums/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index 4247a47eef7df..1417da495a6b9 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -17,31 +17,27 @@ /// 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 - + var/hello = desired_phrases 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 @@ -62,22 +58,15 @@ SIGNAL_HANDLER var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller + var/hello = speech_buffer + var/shmawg = LAZYLEN(speech_buffer) 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/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index c51488d8fd6c1..9c9bc98c8bd72 100644 --- a/code/modules/mob/living/basic/pets/parrot/parrot.dm +++ b/code/modules/mob/living/basic/pets/parrot/parrot.dm @@ -66,16 +66,16 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// The blackboard key we use to store the string we're repeating var/speech_blackboard_key = BB_PARROT_REPEAT_STRING /// The generic probability odds we have to do a speech-related action // FIXME might need to tone this down - 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 + var/speech_shuffle_rate = 30 ////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. - var/static/list/desired_perches = list( + var/static/list/desired_perches = typecacheof(list( /obj/machinery/computer, /obj/machinery/dna_scannernew, /obj/machinery/nuclearbomb, @@ -87,7 +87,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /obj/structure/displaycase, /obj/structure/filingcabinet, /obj/structure/frame/computer, - ) + )) /mob/living/basic/parrot/Initialize(mapload) @@ -96,14 +96,13 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( 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) + 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) 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,19 +182,17 @@ 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)) @@ -212,22 +209,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_typecache(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,20 +236,25 @@ 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. @@ -381,10 +387,10 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /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. 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..a2494225e3766 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 @@ -8,9 +8,11 @@ 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/perch, /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/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/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_say.dm b/code/modules/mob/living/living_say.dm index 1c56e070cc35a..e01222ddcb1f8 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -251,6 +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) + SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_HEAR, args) if(!GET_CLIENT(src)) return FALSE From b17fe85fbaf5f63d778489cdd8aa4c95bbd86e0a Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Sun, 12 Nov 2023 04:15:44 +0200 Subject: [PATCH 2/7] gruuytyr --- code/__DEFINES/ai/pets.dm | 15 +- .../basic_ai_behaviors/basic_attacking.dm | 11 + code/datums/ai/generic/find_and_set.dm | 13 ++ .../mob/living/basic/pets/parrot/parrot.dm | 76 +++---- .../parrot/parrot_ai/parrot_controller.dm | 188 +++++++++++++++++- .../pets/parrot/parrot_ai/parrot_search.dm | 18 -- .../mob/living/basic/pets/parrot/poly.dm | 40 ++++ tgstation.dme | 3 +- 8 files changed, 280 insertions(+), 84 deletions(-) delete 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 d054918dee369..3afaedfe0bcc6 100644 --- a/code/__DEFINES/ai/pets.dm +++ b/code/__DEFINES/ai/pets.dm @@ -36,13 +36,12 @@ #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 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/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm index 93f20babf365f..15ddc7b264d73 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/modules/mob/living/basic/pets/parrot/parrot.dm b/code/modules/mob/living/basic/pets/parrot/parrot.dm index 9c9bc98c8bd72..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,11 +50,6 @@ 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 @@ -70,12 +57,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// The generic probability odds we have to switch out our speech string var/speech_shuffle_rate = 30 - ////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. - var/static/list/desired_perches = typecacheof(list( + var/static/list/desired_perches = list( /obj/machinery/computer, /obj/machinery/dna_scannernew, /obj/machinery/nuclearbomb, @@ -87,7 +70,9 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /obj/structure/displaycase, /obj/structure/filingcabinet, /obj/structure/frame/computer, - )) + ) + ///items we wont pick up + var/static/list/ignore_items = typecacheof(list(/obj/item/radio)) /mob/living/basic/parrot/Initialize(mapload) @@ -95,11 +80,14 @@ 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) - + 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)) @@ -194,11 +182,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( SIGNAL_HANDLER 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. @@ -211,7 +195,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( if(ishuman(target)) return perch_on_human(target) - if(!is_type_in_typecache(target, desired_perches)) + if(!is_type_in_list(target, desired_perches)) return FALSE forceMove(get_turf(target)) @@ -260,18 +244,14 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /// 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) @@ -283,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( @@ -353,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)) @@ -369,21 +336,23 @@ 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 @@ -424,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 a2494225e3766..b285d41383ce0 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,18 +1,198 @@ /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/perch, /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/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) + + + +/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 ..() + +/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 + + var/mob/living/living_human = target + if(living_human.stat == DEAD || LAZYLEN(living_human.buckled_mobs) >= living_human.max_buckled_mobs) + finish_action(controller, FALSE, target_key) + return + + living_pawn.start_perching(living_human) + finish_action(controller, TRUE, target_key) + +/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) 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/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm index 8b98dfa9783f0..770e8df5d5033 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 @@ -203,8 +207,44 @@ // 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" = 28)) + 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.parrot = src + possessed_human.ForceContractDisease(on_possession, make_copy = FALSE, del_on_fail = TRUE) + return + #undef POLY_DEFAULT #undef POLY_LONGEST_SURVIVAL #undef POLY_BEATING_DEATHSTREAK diff --git a/tgstation.dme b/tgstation.dme index cc733521b8717..b33e917f2737e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1069,8 +1069,8 @@ #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\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" @@ -4538,7 +4538,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" From 3a344cba05678309aa35f6bbc246cbad2c1dc1d3 Mon Sep 17 00:00:00 2001 From: distributivgesetz Date: Mon, 13 Nov 2023 03:21:35 +0100 Subject: [PATCH 3/7] Fix some roundstart active turfs (#79666) ## About The Pull Request Wrong turf type and a missing turf on surface mining site, as well as some jungle turfs on the golem ship which causes atmos differences. ## Why It's Good For The Game Fixes #79624 better init time ## Changelog :cl: fix: Removes some roundstart active turfs. /:cl: --- .../RandomRuins/AnywhereRuins/golem_ship.dmm | 23 ++++++++++--------- .../IceRuins/icemoon_surface_mining_site.dmm | 10 ++------ 2 files changed, 14 insertions(+), 19 deletions(-) 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 From 47ce60824190909ce8e7730feb4fd179de5c7832 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:24:57 +1300 Subject: [PATCH 4/7] Automatic changelog for PR #79666 [ci skip] --- html/changelogs/AutoChangeLog-pr-79666.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-79666.yml 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 From 27f51556b58f24edfdf7ee5a833b39aa39087d66 Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Mon, 13 Nov 2023 05:37:29 +0200 Subject: [PATCH 5/7] , --- .../signals_atom/signals_atom_movable.dm | 2 + code/_globalvars/phobias.dm | 2 +- .../subsystem/persistence/_persistence.dm | 5 +- code/datums/components/listen_and_repeat.dm | 3 - code/datums/components/supermatter_crystal.dm | 2 +- code/datums/diseases/parrotpossession.dm | 33 +- code/datums/memory/_memory.dm | 2 +- .../pirate/pirate_shuttle_equipment.dm | 4 +- .../traitor/objectives/kill_pet.dm | 2 +- code/modules/cargo/packs/livestock.dm | 4 +- code/modules/events/ghost_role/sentience.dm | 2 +- code/modules/events/holiday/halloween.dm | 6 +- code/modules/events/wizard/petsplosion.dm | 2 +- .../parrot/parrot_ai/parrot_controller.dm | 75 ++- .../living/basic/pets/parrot/parrot_items.dm | 9 +- .../mob/living/basic/pets/parrot/poly.dm | 8 +- code/modules/mob/living/living.dm | 2 +- code/modules/mob/living/living_say.dm | 3 +- .../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 - tgstation.dme | 1 - 23 files changed, 114 insertions(+), 601 deletions(-) delete mode 100644 code/modules/mob/living/simple_animal/parrot.dm 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 ae0c411a68c3f..ed0e09f433137 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -49,6 +49,8 @@ #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 a00ff5f348331..9bff2f3384ea4 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/components/listen_and_repeat.dm b/code/datums/components/listen_and_repeat.dm index 1417da495a6b9..0b816f722f27e 100644 --- a/code/datums/components/listen_and_repeat.dm +++ b/code/datums/components/listen_and_repeat.dm @@ -22,7 +22,6 @@ if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE - var/hello = desired_phrases if(!isnull(desired_phrases)) LAZYADD(speech_buffer, desired_phrases) @@ -58,8 +57,6 @@ SIGNAL_HANDLER var/atom/movable/atom_source = source var/datum/ai_controller/controller = atom_source.ai_controller - var/hello = speech_buffer - var/shmawg = LAZYLEN(speech_buffer) if(!LAZYLEN(speech_buffer)) controller.clear_blackboard_key(blackboard_key) return NO_NEW_PHRASE_AVAILABLE diff --git a/code/datums/components/supermatter_crystal.dm b/code/datums/components/supermatter_crystal.dm index 3c37bba33cb8d..14a0122dcf5cf 100644 --- a/code/datums/components/supermatter_crystal.dm +++ b/code/datums/components/supermatter_crystal.dm @@ -300,7 +300,7 @@ message_admins("[atom_source] has consumed [key_name_admin(consumed_mob)] [ADMIN_JMP(atom_source)].") atom_source.investigate_log("has consumed [key_name(consumed_mob)].", INVESTIGATE_ENGINE) consumed_mob.investigate_log("has been dusted by [atom_source].", INVESTIGATE_DEATHS) - if(istype(consumed_mob, /mob/living/simple_animal/parrot/poly)) // Dusting Poly creates a power surge + if(istype(consumed_mob, /mob/living/basic/parrot/poly)) // Dusting Poly creates a power surge force_event(/datum/round_event_control/supermatter_surge/poly, "Poly's revenge") notify_ghosts( "[consumed_mob] has been dusted by [atom_source]!", diff --git a/code/datums/diseases/parrotpossession.dm b/code/datums/diseases/parrotpossession.dm index 23f68e1a42ff6..c2828fc6bb22f 100644 --- a/code/datums/diseases/parrotpossession.dm +++ b/code/datums/diseases/parrotpossession.dm @@ -13,24 +13,35 @@ severity = DISEASE_SEVERITY_MEDIUM infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC|MOB_MINERAL bypasses_immunity = TRUE //2spook - var/mob/living/simple_animal/parrot/poly/ghost/parrot + ///chance we speak + var/speak_chance = 5 + ///list of words we can use + var/list/speech_list = null /datum/disease/parrot_possession/stage_act(seconds_per_tick, times_fired) . = ..() + if(!.) return - if(QDELETED(parrot) || parrot.loc != affected_mob) - cure() - return FALSE - - if(length(parrot.speech_buffer) && SPT_PROB(parrot.speak_chance, seconds_per_tick)) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach - affected_mob.say(pick(parrot.speech_buffer), forced = "parrot possession") + if(LAZYLEN(speech_list) && SPT_PROB(speak_chance, seconds_per_tick)) // I'm not going to dive into polycode trying to adjust that probability. Enjoy doubled ghost parrot speach + affected_mob.say(pick(speech_list), forced = "parrot possession") /datum/disease/parrot_possession/cure() - if(parrot && parrot.loc == affected_mob) - parrot.forceMove(affected_mob.drop_location()) - affected_mob.visible_message(span_danger("[parrot] is violently driven out of [affected_mob]!"), span_userdanger("[parrot] bursts out of your chest!")) - ..() + var/atom/movable/inside_parrot = locate(/mob/living/basic/parrot/poly/ghost) in affected_mob + if(inside_parrot) + UnregisterSignal(inside_parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED)) + inside_parrot.forceMove(affected_mob.drop_location()) + affected_mob.visible_message(span_danger("[inside_parrot] is violently driven out of [affected_mob]!"), span_userdanger("[inside_parrot] bursts out of your chest!")) + return ..() + +/datum/disease/parrot_possession/proc/set_parrot(mob/living/parrot) + speech_list = parrot.ai_controller?.blackboard[BB_EXPORTABLE_STRING_BUFFER_LIST] + RegisterSignals(parrot, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED), PROC_REF(on_parrot_exit)) + +/datum/disease/parrot_possession/proc/on_parrot_exit(datum/source) + SIGNAL_HANDLER + UnregisterSignal(source, list(COMSIG_PREQDELETED, COMSIG_MOVABLE_MOVED)) + cure() diff --git a/code/datums/memory/_memory.dm b/code/datums/memory/_memory.dm index 2b3e250a3fbf2..5afde7843ea30 100644 --- a/code/datums/memory/_memory.dm +++ b/code/datums/memory/_memory.dm @@ -276,7 +276,7 @@ /mob/living/basic/stickman, /mob/living/basic/stickman/dog, /mob/living/simple_animal/hostile/megafauna/dragon/lesser, - /mob/living/simple_animal/parrot, + /mob/living/basic/parrot, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/cat/cak, /obj/item/food/sausage/american, diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm index 3ea6488b2d42d..7521ff155496e 100644 --- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm +++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm @@ -404,10 +404,10 @@ /datum/export/pirate/parrot cost = 2000 unit_name = "alive parrot" - export_types = list(/mob/living/simple_animal/parrot) + export_types = list(/mob/living/basic/parrot) /datum/export/pirate/parrot/find_loot() - for(var/mob/living/simple_animal/parrot/current_parrot in GLOB.alive_mob_list) + for(var/mob/living/basic/parrot/current_parrot in GLOB.alive_mob_list) var/turf/parrot_turf = get_turf(current_parrot) if(parrot_turf && is_station_level(parrot_turf.z)) return current_parrot diff --git a/code/modules/antagonists/traitor/objectives/kill_pet.dm b/code/modules/antagonists/traitor/objectives/kill_pet.dm index 8ea89cb44d033..01ab042f11b03 100644 --- a/code/modules/antagonists/traitor/objectives/kill_pet.dm +++ b/code/modules/antagonists/traitor/objectives/kill_pet.dm @@ -24,7 +24,7 @@ ), JOB_CAPTAIN = /mob/living/basic/pet/fox/renault, JOB_CHIEF_MEDICAL_OFFICER = /mob/living/simple_animal/pet/cat/runtime, - JOB_CHIEF_ENGINEER = /mob/living/simple_animal/parrot/poly, + JOB_CHIEF_ENGINEER = /mob/living/basic/parrot/poly, JOB_QUARTERMASTER = list( /mob/living/basic/gorilla/cargorilla, /mob/living/basic/sloth/citrus, diff --git a/code/modules/cargo/packs/livestock.dm b/code/modules/cargo/packs/livestock.dm index ef9fb96182302..942b1414cf9f5 100644 --- a/code/modules/cargo/packs/livestock.dm +++ b/code/modules/cargo/packs/livestock.dm @@ -6,13 +6,13 @@ name = "Bird Crate" desc = "Contains five expert telecommunication birds." cost = CARGO_CRATE_VALUE * 8 - contains = list(/mob/living/simple_animal/parrot) + contains = list(/mob/living/basic/parrot) crate_name = "parrot crate" /datum/supply_pack/critter/parrot/generate() . = ..() for(var/i in 1 to 4) - new /mob/living/simple_animal/parrot(.) + new /mob/living/basic/parrot(.) /datum/supply_pack/critter/butterfly name = "Butterflies Crate" diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm index abc57d33a0758..f33333ddb1bd0 100644 --- a/code/modules/events/ghost_role/sentience.dm +++ b/code/modules/events/ghost_role/sentience.dm @@ -7,6 +7,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /mob/living/basic/goat, /mob/living/basic/lizard, /mob/living/basic/mouse/brown/tom, + /mob/living/basic/parrot, /mob/living/basic/pet, /mob/living/basic/pig, /mob/living/basic/rabbit, @@ -16,7 +17,6 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /mob/living/basic/spider/giant/sgt_araneus, /mob/living/simple_animal/bot/secbot/beepsky, /mob/living/simple_animal/hostile/retaliate/goose/vomit, - /mob/living/simple_animal/parrot, /mob/living/simple_animal/pet, ))) diff --git a/code/modules/events/holiday/halloween.dm b/code/modules/events/holiday/halloween.dm index d52dd1b9c508a..8eb29fa706c45 100644 --- a/code/modules/events/holiday/halloween.dm +++ b/code/modules/events/holiday/halloween.dm @@ -18,9 +18,9 @@ for(var/mob/living/basic/pet/dog/corgi/ian/Ian in GLOB.mob_living_list) Ian.place_on_head(new /obj/item/bedsheet(Ian)) - for(var/mob/living/simple_animal/parrot/poly/Poly in GLOB.mob_living_list) - new /mob/living/simple_animal/parrot/poly/ghost(Poly.loc) - qdel(Poly) + for(var/mob/living/basic/parrot/poly/bird in GLOB.mob_living_list) + new /mob/living/basic/parrot/poly/ghost(bird.loc) + qdel(bird) /datum/round_event/spooky/announce(fake) priority_announce(pick("RATTLE ME BONES!","THE RIDE NEVER ENDS!", "A SKELETON POPS OUT!", "SPOOKY SCARY SKELETONS!", "CREWMEMBERS BEWARE, YOU'RE IN FOR A SCARE!") , "THE CALL IS COMING FROM INSIDE THE HOUSE") diff --git a/code/modules/events/wizard/petsplosion.dm b/code/modules/events/wizard/petsplosion.dm index e670fa910a1f0..9de7fffcc3aea 100644 --- a/code/modules/events/wizard/petsplosion.dm +++ b/code/modules/events/wizard/petsplosion.dm @@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(petsplosion_candidates, typecacheof(list( /mob/living/basic/lizard, /mob/living/basic/mothroach, /mob/living/basic/mouse/brown/tom, + /mob/living/basic/parrot, /mob/living/basic/pet, /mob/living/basic/pig, /mob/living/basic/rabbit, @@ -17,7 +18,6 @@ GLOBAL_LIST_INIT(petsplosion_candidates, typecacheof(list( /mob/living/basic/snake, /mob/living/basic/spider/giant/sgt_araneus, /mob/living/simple_animal/hostile/retaliate/goose/vomit, - /mob/living/simple_animal/parrot, /mob/living/simple_animal/pet, ))) diff --git a/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm b/code/modules/mob/living/basic/pets/parrot/parrot_ai/parrot_controller.dm index b285d41383ce0..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 @@ -16,6 +16,16 @@ /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 @@ -98,17 +108,7 @@ . = ..() controller.set_blackboard_key(BB_ALWAYS_IGNORE_FACTION, FALSE) - - -/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 perch on targets /datum/ai_planning_subtree/perch_on_target ///perchance... var/perch_chance = 5 @@ -170,14 +170,19 @@ finish_action(controller, TRUE, target_key) return - var/mob/living/living_human = target - if(living_human.stat == DEAD || LAZYLEN(living_human.buckled_mobs) >= living_human.max_buckled_mobs) + if(!check_human_conditions(target)) finish_action(controller, FALSE, target_key) return - living_pawn.start_perching(living_human) + 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) @@ -196,3 +201,45 @@ 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_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 770e8df5d5033..f6788006b1c54 100644 --- a/code/modules/mob/living/basic/pets/parrot/poly.dm +++ b/code/modules/mob/living/basic/pets/parrot/poly.dm @@ -197,11 +197,12 @@ 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 @@ -233,7 +234,7 @@ return var/mob/living/possessed_human = loc - possessed_human.add_filter(POLY_POSSESS_FILTER, 2, list("type" = "outline", "color" = POLY_POSSESS_GLOW, "size" = 28)) + 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) @@ -241,9 +242,8 @@ animate(alpha = 60, time = 2 SECONDS) var/datum/disease/parrot_possession/on_possession = new /datum/disease/parrot_possession - on_possession.parrot = src + on_possession.set_parrot(src) possessed_human.ForceContractDisease(on_possession, make_copy = FALSE, del_on_fail = TRUE) - return #undef POLY_DEFAULT #undef POLY_LONGEST_SURVIVAL diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index fd947e10f04df..61f9024915f29 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1433,6 +1433,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, @@ -1443,7 +1444,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 e01222ddcb1f8..e941f87880d9d 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -251,8 +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) - SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_HEAR, args) - 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 d5e1e773c9256..a84462dd115f8 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -108,10 +108,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/tgstation.dme b/tgstation.dme index b33e917f2737e..0455734dba718 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4787,7 +4787,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 08cbf579fed1409044dc28040f11349574b372b3 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Mon, 13 Nov 2023 00:42:16 -0600 Subject: [PATCH 6/7] Fixes some shielded component jank (#79674) ## About The Pull Request If you attempted to use the shielded component properly (applying it in `Init`), it would not work because the equipped signal was improperly passing its arguments to `set_wearer`. The only reason why this worked now is that every consumer added the component after it was `equipped`... usually in `equipped`. This also meant shielded items that added it in equipped were open to an exploit, allowing you to reset the charges by unequip / re-equip. ## Changelog :cl: Melbert fix: Fixes some potential exploits and issues involving shielded equipment. /:cl: --- code/datums/components/shielded.dm | 8 +++++++- code/modules/antagonists/cult/cult_items.dm | 11 +++++++++-- code/modules/capture_the_flag/ctf_equipment.dm | 16 +++++++++++----- code/modules/clothing/suits/_suits.dm | 13 ------------- 4 files changed, 27 insertions(+), 21 deletions(-) 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/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/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/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 From 10deb0f8cb3de5829d4682740d5c2e07e7cf58b6 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Mon, 13 Nov 2023 19:42:35 +1300 Subject: [PATCH 7/7] Automatic changelog for PR #79674 [ci skip] --- html/changelogs/AutoChangeLog-pr-79674.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-79674.yml 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