diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 157381a259e..e1e29dfa8aa 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -57466,7 +57466,7 @@ /turf/open/floor/iron/white, /area/medical/medbay/central) "syT" = ( -/mob/living/simple_animal/cow{ +/mob/living/basic/cow{ name = "Betsy"; real_name = "Betsy" }, diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 35ffe84198f..1cb60d373a1 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -15045,7 +15045,7 @@ /turf/open/floor/iron, /area/centcom/supplypod/loading/four) "ZT" = ( -/mob/living/simple_animal/cow, +/mob/living/basic/cow, /turf/open/floor/grass, /area/centcom/holding) "ZU" = ( diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm index 0dc68fc0d7f..55c75c99c02 100644 --- a/code/__DEFINES/ai.dm +++ b/code/__DEFINES/ai.dm @@ -180,6 +180,12 @@ ///Basic Mob Keys +///Tipped blackboards +///Bool that means a basic mob will start reacting to being tipped in it's planning +#define BB_BASIC_MOB_TIP_REACTING "BB_basic_tip_reacting" +///the motherfucker who tipped us +#define BB_BASIC_MOB_TIPPER "BB_basic_tip_tipper" + ///Targetting subtrees #define BB_BASIC_MOB_CURRENT_TARGET "BB_basic_current_target" #define BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION "BB_basic_current_target_hiding_location" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index c0328b993b9..c060d03067c 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -118,7 +118,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define ismouse(A) (istype(A, /mob/living/simple_animal/mouse)) -#define iscow(A) (istype(A, /mob/living/simple_animal/cow)) +#define iscow(A) (istype(A, /mob/living/basic/cow)) #define isslime(A) (istype(A, /mob/living/simple_animal/slime)) diff --git a/code/__DEFINES/research.dm b/code/__DEFINES/research.dm index 8780ebc8850..fd41c9abadc 100644 --- a/code/__DEFINES/research.dm +++ b/code/__DEFINES/research.dm @@ -61,6 +61,7 @@ #define CELL_LINE_TABLE_COCKROACH "cell_line_cockroach_table" #define CELL_LINE_TABLE_CORGI "cell_line_corgi_table" #define CELL_LINE_TABLE_COW "cell_line_cow_table" +#define CELL_LINE_TABLE_MOONICORN "cell_line_moonicorn_table" #define CELL_LINE_TABLE_GELATINOUS "cell_line_gelatinous_table" #define CELL_LINE_TABLE_GRAPE "cell_line_grape_table" #define CELL_LINE_TABLE_MEGACARP "cell_line_megacarp_table" diff --git a/code/_globalvars/lists/xenobiology.dm b/code/_globalvars/lists/xenobiology.dm index f7057481255..ad9511c5739 100644 --- a/code/_globalvars/lists/xenobiology.dm +++ b/code/_globalvars/lists/xenobiology.dm @@ -15,6 +15,7 @@ GLOBAL_LIST_INIT_TYPED(cell_line_tables, /list, list( CELL_LINE_TABLE_COCKROACH = list(/datum/micro_organism/cell_line/cockroach = 1), CELL_LINE_TABLE_CORGI = list(/datum/micro_organism/cell_line/corgi = 1), CELL_LINE_TABLE_COW = list(/datum/micro_organism/cell_line/cow = 1), + CELL_LINE_TABLE_MOONICORN = list(/datum/micro_organism/cell_line/moonicorn = 1), CELL_LINE_TABLE_GELATINOUS = list(/datum/micro_organism/cell_line/gelatinous_cube = 1), CELL_LINE_TABLE_GRAPE = list(/datum/micro_organism/cell_line/sholean_grapes = 1), CELL_LINE_TABLE_MEGACARP = list(/datum/micro_organism/cell_line/megacarp = 1), diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/tipped_reaction.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/tipped_reaction.dm new file mode 100644 index 00000000000..5b13fe6d3ef --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/tipped_reaction.dm @@ -0,0 +1,37 @@ + +///type of tipped reaction that is akin to puppy dog eyes +/datum/ai_behavior/tipped_reaction + +/datum/ai_behavior/tipped_reaction/perform(delta_time, datum/ai_controller/controller, tipper_key, reacting_key) + . = ..() + + var/mob/living/carbon/tipper = controller.blackboard[tipper_key] + + // visible part of the visible message + var/seen_message = "" + // self part of the visible message + var/self_message = "" + // the mob we're looking to for aid + var/mob/living/carbon/savior + // look for someone in a radius around us for help. If our original tipper is in range, prioritize them + for(var/mob/living/carbon/potential_aid in oview(3, get_turf(controller.pawn))) + if(potential_aid == tipper) + savior = tipper + break + savior = potential_aid + + if(prob(75) && savior) + var/text = pick("imploringly", "pleadingly", "with a resigned expression") + seen_message = "[controller.pawn] looks at [savior] [text]." + self_message = "You look at [savior] [text]." + else + seen_message = "[controller.pawn] seems resigned to its fate." + self_message = "You resign yourself to your fate." + controller.pawn.visible_message(span_notice("[seen_message]"), span_notice("[self_message]")) + finish_action(controller, TRUE, tipper_key, reacting_key) + +/datum/ai_behavior/tipped_reaction/finish_action(datum/ai_controller/controller, succeeded, tipper_key, reacting_key) + . = ..() + //I'VE SAID MY PEACE... + controller.blackboard[reacting_key] = FALSE + controller.blackboard[tipper_key] = null 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 ad4555149cd..55654a33ffc 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm @@ -37,3 +37,17 @@ /datum/ai_planning_subtree/random_speech/cockroach speech_chance = 5 emote_hear = list("chitters") + +/datum/ai_planning_subtree/random_speech/cow + speech_chance = 1 + speak = list("moo?","moo","MOOOOOO") + emote_hear = list("brays.") + emote_see = list("shakes her head.") + +///unlike normal cows, wisdom cows speak of wisdom and won't shut the fuck up +/datum/ai_planning_subtree/random_speech/cow/wisdom + speech_chance = 15 + +/datum/ai_planning_subtree/random_speech/cow/wisdom/New() + . = ..() + speak = GLOB.wisdoms //Done here so it's setup properly diff --git a/code/datums/ai/basic_mobs/basic_subtrees/tipped_subtree.dm b/code/datums/ai/basic_mobs/basic_subtrees/tipped_subtree.dm new file mode 100644 index 00000000000..4dc9af13b6e --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/tipped_subtree.dm @@ -0,0 +1,10 @@ +///used by cows +/datum/ai_planning_subtree/tip_reaction + +/datum/ai_planning_subtree/tip_reaction/SelectBehaviors(datum/ai_controller/controller, delta_time) + . = ..() + var/tip_reacting = controller.blackboard[BB_BASIC_MOB_TIP_REACTING] + if(!tip_reacting) + return + controller.queue_behavior(/datum/ai_behavior/tipped_reaction, BB_BASIC_MOB_TIPPER, BB_BASIC_MOB_TIP_REACTING) + return SUBTREE_RETURN_FINISH_PLANNING //no point in trying, boy. you're TIPPED. diff --git a/code/datums/elements/movement_turf_changer.dm b/code/datums/elements/movement_turf_changer.dm new file mode 100644 index 00000000000..25dff387b52 --- /dev/null +++ b/code/datums/elements/movement_turf_changer.dm @@ -0,0 +1,32 @@ +/** + * movement_turf_changer element; which makes the movement of a movable atom change the turf it moved to + * + * Used for moonicorns! + */ +/datum/element/movement_turf_changer + element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH + id_arg_index = 2 + ///Path of the turf added on top + var/turf_type + +/datum/element/movement_turf_changer/Attach(datum/target, turf_type) + . = ..() + + if(!ismovable(target)) + return ELEMENT_INCOMPATIBLE + + src.turf_type = turf_type + RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/on_moved) + +/datum/element/movement_turf_changer/Detach(datum/target) + UnregisterSignal(target, COMSIG_MOVABLE_MOVED) + . = ..() + +/datum/element/movement_turf_changer/proc/on_moved(atom/movable/target, atom/origin, direction, forced) + SIGNAL_HANDLER + + var/turf/destination = target.loc + if(!isturf(destination) || istype(destination, turf_type)) + return + + destination.PlaceOnTop(turf_type) diff --git a/code/datums/elements/venomous.dm b/code/datums/elements/venomous.dm index 2901dbf97c3..f513e03bd6c 100644 --- a/code/datums/elements/venomous.dm +++ b/code/datums/elements/venomous.dm @@ -18,7 +18,7 @@ RegisterSignal(target, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit) else if(isitem(target)) RegisterSignal(target, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack) - else if(ishostile(target)) + else if(ishostile(target) || isbasicmob(target)) RegisterSignal(target, COMSIG_HOSTILE_POST_ATTACKINGTARGET, .proc/hostile_attackingtarget) else return ELEMENT_INCOMPATIBLE diff --git a/code/datums/memory/memory.dm b/code/datums/memory/memory.dm index f0b934dd734..270a491e58d 100644 --- a/code/datums/memory/memory.dm +++ b/code/datums/memory/memory.dm @@ -64,13 +64,13 @@ /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/mouse, /mob/living/simple_animal/chicken, - /mob/living/simple_animal/cow, + /mob/living/basic/cow, /mob/living/simple_animal/hostile/lizard, /mob/living/simple_animal/pet/fox, /mob/living/simple_animal/butterfly, /mob/living/simple_animal/pet/cat/cak, /mob/living/simple_animal/chick, - /mob/living/simple_animal/cow/wisdom, + /mob/living/basic/cow/wisdom, /obj/item/skub, /obj/item/food/american_sausage ) diff --git a/code/game/objects/items/drug_items.dm b/code/game/objects/items/drug_items.dm index 237600841ea..498d768dbbe 100644 --- a/code/game/objects/items/drug_items.dm +++ b/code/game/objects/items/drug_items.dm @@ -27,6 +27,7 @@ /obj/item/food/drug/moon_rock/Initialize() . = ..() icon_state = pick("moon_rock1", "moon_rock2", "moon_rock3") + AddElement(/datum/element/swabable, CELL_LINE_TABLE_MOONICORN, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) /obj/item/reagent_containers/glass/blastoff_ampoule name = "bLaSToFF ampoule" //stylized name diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index 2712bb6e640..ac3faac2aad 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -906,7 +906,7 @@ /obj/effect/mob_spawn/cow name = "sleeper" - mob_type = /mob/living/simple_animal/cow + mob_type = /mob/living/basic/cow death = FALSE roundstart = FALSE mob_gender = FEMALE diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm index 24bff745427..a9bde16e738 100644 --- a/code/modules/antagonists/wizard/equipment/artefact.dm +++ b/code/modules/antagonists/wizard/equipment/artefact.dm @@ -40,7 +40,7 @@ icon_state = "rift" density = TRUE anchored = TRUE - var/spawn_path = /mob/living/simple_animal/cow //defaulty cows to prevent unintentional narsies + var/spawn_path = /mob/living/basic/cow //defaulty cows to prevent unintentional narsies var/spawn_amt_left = 20 var/spawn_fast = FALSE @@ -78,7 +78,7 @@ /obj/item/veilrender/vealrender name = "veal render" desc = "A wicked curved blade of alien origin, recovered from the ruins of a vast farm." - spawn_type = /mob/living/simple_animal/cow + spawn_type = /mob/living/basic/cow spawn_amt = 20 activate_descriptor = "hunger" rend_desc = "Reverberates with the sound of ten thousand moos." diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 15197e3a365..b482590b019 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -2077,7 +2077,7 @@ desc = "The cow goes moo!" cost = CARGO_CRATE_VALUE * 6 access_view = ACCESS_HYDROPONICS - contains = list(/mob/living/simple_animal/cow) + contains = list(/mob/living/basic/cow) crate_name = "cow crate" /datum/supply_pack/critter/crab diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index 1b9dd30a803..15893a71184 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -6,7 +6,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( /mob/living/simple_animal/mouse/brown/tom, /mob/living/simple_animal/hostile/retaliate/goat, /mob/living/simple_animal/chicken, - /mob/living/simple_animal/cow, + /mob/living/basic/cow, /mob/living/simple_animal/hostile/retaliate/bat, /mob/living/simple_animal/hostile/carp/cayenne, /mob/living/simple_animal/butterfly, diff --git a/code/modules/events/wisdomcow.dm b/code/modules/events/wisdomcow.dm index cfffb060a7e..b12938b3f94 100644 --- a/code/modules/events/wisdomcow.dm +++ b/code/modules/events/wisdomcow.dm @@ -9,6 +9,6 @@ /datum/round_event/wisdomcow/start() var/turf/targetloc = get_safe_random_station_turf() - new /mob/living/simple_animal/cow/wisdom(targetloc) + new /mob/living/basic/cow/wisdom(targetloc) do_smoke(1, targetloc) diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm index 8136e138d34..79247e54477 100644 --- a/code/modules/experisci/experiment/experiments.dm +++ b/code/modules/experisci/experiment/experiments.dm @@ -62,7 +62,7 @@ description = "We need to see how the body functions from the earliest moments. Some cytology experiments will help us gain this understanding." total_requirement = 3 max_requirement_per_type = 2 - possible_types = list(/mob/living/simple_animal/hostile/carp, /mob/living/simple_animal/hostile/retaliate/snake, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/dog/corgi, /mob/living/simple_animal/cow, /mob/living/simple_animal/chicken) + possible_types = list(/mob/living/simple_animal/hostile/carp, /mob/living/simple_animal/hostile/retaliate/snake, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/dog/corgi, /mob/living/basic/cow, /mob/living/simple_animal/chicken) /datum/experiment/scanning/random/cytology/medium/one name = "Advanced Cytology Scanning Experiment One" diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm index a955f90dde3..30bae0e35af 100644 --- a/code/modules/mob/living/basic/basic.dm +++ b/code/modules/mob/living/basic/basic.dm @@ -83,8 +83,6 @@ ///Sentience type, for slime potions. SHOULD BE AN ELEMENT BUT I DONT CARE ABOUT IT FOR NOW var/sentience_type = SENTIENCE_ORGANIC - - /mob/living/basic/Initialize(mapload) . = ..() @@ -132,6 +130,10 @@ SEND_SIGNAL(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, target, result) return result +/mob/living/basic/vv_edit_var(vname, vval) + if(vname == NAMEOF(src, speed)) + set_varspeed(vval) + /mob/living/basic/proc/set_varspeed(var_value) speed = var_value update_basic_mob_varspeed() diff --git a/code/modules/mob/living/basic/farm_animals/cows.dm b/code/modules/mob/living/basic/farm_animals/cows.dm new file mode 100644 index 00000000000..06ecf100b2e --- /dev/null +++ b/code/modules/mob/living/basic/farm_animals/cows.dm @@ -0,0 +1,192 @@ +//cow +/mob/living/basic/cow + name = "cow" + desc = "Known for their milk, just don't tip them over." + icon = 'icons/mob/cows.dmi' + icon_state = "cow" + icon_living = "cow" + icon_dead = "cow_dead" + icon_gib = "cow_gib" + gender = FEMALE + mob_biotypes = MOB_ORGANIC | MOB_BEAST + speak_emote = list("moos","moos hauntingly") + speed = 1.1 + see_in_dark = 6 + butcher_results = list(/obj/item/food/meat/slab = 6) + 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" + attack_sound = 'sound/weapons/punch1.ogg' + attack_vis_effect = ATTACK_EFFECT_KICK + health = 50 + maxHealth = 50 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + ai_controller = /datum/ai_controller/basic_controller/cow + +/mob/living/basic/cow/Initialize() + AddComponent(/datum/component/tippable, \ + tip_time = 0.5 SECONDS, \ + untip_time = 0.5 SECONDS, \ + self_right_time = rand(25 SECONDS, 50 SECONDS), \ + post_tipped_callback = CALLBACK(src, .proc/after_cow_tipped)) + AddElement(/datum/element/pet_bonus, "moos happily!") + AddElement(/datum/element/swabable, CELL_LINE_TABLE_COW, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) + udder_component() + make_tameable() + . = ..() + +///wrapper for the udder component addition so you can have uniquely uddered cow subtypes +/mob/living/basic/cow/proc/udder_component() + AddComponent(/datum/component/udder) + +///wrapper for the tameable component addition so you can have non tamable cow subtypes +/mob/living/basic/cow/proc/make_tameable() + AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/wheat), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, .proc/tamed)) + +/mob/living/basic/cow/proc/tamed(mob/living/tamer) + can_buckle = TRUE + buckle_lying = 0 + AddElement(/datum/element/ridable, /datum/component/riding/creature/cow) + +/* + * Proc called via callback after the cow is tipped by the tippable component. + * Begins a timer for us pleading for help. + * + * tipper - the mob who tipped us + */ +/mob/living/basic/cow/proc/after_cow_tipped(mob/living/carbon/tipper) + addtimer(CALLBACK(src, .proc/set_tip_react_blackboard, tipper), rand(10 SECONDS, 20 SECONDS)) + +/* + * We've been waiting long enough, we're going to tell our AI to begin pleading. + * + * tipper - the mob who originally tipped us + */ +/mob/living/basic/cow/proc/set_tip_react_blackboard(mob/living/carbon/tipper) + if(!HAS_TRAIT_FROM(src, TRAIT_IMMOBILIZED, TIPPED_OVER) || !ai_controller) + return + ai_controller.blackboard[BB_BASIC_MOB_TIP_REACTING] = TRUE + ai_controller.blackboard[BB_BASIC_MOB_TIPPER] = tipper + +/datum/ai_controller/basic_controller/cow + blackboard = list( + BB_BASIC_MOB_TIP_REACTING = FALSE, + BB_BASIC_MOB_TIPPER = null, + ) + + ai_traits = STOP_MOVING_WHEN_PULLED + ai_movement = /datum/ai_movement/basic_avoidance + planning_subtrees = list( + /datum/ai_planning_subtree/tip_reaction, + /datum/ai_planning_subtree/random_speech/cow, + ) + +/datum/ai_controller/basic_controller/cow/PerformIdleBehavior(delta_time) + . = ..() + var/mob/living/living_pawn = pawn + + if(DT_PROB(25, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby) + var/move_dir = pick(GLOB.alldirs) + living_pawn.Move(get_step(living_pawn, move_dir), move_dir) + +///Wisdom cow, gives XP to a random skill and speaks wisdoms +/mob/living/basic/cow/wisdom + name = "wisdom cow" + desc = "Known for its wisdom, shares it with all." + gold_core_spawnable = FALSE + +/mob/living/basic/cow/wisdom/make_tameable() + return //cannot tame me! + +/datum/ai_controller/basic_controller/cow/wisdom + planning_subtrees = list( + /datum/ai_planning_subtree/tip_reaction, + /datum/ai_planning_subtree/random_speech/cow/wisdom, + ) + +///Give intense wisdom to the attacker if they're being friendly about it +/mob/living/basic/cow/wisdom/attack_hand(mob/living/carbon/user, list/modifiers) + if(!stat && !user.combat_mode) + to_chat(user, span_nicegreen("[src] whispers you some intense wisdoms and then disappears!")) + user.mind?.adjust_experience(pick(GLOB.skill_types), 500) + do_smoke(1, get_turf(src)) + qdel(src) + return + return ..() + +/mob/living/basic/cow/moonicorn + name = "moonicorn" + desc = "Magical cow of legend. Its horn will pacify anything it touches, rendering victims mostly helpless. \ + Victims, yes, because despite the enimatic and wonderous appearance, the moonicorn is incredibly aggressive." + icon_state = "moonicorn" + icon_living = "moonicorn" + icon_dead = "moonicorn_dead" + icon_gib = null //otherwise does the regular cow gib animation + faction = list("hostile") + speed = 1 + melee_damage_lower = 25 + melee_damage_upper = 25 + obj_damage = 35 + attack_verb_continuous = "telekinetically rams its moonihorn into" + attack_verb_simple = "telekinetically ram your moonihorn into" + gold_core_spawnable = NO_SPAWN + attack_sound = 'sound/weapons/bladeslice.ogg' + attack_vis_effect = ATTACK_EFFECT_SLASH + ai_controller = /datum/ai_controller/basic_controller/cow/moonicorn + +/mob/living/basic/cow/moonicorn/Initialize() + . = ..() + AddElement(/datum/element/venomous, /datum/reagent/pax, 5) + AddElement(/datum/element/movement_turf_changer, /turf/open/floor/grass/fairy) + +/mob/living/basic/cow/moonicorn/udder_component() + AddComponent(/datum/component/udder, /obj/item/udder, null, null, /datum/reagent/drug/mushroomhallucinogen) + +/mob/living/basic/cow/moonicorn/make_tameable() + AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/galaxythistle), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, .proc/tamed)) + +/mob/living/basic/cow/moonicorn/tamed(mob/living/tamer) + . = ..() + visible_message(span_notice("[src] nods with respect.")) + ///stop killing my FRIENDS + faction |= tamer.faction + +/datum/ai_controller/basic_controller/cow/moonicorn + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/moonicorn(), + BB_BASIC_MOB_TIP_REACTING = FALSE, + BB_BASIC_MOB_TIPPER = null, + ) + + planning_subtrees = list( + /datum/ai_planning_subtree/tip_reaction, + /datum/ai_planning_subtree/random_speech/cow, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree/moonicorn, + ) + +/datum/ai_planning_subtree/basic_melee_attack_subtree/moonicorn + melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/moonicorn + +/datum/ai_behavior/basic_melee_attack/moonicorn + //it's a fairly strong attack and it applies pax, so they do not attack often + action_cooldown = 2 SECONDS + +///moonicorns will not attack people holding something that could tame them. +/datum/targetting_datum/basic/moonicorn + +/datum/targetting_datum/basic/moonicorn/can_attack(mob/living/living_mob, atom/the_target) + . = ..() + if(!.) + return FALSE + + if(isliving(the_target)) //Targetting vs living mobs + var/mob/living/living_target = the_target + for(var/obj/item/food/grown/galaxythistle/tame_food in living_target.held_items) + return FALSE //heyyy this can tame me! let's NOT fight diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 4071cba2ec5..905b13d5fef 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1280,7 +1280,7 @@ /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/mouse, /mob/living/simple_animal/chicken, - /mob/living/simple_animal/cow, + /mob/living/basic/cow, /mob/living/simple_animal/hostile/lizard, /mob/living/simple_animal/pet/fox, /mob/living/simple_animal/butterfly, 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 a9ae4717e54..13ac50511ca 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -97,126 +97,6 @@ H.visible_message(span_warning("[src] takes a big chomp out of [H]!"), \ span_userdanger("[src] takes a big chomp out of your [NB]!")) NB.dismember() -//cow -/mob/living/simple_animal/cow - name = "cow" - desc = "Known for their milk, just don't tip them over." - icon_state = "cow" - icon_living = "cow" - icon_dead = "cow_dead" - icon_gib = "cow_gib" - gender = FEMALE - mob_biotypes = MOB_ORGANIC | MOB_BEAST - speak = list("moo?","moo","MOOOOOO") - speak_emote = list("moos","moos hauntingly") - emote_hear = list("brays.") - emote_see = list("shakes her head.") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/food/meat/slab = 6) - 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" - attack_sound = 'sound/weapons/punch1.ogg' - attack_vis_effect = ATTACK_EFFECT_KICK - health = 50 - maxHealth = 50 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - footstep_type = FOOTSTEP_MOB_SHOE - -/mob/living/simple_animal/cow/Initialize() - AddComponent(/datum/component/udder) - AddComponent(/datum/component/tippable, \ - tip_time = 0.5 SECONDS, \ - untip_time = 0.5 SECONDS, \ - self_right_time = rand(25 SECONDS, 50 SECONDS), \ - post_tipped_callback = CALLBACK(src, .proc/after_cow_tipped)) - AddElement(/datum/element/pet_bonus, "moos happily!") - add_cell_sample() - make_tameable() - . = ..() - -///wrapper for the tameable component addition so you can have non tamable cow subtypes -/mob/living/simple_animal/cow/proc/make_tameable() - AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/wheat), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, .proc/tamed)) - -/mob/living/simple_animal/cow/add_cell_sample() - AddElement(/datum/element/swabable, CELL_LINE_TABLE_COW, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) - -/mob/living/simple_animal/cow/proc/tamed(mob/living/tamer) - can_buckle = TRUE - buckle_lying = 0 - AddElement(/datum/element/ridable, /datum/component/riding/creature/cow) - -/* - * Proc called via callback after the cow is tipped by the tippable component. - * Begins a timer for us pleading for help. - * - * tipper - the mob who tipped us - */ -/mob/living/simple_animal/cow/proc/after_cow_tipped(mob/living/carbon/tipper) - addtimer(CALLBACK(src, .proc/look_for_help, tipper), rand(10 SECONDS, 20 SECONDS)) - -/* - * Find a mob in a short radius around us (prioritizing the person who originally tipped us) - * and either look at them for help, or give up. No actual mechanical difference between the two. - * - * tipper - the mob who originally tipped us - */ -/mob/living/simple_animal/cow/proc/look_for_help(mob/living/carbon/tipper) - // visible part of the visible message - var/seen_message = "" - // self part of the visible message - var/self_message = "" - // the mob we're looking to for aid - var/mob/living/carbon/savior - // look for someone in a radius around us for help. If our original tipper is in range, prioritize them - for(var/mob/living/carbon/potential_aid in oview(3, get_turf(src))) - if(potential_aid == tipper) - savior = tipper - break - savior = potential_aid - - if(prob(75) && savior) - var/text = pick("imploringly", "pleadingly", "with a resigned expression") - seen_message = "[src] looks at [savior] [text]." - self_message = "You look at [savior] [text]." - else - seen_message = "[src] seems resigned to its fate." - self_message = "You resign yourself to your fate." - visible_message(span_notice("[seen_message]"), span_notice("[self_message]")) - -///Wisdom cow, gives XP to a random skill and speaks wisdoms -/mob/living/simple_animal/cow/wisdom - name = "wisdom cow" - desc = "Known for its wisdom, shares it with all" - gold_core_spawnable = FALSE - speak_chance = 15 - -/mob/living/simple_animal/cow/wisdom/Initialize() - . = ..() - speak = GLOB.wisdoms //Done here so it's setup properly - -/mob/living/simple_animal/cow/wisdom/make_tameable() - return //cannot tame - -///Give intense wisdom to the attacker if they're being friendly about it -/mob/living/simple_animal/cow/wisdom/attack_hand(mob/living/carbon/user, list/modifiers) - if(!stat && !user.combat_mode) - to_chat(user, span_nicegreen("[src] whispers you some intense wisdoms and then disappears!")) - user.mind?.adjust_experience(pick(GLOB.skill_types), 500) - do_smoke(1, get_turf(src)) - qdel(src) - return - return ..() - /mob/living/simple_animal/chick name = "\improper chick" diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 20fa0be40b5..cde09d2865c 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -25,6 +25,7 @@ ///Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps var/list/emote_see = list() + ///ticks up every time `handle_automated_movement()` is called, which is every 2 seconds at the time of documenting. 1 turns per move is 1 movement every 2 seconds. var/turns_per_move = 1 var/turns_since_move = 0 ///Use this to temporarely stop random movement or to if you write special movement code for animals. diff --git a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm index ec8614c1507..9f060524007 100644 --- a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm +++ b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm @@ -61,7 +61,32 @@ /datum/reagent/toxin/carpotoxin = -5) virus_suspectibility = 1 - resulting_atoms = list(/mob/living/simple_animal/cow = 1) + resulting_atoms = list(/mob/living/basic/cow = 1) + +/datum/micro_organism/cell_line/moonicorn + desc = "Fairyland Bovine stem cells" + required_reagents = list( + /datum/reagent/consumable/nutriment/protein, + /datum/reagent/consumable/nutriment, + /datum/reagent/drug/mushroomhallucinogen, + ) + + supplementary_reagents = list( + /datum/reagent/growthserum = 4, + /datum/reagent/consumable/tinlux = 2, + /datum/reagent/consumable/vitfro = 2, + /datum/reagent/consumable/astrotame = 1, + ) + + suppressive_reagents = list( + /datum/reagent/toxin = -2, + /datum/reagent/toxin/carpotoxin = -5, + /datum/reagent/consumable/coffee = -3, + /datum/reagent/consumable/triple_citrus = -5, + ) + + virus_suspectibility = 1 + resulting_atoms = list(/mob/living/basic/cow/moonicorn = 1) /datum/micro_organism/cell_line/cat desc = "Feliform cells" diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index 0439961ad2e..e95e2174722 100644 Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ diff --git a/icons/mob/cows.dmi b/icons/mob/cows.dmi new file mode 100644 index 00000000000..87c2247e0ed Binary files /dev/null and b/icons/mob/cows.dmi differ diff --git a/tgstation.dme b/tgstation.dme index b6feb03a6d7..af0ed4ce30d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -478,9 +478,11 @@ #include "code\datums\ai\basic_mobs\base_basic_controller.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\basic_attacking.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\targetting.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\speech_subtree.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\tipped_subtree.dm" #include "code\datums\ai\basic_mobs\targetting_datums\basic_targetting_datum.dm" #include "code\datums\ai\cursed\cursed_behaviors.dm" #include "code\datums\ai\cursed\cursed_controller.dm" @@ -767,6 +769,7 @@ #include "code\datums\elements\light_blocking.dm" #include "code\datums\elements\light_eaten.dm" #include "code\datums\elements\light_eater.dm" +#include "code\datums\elements\movement_turf_changer.dm" #include "code\datums\elements\movetype_handler.dm" #include "code\datums\elements\obj_regen.dm" #include "code\datums\elements\openspace_item_click_handler.dm" @@ -2776,6 +2779,7 @@ #include "code\modules\mob\living\basic\basic.dm" #include "code\modules\mob\living\basic\basic_defense.dm" #include "code\modules\mob\living\basic\health_adjustment.dm" +#include "code\modules\mob\living\basic\farm_animals\cows.dm" #include "code\modules\mob\living\basic\ruin_defender\stickman.dm" #include "code\modules\mob\living\basic\vermin\cockroach.dm" #include "code\modules\mob\living\brain\brain.dm"