Skip to content

Commit

Permalink
Merge pull request #4 from san7890/parrot-rework
Browse files Browse the repository at this point in the history
Parrot rework
  • Loading branch information
Ben10Omintrix authored Nov 14, 2023
2 parents 3dc9db6 + f68dd72 commit 482b53d
Show file tree
Hide file tree
Showing 35 changed files with 1,227 additions and 1,111 deletions.
18 changes: 18 additions & 0 deletions code/__DEFINES/ai/pets.dm
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,21 @@
#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_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 COMPONENT IN MOST CASES
#define BB_EXPORTABLE_STRING_BUFFER_LIST "BB_parrot_repeat_string_buffer"
/// The types of perches we desire to use
#define BB_PARROT_PERCH_TYPES "BB_parrot_perch_types"
#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"

9 changes: 9 additions & 0 deletions code/__DEFINES/combat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,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,\
)

GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))

#define BODY_ZONE_PRECISE_EYES "eyes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,17 @@
#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 - (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
Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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]"

4 changes: 4 additions & 0 deletions code/__DEFINES/dcs/signals/signals_mob/signals_mob_basic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@

///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_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
2 changes: 1 addition & 1 deletion code/_globalvars/phobias.dm
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ GLOBAL_LIST_INIT(phobia_mobs, list(
"birds" = typecacheof(list(
/mob/living/basic/chick,
/mob/living/basic/chicken,
/mob/living/basic/parrot,
/mob/living/basic/pet/penguin,
/mob/living/simple_animal/parrot,
)),
"conspiracies" = typecacheof(list(
/mob/living/basic/drone,
Expand Down
5 changes: 3 additions & 2 deletions code/controllers/subsystem/persistence/_persistence.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
11 changes: 11 additions & 0 deletions code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
Original file line number Diff line number Diff line change
@@ -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)
. = ..()
Expand Down Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions code/datums/ai/generic/find_and_set.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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/turf_location/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
*/
Expand Down Expand Up @@ -152,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
2 changes: 2 additions & 0 deletions code/datums/ai/generic/generic_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@
/datum/ai_behavior/perform_speech

/datum/ai_behavior/perform_speech/perform(seconds_per_tick, datum/ai_controller/controller, speech, speech_sound)
. = ..()

var/mob/living/living_pawn = controller.pawn
if(!istype(living_pawn))
return
Expand Down
80 changes: 80 additions & 0 deletions code/datums/components/listen_and_repeat.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/// 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.
/// Requires a valid AI Blackboard.
/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.
var/blackboard_key = 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)
. = ..()
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE

if(!isnull(desired_phrases))
LAZYADD(speech_buffer, desired_phrases)

src.blackboard_key = blackboard_key

RegisterSignal(parent, COMSIG_MOVABLE_PRE_HEAR, PROC_REF(on_hear))
RegisterSignal(parent, COMSIG_NEEDS_NEW_PHRASE, PROC_REF(set_new_blackboard_phrase))
RegisterSignal(parent, COMSIG_LIVING_WRITE_MEMORY, PROC_REF(on_write_memory))
RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(on_login))

/// Called if a client logs in- don't want to be forced speaking while under their control (sadly)
/datum/component/listen_and_repeat/proc/on_login(datum/source)
SIGNAL_HANDLER
qdel(src)

/// Called when we hear something.
/datum/component/listen_and_repeat/proc/on_hear(datum/source, list/passed_arguments)
SIGNAL_HANDLER

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
return

if(over_radio && prob(RADIO_IGNORE_CHANCE))
return

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/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.clear_blackboard_key(blackboard_key)
return NO_NEW_PHRASE_AVAILABLE

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/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

controller.set_blackboard_key(BB_EXPORTABLE_STRING_BUFFER_LIST, speech_buffer.Copy())
2 changes: 1 addition & 1 deletion code/datums/components/supermatter_crystal.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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]!",
Expand Down
33 changes: 22 additions & 11 deletions code/datums/diseases/parrotpossession.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()
2 changes: 1 addition & 1 deletion code/datums/memory/_memory.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -276,7 +277,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,
/obj/item/food/sausage/american,
Expand Down
4 changes: 2 additions & 2 deletions code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/traitor/objectives/kill_pet.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions code/modules/cargo/packs/livestock.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion code/modules/events/ghost_role/sentience.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
)))

Expand Down
6 changes: 3 additions & 3 deletions code/modules/events/holiday/halloween.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading

0 comments on commit 482b53d

Please sign in to comment.