From 6676702008b9b8af4802b87ed844f39a0f68433c Mon Sep 17 00:00:00 2001 From: san7890 Date: Fri, 2 Jun 2023 20:40:55 -0600 Subject: [PATCH] Refactors chicks into basic mobs (#75663) ## About The Pull Request On the tin. They have pretty much nothing in common with chickens, so no subtyping. They are in the same folder to keep that whole thing tidy, though. Also includes fixes to `growth_and_differentiation` element that I made for spiderlings, since some stuff was yorked without me realizing. It pretty much worked flawlessly for these chicks otherwise though. It all works fine now. ## Why It's Good For The Game More verbose naming scheme (instead of "holo", we get "permanent" chicks), smarter AI for chicks, knocks them off the list, etc. etc. One thing that I wanted to do was to have chicks recognize their mother (if they had one), but that would be way out of scope for this simple port PR. I'll dwell on adding something cool for that in the future. ## Changelog :cl: refactor: Chicks are now a bit smarter, be careful not to squish them! /:cl: Let me know if the whole "COMPONENT_KILL" thing is cringe, I couldn't figure out a better way to do it without abusing `GetComponent()` to `qdel()` it that way. --- _maps/map_files/Birdshot/birdshot.dmm | 2 +- code/__DEFINES/dcs/signals/signals_datum.dm | 1 + code/_globalvars/phobias.dm | 2 +- .../basic_subtrees/speech_subtree.dm | 6 ++ .../components/growth_and_differentiation.dm | 36 ++++++++--- code/datums/memory/_memory.dm | 2 +- code/game/objects/items/food/egg.dm | 6 +- code/modules/cargo/packs/livestock.dm | 2 +- code/modules/holodeck/holo_effect.dm | 2 +- .../basic/farm_animals/chicken/chick.dm | 64 +++++++++++++++++++ .../farm_animals/{ => chicken}/chicken.dm | 5 +- code/modules/mob/living/living.dm | 2 +- .../simple_animal/friendly/farm_animals.dm | 60 ----------------- .../unit_tests/simple_animal_freeze.dm | 2 - tgstation.dme | 3 +- .../75592_simple_to_basic_chickens.txt | 4 ++ 16 files changed, 114 insertions(+), 85 deletions(-) create mode 100644 code/modules/mob/living/basic/farm_animals/chicken/chick.dm rename code/modules/mob/living/basic/farm_animals/{ => chicken}/chicken.dm (98%) diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index cc14178268e..942a3a60034 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -52156,7 +52156,7 @@ }, /obj/structure/window/spawner/directional/east, /obj/structure/window/spawner/directional/north, -/mob/living/simple_animal/chick{ +/mob/living/basic/chick/permanent{ name = "Morgan" }, /turf/open/floor/grass, diff --git a/code/__DEFINES/dcs/signals/signals_datum.dm b/code/__DEFINES/dcs/signals/signals_datum.dm index 98750ef5e7b..9e625a5f3b7 100644 --- a/code/__DEFINES/dcs/signals/signals_datum.dm +++ b/code/__DEFINES/dcs/signals/signals_datum.dm @@ -7,6 +7,7 @@ #define COMSIG_COMPONENT_ADDED "component_added" /// before a component is removed from a datum because of ClearFromParent: (/datum/component) #define COMSIG_COMPONENT_REMOVING "component_removing" + /// before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation /// you should only be using this if you want to block deletion /// that's the only functional difference between it and COMSIG_PARENT_QDELETING, outside setting QDELETING to detect diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm index 618caf1ccf2..d9adb3c8bff 100644 --- a/code/_globalvars/phobias.dm +++ b/code/_globalvars/phobias.dm @@ -57,8 +57,8 @@ GLOBAL_LIST_INIT(phobia_mobs, list( )), "anime" = typecacheof(list(/mob/living/simple_animal/hostile/guardian)), "birds" = typecacheof(list( + /mob/living/basic/chick, /mob/living/basic/chicken, - /mob/living/simple_animal/chick, /mob/living/simple_animal/parrot, /mob/living/simple_animal/pet/penguin, )), diff --git a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm b/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm index 05b09c53fb9..9c60f2f4164 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm @@ -96,6 +96,12 @@ emote_hear = list("clucks.", "croons.") emote_see = list("pecks at the ground.","flaps her wings viciously.") +/datum/ai_planning_subtree/random_speech/chick + speech_chance = 4 + speak = list("Cherp.", "Cherp?", "Chirrup.", "Cheep!") + emote_hear = list("cheeps.") + emote_see = list("pecks at the ground.","flaps her tiny wings.") + /datum/ai_planning_subtree/random_speech/cow speech_chance = 1 speak = list("moo?","moo","MOOOOOO") diff --git a/code/datums/components/growth_and_differentiation.dm b/code/datums/components/growth_and_differentiation.dm index 8dd659bad81..e326c4f4a77 100644 --- a/code/datums/components/growth_and_differentiation.dm +++ b/code/datums/components/growth_and_differentiation.dm @@ -2,10 +2,7 @@ * ### Growth and Differentiation Component: Used to randomly "grow" a creature into a new entity over its lifespan. * * If we are passed a typepath, we will 100% grow into that type. However, if we are not passed a typepath, we will pick one from a subtype of the parent we were applied to! - * - * Used for spiderlings to turn them into giant spiders. */ - /datum/component/growth_and_differentiation /// What this mob turns into when fully grown. var/growth_path @@ -18,6 +15,8 @@ var/lower_growth_value /// Integer - The upper bound for the percentage we have to grow before we can differentiate. var/upper_growth_value + /// List of signals we kill on ourselves when we grow. + var/list/signals_to_kill_on /// Optional callback for checks to see if we're okay to grow. var/datum/callback/optional_checks /// Optional callback in case we wish to override the default grow() behavior. Assume we supersede the change_mob_type() call if we have this set. @@ -32,7 +31,16 @@ /// and will actively try to grow the mob (only barred by optional checks). var/ready_to_grow = FALSE -/datum/component/growth_and_differentiation/Initialize(growth_time, growth_path, growth_probability, lower_growth_value, upper_growth_value, optional_checks, optional_grow_behavior) +/datum/component/growth_and_differentiation/Initialize( + growth_time, + growth_path, + growth_probability, + lower_growth_value, + upper_growth_value, + list/signals_to_kill_on, + datum/callback/optional_checks, + datum/callback/optional_grow_behavior, +) if(!isliving(parent)) return COMPONENT_INCOMPATIBLE @@ -44,6 +52,10 @@ src.optional_checks = optional_checks src.optional_grow_behavior = optional_grow_behavior + if(islist(signals_to_kill_on)) + src.signals_to_kill_on = signals_to_kill_on + RegisterSignals(parent, src.signals_to_kill_on, PROC_REF(stop_component_processing_entirely)) + // If we haven't started the round, we can't do timer stuff. Let's wait in case we're mapped in or something. if(!SSticker.HasRoundStarted() && !isnull(growth_time)) RegisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING, PROC_REF(comp_on_round_start)) @@ -52,11 +64,14 @@ return setup_growth_tracking() /datum/component/growth_and_differentiation/Destroy(force, silent) - . = ..() + STOP_PROCESSING(SSdcs, src) deltimer(timer_id) + return ..() -/datum/component/growth_and_differentiation/UnregisterFromParent() - UnregisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING) +/// Wrapper for qdel() so we can pass it in RegisterSignals(). I hate it here too. +/datum/component/growth_and_differentiation/proc/stop_component_processing_entirely() + SIGNAL_HANDLER + qdel(src) /// What we invoke when the round starts so we can set up our timer. /datum/component/growth_and_differentiation/proc/comp_on_round_start() @@ -64,7 +79,7 @@ setup_growth_tracking() UnregisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING) -/// Sets up the failover timer for certain growth. +/// Sets up the two different systems for growth: the timer and the probability based one. Both can coexist. Return COMPONENT_INCOMPATIBLE if we fail to set up either. /datum/component/growth_and_differentiation/proc/setup_growth_tracking() var/did_we_add_at_least_one_thing = FALSE @@ -113,10 +128,11 @@ optional_grow_behavior.Invoke() return - var/mob/living/new_mob = growth_path - if(!istype(new_mob)) + if(!ispath(growth_path, /mob/living)) CRASH("Growth and Differentiation Component: Growth path was not a mob type! If you wanted to do something special, please put it in the optional_grow_behavior callback instead!") + var/mob/living/new_mob = growth_path + var/new_mob_name = initial(new_mob.name) if(!silent) diff --git a/code/datums/memory/_memory.dm b/code/datums/memory/_memory.dm index c0bf77fafd5..e9dfa19d469 100644 --- a/code/datums/memory/_memory.dm +++ b/code/datums/memory/_memory.dm @@ -249,6 +249,7 @@ /mob/living/basic/carp, /mob/living/basic/carp/magic, /mob/living/basic/carp/magic/chaos, + /mob/living/basic/chick, /mob/living/basic/chicken, /mob/living/basic/cow, /mob/living/basic/cow/wisdom, @@ -263,7 +264,6 @@ /mob/living/basic/statue, /mob/living/basic/stickman, /mob/living/basic/stickman/dog, - /mob/living/simple_animal/chick, /mob/living/simple_animal/crab, /mob/living/simple_animal/hostile/asteroid/basilisk/watcher, /mob/living/simple_animal/hostile/asteroid/goliath/beast, diff --git a/code/game/objects/items/food/egg.dm b/code/game/objects/items/food/egg.dm index 174f893a9f7..a93ba6e3cd1 100644 --- a/code/game/objects/items/food/egg.dm +++ b/code/game/objects/items/food/egg.dm @@ -67,14 +67,14 @@ GLOBAL_VAR_INIT(chicks_from_eggs, 0) var/chance = rand(0, 255) switch(chance) if(0 to 30) - new /mob/living/simple_animal/chick(hit_turf) + new /mob/living/basic/chick(hit_turf) GLOB.chicks_from_eggs++ visible_message(span_notice("A chick comes out of the cracked egg!")) if(31) var/spawned_chickens = min(4, MAX_CHICKENS - GLOB.chicks_from_eggs) // We don't want to go over the limit visible_message(span_notice("[spawned_chickens] chicks come out of the egg! Jackpot!")) for(var/i in 1 to spawned_chickens) - new /mob/living/simple_animal/chick(hit_turf) + new /mob/living/basic/chick(hit_turf) GLOB.chicks_from_eggs++ reagents.expose(hit_atom, TOUCH) @@ -171,7 +171,7 @@ GLOBAL_VAR_INIT(chicks_from_eggs, 0) . = ..() AddComponent(/datum/component/fertile_egg,\ - embryo_type = /mob/living/simple_animal/chick,\ + embryo_type = /mob/living/basic/chick,\ minimum_growth_rate = 1,\ maximum_growth_rate = 2,\ total_growth_required = 200,\ diff --git a/code/modules/cargo/packs/livestock.dm b/code/modules/cargo/packs/livestock.dm index 333ead4bc1a..bd58abd984f 100644 --- a/code/modules/cargo/packs/livestock.dm +++ b/code/modules/cargo/packs/livestock.dm @@ -52,7 +52,7 @@ desc = "The chicken goes bwaak!" cost = CARGO_CRATE_VALUE * 4 access_view = ACCESS_KITCHEN - contains = list( /mob/living/simple_animal/chick) + contains = list(/mob/living/basic/chick) crate_name = "chicken crate" /datum/supply_pack/critter/corgi diff --git a/code/modules/holodeck/holo_effect.dm b/code/modules/holodeck/holo_effect.dm index 5b2de971ce1..f97a7f2a139 100644 --- a/code/modules/holodeck/holo_effect.dm +++ b/code/modules/holodeck/holo_effect.dm @@ -83,8 +83,8 @@ . = ..() mobtype = list( /mob/living/basic/butterfly, + /mob/living/basic/chick/permanent, /mob/living/basic/rabbit, - /mob/living/simple_animal/chick/holo, /mob/living/simple_animal/pet/fox, ) mobtype += pick( diff --git a/code/modules/mob/living/basic/farm_animals/chicken/chick.dm b/code/modules/mob/living/basic/farm_animals/chicken/chick.dm new file mode 100644 index 00000000000..339b2c5e7f5 --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/chicken/chick.dm @@ -0,0 +1,64 @@ +/** + * ## Chicks + * + * Baby birds that grow into big chickens. + */ +/mob/living/basic/chick + name = "\improper chick" + desc = "Adorable! They make such a racket though." + icon_state = "chick" + icon_living = "chick" + icon_dead = "chick_dead" + icon_gib = "chick_gib" + gender = FEMALE + mob_biotypes = MOB_ORGANIC|MOB_BEAST + speak_emote = list("cheeps") + density = FALSE + butcher_results = list(/obj/item/food/meat/slab/chicken = 1) + response_help_continuous = "pets" + response_help_simple = "pet" + response_disarm_continuous = "gently pushes aside" + response_disarm_simple = "gently push aside" + response_harm_continuous = "kicks" + response_harm_simple = "kick" + attack_verb_continuous = "kicks" + attack_verb_simple = "kick" + health = 3 + maxHealth = 3 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + gold_core_spawnable = FRIENDLY_SPAWN + + /// What we grow into. + var/grow_as = /mob/living/basic/chicken + +/mob/living/basic/chick/Initialize(mapload) + . = ..() + pixel_x = base_pixel_x + rand(-6, 6) + pixel_y = base_pixel_y + rand(0, 10) + + ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) + + AddElement(/datum/element/pet_bonus, "chirps!") + AddElement(/datum/element/swabable, CELL_LINE_TABLE_CHICKEN, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) + AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW) + + if(!isnull(grow_as)) // we don't have a set time to grow up beyond whatever RNG dictates, and if we somehow get a client, all growth halts. + AddComponent(\ + /datum/component/growth_and_differentiation,\ + growth_time = null,\ + growth_path = grow_as,\ + growth_probability = 100,\ + lower_growth_value = 0.5,\ + upper_growth_value = 1,\ + signals_to_kill_on = list(COMSIG_MOB_CLIENT_LOGIN),\ + optional_checks = CALLBACK(src, PROC_REF(ready_to_grow)),\ + ) + +/// We don't grow into a chicken if we're not conscious. +/mob/living/basic/chick/proc/ready_to_grow() + return (stat == CONSCIOUS) + +/// Variant of chick that just spawns in the holodeck so you can pet it. Doesn't grow up. +/mob/living/basic/chick/permanent + grow_as = null diff --git a/code/modules/mob/living/basic/farm_animals/chicken.dm b/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm similarity index 98% rename from code/modules/mob/living/basic/farm_animals/chicken.dm rename to code/modules/mob/living/basic/farm_animals/chicken/chicken.dm index 6d39d483c0a..b3eb18e0cd4 100644 --- a/code/modules/mob/living/basic/farm_animals/chicken.dm +++ b/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm @@ -6,8 +6,6 @@ GLOBAL_VAR_INIT(chicken_count, 0) * * Not-entirely-flightless domesticated birds that lay eggs, which are then consumed by humans and other animals. */ - - /mob/living/basic/chicken name = "\improper chicken" desc = "Hopefully the eggs are good this season." @@ -69,7 +67,7 @@ GLOBAL_VAR_INIT(chicken_count, 0) if(GLOB.chicken_count <= MAX_CHICKENS && fertile && prob(25)) egg.AddComponent(\ /datum/component/fertile_egg,\ - embryo_type = /mob/living/simple_animal/chick,\ + embryo_type = /mob/living/basic/chick,\ minimum_growth_rate = 1,\ maximum_growth_rate = 2,\ total_growth_required = 200,\ @@ -94,3 +92,4 @@ GLOBAL_VAR_INIT(chicken_count, 0) /datum/ai_planning_subtree/basic_melee_attack_subtree, /datum/ai_planning_subtree/random_speech/chicken, ) + diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 4db1bc36496..f9c7ab40abd 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1380,6 +1380,7 @@ /mob/living/basic/carp, /mob/living/basic/carp/magic, /mob/living/basic/carp/magic/chaos, + /mob/living/basic/chick, /mob/living/basic/chicken, /mob/living/basic/cow, /mob/living/basic/giant_spider, @@ -1393,7 +1394,6 @@ /mob/living/basic/statue, /mob/living/basic/stickman, /mob/living/basic/stickman/dog, - /mob/living/simple_animal/chick, /mob/living/simple_animal/crab, /mob/living/simple_animal/hostile/asteroid/basilisk/watcher, /mob/living/simple_animal/hostile/asteroid/goliath/beast, diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index a1556d2f684..04d7ac56023 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -118,63 +118,3 @@ plant_target.visible_message(span_warning("[src] takes a big chomp out of [plant_target]!"), \ span_userdanger("[src] takes a big chomp out of your [edible_bodypart || "body"]!")) - - -/mob/living/simple_animal/chick - name = "\improper chick" - desc = "Adorable! They make such a racket though." - icon_state = "chick" - icon_living = "chick" - icon_dead = "chick_dead" - icon_gib = "chick_gib" - gender = FEMALE - mob_biotypes = MOB_ORGANIC|MOB_BEAST - speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") - speak_emote = list("cheeps") - emote_hear = list("cheeps.") - emote_see = list("pecks at the ground.","flaps her tiny wings.") - density = FALSE - speak_chance = 2 - turns_per_move = 2 - butcher_results = list(/obj/item/food/meat/slab/chicken = 1) - response_help_continuous = "pets" - response_help_simple = "pet" - response_disarm_continuous = "gently pushes aside" - response_disarm_simple = "gently push aside" - response_harm_continuous = "kicks" - response_harm_simple = "kick" - attack_verb_continuous = "kicks" - attack_verb_simple = "kick" - health = 3 - maxHealth = 3 - var/amount_grown = 0 - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_size = MOB_SIZE_TINY - gold_core_spawnable = FRIENDLY_SPAWN - - footstep_type = FOOTSTEP_MOB_CLAW - -/mob/living/simple_animal/chick/Initialize(mapload) - . = ..() - AddElement(/datum/element/pet_bonus, "chirps!") - pixel_x = base_pixel_x + rand(-6, 6) - pixel_y = base_pixel_y + rand(0, 10) - add_cell_sample() - ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) - -/mob/living/simple_animal/chick/add_cell_sample() - AddElement(/datum/element/swabable, CELL_LINE_TABLE_CHICKEN, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) - -/mob/living/simple_animal/chick/Life(seconds_per_tick = SSMOBS_DT, times_fired) - . =..() - if(!.) - return - if(!stat && !ckey) - amount_grown += rand(0.5 * seconds_per_tick, 1 * seconds_per_tick) - if(amount_grown >= 100) - new /mob/living/basic/chicken(src.loc) - qdel(src) - -/mob/living/simple_animal/chick/holo/Life(seconds_per_tick = SSMOBS_DT, times_fired) - ..() - amount_grown = 0 diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 1d6823dc1ba..00a07a29850 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -32,8 +32,6 @@ /mob/living/simple_animal/bot/secbot/honkbot, /mob/living/simple_animal/bot/secbot/pingsky, /mob/living/simple_animal/bot/vibebot, - /mob/living/simple_animal/chick, - /mob/living/simple_animal/chick/holo, /mob/living/simple_animal/crab, /mob/living/simple_animal/crab/coffee, /mob/living/simple_animal/crab/evil, diff --git a/tgstation.dme b/tgstation.dme index 6622a0b9b10..19256d0c56f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3942,11 +3942,12 @@ #include "code\modules\mob\living\basic\festivus_pole.dm" #include "code\modules\mob\living\basic\health_adjustment.dm" #include "code\modules\mob\living\basic\tree.dm" -#include "code\modules\mob\living\basic\farm_animals\chicken.dm" #include "code\modules\mob\living\basic\farm_animals\deer.dm" #include "code\modules\mob\living\basic\farm_animals\pig.dm" #include "code\modules\mob\living\basic\farm_animals\rabbit.dm" #include "code\modules\mob\living\basic\farm_animals\sheep.dm" +#include "code\modules\mob\living\basic\farm_animals\chicken\chick.dm" +#include "code\modules\mob\living\basic\farm_animals\chicken\chicken.dm" #include "code\modules\mob\living\basic\farm_animals\cow\_cow.dm" #include "code\modules\mob\living\basic\farm_animals\cow\cow_ai.dm" #include "code\modules\mob\living\basic\farm_animals\cow\cow_moonicorn.dm" diff --git a/tools/UpdatePaths/Scripts/75592_simple_to_basic_chickens.txt b/tools/UpdatePaths/Scripts/75592_simple_to_basic_chickens.txt index 1b2f6ee1d19..c2eaf98dc58 100644 --- a/tools/UpdatePaths/Scripts/75592_simple_to_basic_chickens.txt +++ b/tools/UpdatePaths/Scripts/75592_simple_to_basic_chickens.txt @@ -1 +1,5 @@ /mob/living/simple_animal/chicken : /mob/living/basic/chicken{@OLD} + +## Updated in #75663 +/mob/living/simple_animal/chick : /mob/living/basic/chick{@OLD} +/mob/living/simple_animal/chick/holo : /mob/living/basic/chick/permanent{@OLD}