mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-23 03:57:13 +01:00
Ports deer to basic mobs and adds some behaviors (#29270)
* ports deer to basic mobs and adds some behaviors * update updatepaths script number * whitespace * Apply suggestions from code review Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: warriorstar-orion <orion@snowfrost.garden> --------- Signed-off-by: warriorstar-orion <orion@snowfrost.garden> Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
This commit is contained in:
co-authored by
PollardTheDragon
parent
9cabc380b4
commit
dfacf83e18
@@ -96809,7 +96809,7 @@
|
||||
/obj/structure/flora/ausbushes/fullgrass,
|
||||
/obj/structure/flora/ausbushes/grassybush,
|
||||
/obj/structure/railing,
|
||||
/mob/living/simple_animal/deer{
|
||||
/mob/living/basic/deer{
|
||||
name = "Reed"
|
||||
},
|
||||
/turf/simulated/floor/grass,
|
||||
|
||||
@@ -42,6 +42,15 @@
|
||||
///List of mobs who have damaged us
|
||||
#define BB_BASIC_MOB_RETALIATE_LIST "BB_basic_mob_shitlist"
|
||||
|
||||
//Hunting BB keys
|
||||
|
||||
/// Key that holds our current hunting target
|
||||
#define BB_CURRENT_HUNTING_TARGET "BB_current_hunting_target"
|
||||
/// Key that holds our less priority hunting target
|
||||
#define BB_LOW_PRIORITY_HUNTING_TARGET "BB_low_priority_hunting_target"
|
||||
/// Key that holds the cooldown for our hunting subtree
|
||||
#define BB_HUNTING_COOLDOWN(type) "BB_HUNTING_COOLDOWN_[type]"
|
||||
|
||||
// Food and eating
|
||||
|
||||
/// list of foods this mob likes
|
||||
@@ -62,6 +71,17 @@
|
||||
/// the motherfucker who tipped us
|
||||
#define BB_BASIC_MOB_TIPPER "BB_basic_tip_tipper"
|
||||
|
||||
/// Is there something that scared us into being stationary? If so, hold the reference here
|
||||
#define BB_STATIONARY_CAUSE "BB_thing_that_made_us_stationary"
|
||||
///How long should we remain stationary for?
|
||||
#define BB_STATIONARY_SECONDS "BB_stationary_time_in_seconds"
|
||||
///Should we move towards the target that triggered us to be stationary?
|
||||
#define BB_STATIONARY_MOVE_TO_TARGET "BB_stationary_move_to_target"
|
||||
/// What targets will trigger us to be stationary? Must be a list.
|
||||
#define BB_STATIONARY_TARGETS "BB_stationary_targets"
|
||||
/// How often can we get spooked by a target?
|
||||
#define BB_STATIONARY_COOLDOWN "BB_stationary_cooldown"
|
||||
|
||||
// Misc
|
||||
|
||||
/// For /datum/ai_behavior/find_potential_targets, what if any field are we using currently
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//deer
|
||||
|
||||
/// Our water target
|
||||
#define BB_DEER_WATER_TARGET "deer_water_target"
|
||||
/// Our grass target
|
||||
#define BB_DEER_GRASS_TARGET "deer_grass_target"
|
||||
/// Our tree target
|
||||
#define BB_DEER_TREE_TARGET "deer_tree_target"
|
||||
/// Our temporary playmate
|
||||
#define BB_DEER_PLAYFRIEND "deer_playfriend"
|
||||
/// Our home target
|
||||
#define BB_DEER_TREEHOME "deer_home"
|
||||
/// Our resting duration
|
||||
#define BB_DEER_RESTING "deer_resting"
|
||||
/// Time till our next rest duration
|
||||
#define BB_DEER_NEXT_REST_TIMER "deer_next_rest_timer"
|
||||
@@ -435,6 +435,7 @@
|
||||
#define INVESTIGATE_SINGULO "singulo"
|
||||
#define INVESTIGATE_SUPERMATTER "supermatter"
|
||||
#define INVESTIGATE_WIRES "wires"
|
||||
#define INVESTIGATE_DEATHS "deaths"
|
||||
|
||||
// The SQL version required by this version of the code
|
||||
#define SQL_VERSION 67
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/datum/ai_behavior/emote_on_target
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
|
||||
|
||||
/datum/ai_behavior/emote_on_target/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/atom/hunt_target = controller.blackboard[target_key]
|
||||
if(isnull(hunt_target))
|
||||
return FALSE
|
||||
set_movement_target(controller, hunt_target)
|
||||
|
||||
/datum/ai_behavior/emote_on_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, list/emote_list)
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
if(!length(emote_list) || isnull(target))
|
||||
return AI_BEHAVIOR_FAILED | AI_BEHAVIOR_DELAY
|
||||
run_emote(controller.pawn, target, emote_list)
|
||||
return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY
|
||||
|
||||
/datum/ai_behavior/emote_on_target/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||
. = ..()
|
||||
if(succeeded)
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
/datum/ai_behavior/emote_on_target/proc/run_emote(mob/living/living_pawn, atom/target, list/emote_list)
|
||||
living_pawn.custom_emote(EMOTE_VISIBLE, "[pick(emote_list)] [target]")
|
||||
@@ -0,0 +1,26 @@
|
||||
/// Makes a mob simply stop and stare at a movable... yea...
|
||||
/datum/ai_behavior/stop_and_stare
|
||||
behavior_flags = AI_BEHAVIOR_MOVE_AND_PERFORM
|
||||
|
||||
/datum/ai_behavior/stop_and_stare/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/atom/movable/target = controller.blackboard[target_key]
|
||||
return ismovable(target) && isturf(target.loc) && ismob(controller.pawn)
|
||||
|
||||
/datum/ai_behavior/stop_and_stare/get_cooldown(datum/ai_controller/cooldown_for)
|
||||
return cooldown_for.blackboard[BB_STATIONARY_COOLDOWN]
|
||||
|
||||
/datum/ai_behavior/stop_and_stare/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
var/atom/movable/target = controller.blackboard[target_key]
|
||||
if(!ismovable(target) || !isturf(target.loc)) // just to make sure that nothing funky happened between setup and perform
|
||||
return AI_BEHAVIOR_DELAY
|
||||
|
||||
var/mob/pawn_mob = controller.pawn
|
||||
var/turf/pawn_turf = get_turf(pawn_mob)
|
||||
|
||||
pawn_mob.face_atom(target)
|
||||
set_movement_target(controller, pawn_turf, /datum/ai_movement/complete_stop)
|
||||
|
||||
if(controller.blackboard[BB_STATIONARY_MOVE_TO_TARGET])
|
||||
addtimer(CALLBACK(src, PROC_REF(set_movement_target), controller, target, initial(controller.ai_movement)), (controller.blackboard[BB_STATIONARY_SECONDS] + 1 SECONDS))
|
||||
return AI_BEHAVIOR_DELAY
|
||||
@@ -0,0 +1,12 @@
|
||||
/// Locate a thing (practically any atom) to stop and stare at.
|
||||
/datum/ai_planning_subtree/stare_at_thing
|
||||
|
||||
/datum/ai_planning_subtree/stare_at_thing/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/atom/target = controller.blackboard[BB_STATIONARY_CAUSE]
|
||||
|
||||
if(isnull(target)) // No target? Time to locate one using the list we set in this mob's blackboard.
|
||||
var/list/potential_scares = controller.blackboard[BB_STATIONARY_TARGETS]
|
||||
controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_STATIONARY_CAUSE, potential_scares)
|
||||
return
|
||||
|
||||
controller.queue_behavior(/datum/ai_behavior/stop_and_stare, BB_STATIONARY_CAUSE)
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Tells the AI to find a certain target nearby to hunt.
|
||||
* If a target has been found, we will start to move towards it, and eventually attack it.
|
||||
*/
|
||||
/datum/ai_planning_subtree/find_and_hunt_target
|
||||
/// What key in the blacbkboard do we store our hunting target?
|
||||
/// If you want to have multiple hunting behaviors on a controller be sure that this is unique
|
||||
var/target_key = BB_CURRENT_HUNTING_TARGET
|
||||
/// What behavior to execute if we have no target
|
||||
var/finding_behavior = /datum/ai_behavior/find_hunt_target
|
||||
/// What behavior to execute if we do have a target
|
||||
var/hunting_behavior = /datum/ai_behavior/hunt_target
|
||||
/// What targets we're hunting for
|
||||
var/list/hunt_targets
|
||||
/// In what radius will we hunt
|
||||
var/hunt_range = 2
|
||||
/// What are the chances we hunt something at any given moment
|
||||
var/hunt_chance = 100
|
||||
///do we finish planning subtree
|
||||
var/finish_planning = TRUE
|
||||
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/New()
|
||||
. = ..()
|
||||
hunt_targets = typecacheof(hunt_targets)
|
||||
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(!SPT_PROB(hunt_chance, seconds_per_tick))
|
||||
return
|
||||
|
||||
if(controller.blackboard[BB_HUNTING_COOLDOWN(type)] >= world.time)
|
||||
return
|
||||
|
||||
if(!controller.blackboard_key_exists(target_key))
|
||||
controller.queue_behavior(finding_behavior, target_key, hunt_targets, hunt_range)
|
||||
return
|
||||
|
||||
// We ARE hunting something, execute the hunt.
|
||||
// Note that if our AI controller has multiple hunting subtrees set,
|
||||
// we may accidentally be executing another tree's hunt - not ideal,
|
||||
// try to set a unique target key if you have multiple
|
||||
|
||||
controller.queue_behavior(hunting_behavior, target_key, BB_HUNTING_COOLDOWN(type))
|
||||
if(finish_planning)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING //If we're hunting we're too busy for anything else
|
||||
|
||||
/// Finds a specific atom type to hunt.
|
||||
/datum/ai_behavior/find_hunt_target
|
||||
///is this only meant to search for turf types?
|
||||
var/search_turf_types = FALSE
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, types_to_hunt, hunt_range)
|
||||
var/mob/living/living_mob = controller.pawn
|
||||
var/list/interesting_objects = search_turf_types ? RANGE_TURFS(hunt_range, living_mob) : oview(hunt_range, living_mob)
|
||||
for(var/atom/possible_dinner as anything in typecache_filter_list(interesting_objects, types_to_hunt))
|
||||
if(!valid_dinner(living_mob, possible_dinner, hunt_range, controller, seconds_per_tick))
|
||||
continue
|
||||
controller.set_blackboard_key(hunting_target_key, possible_dinner)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/proc/valid_dinner(mob/living/source, atom/dinner, radius, datum/ai_controller/controller, seconds_per_tick)
|
||||
if(isliving(dinner))
|
||||
var/mob/living/living_target = dinner
|
||||
if(living_target.stat == DEAD)
|
||||
return FALSE
|
||||
|
||||
return can_see(source, dinner, radius)
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/search_turf_types
|
||||
search_turf_types = TRUE
|
||||
|
||||
/// Hunts down a specific atom type.
|
||||
/datum/ai_behavior/hunt_target
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
|
||||
/// How long do we have to wait after a successful hunt?
|
||||
var/hunt_cooldown = 5 SECONDS
|
||||
/// Do we reset the target after attacking something, so we can check for status changes.
|
||||
var/always_reset_target = FALSE
|
||||
|
||||
/datum/ai_behavior/hunt_target/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
|
||||
. = ..()
|
||||
var/atom/hunt_target = controller.blackboard[hunting_target_key]
|
||||
if(isnull(hunt_target))
|
||||
return FALSE
|
||||
set_movement_target(controller, hunt_target)
|
||||
|
||||
/datum/ai_behavior/hunt_target/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
|
||||
var/mob/living/hunter = controller.pawn
|
||||
var/atom/hunted = controller.blackboard[hunting_target_key]
|
||||
|
||||
if(QDELETED(hunted))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
target_caught(hunter, hunted)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/hunt_target/proc/target_caught(mob/living/hunter, atom/hunted)
|
||||
if(isliving(hunted)) // Are we hunting a living mob?
|
||||
var/mob/living/living_target = hunted
|
||||
hunter.manual_emote("chomps [living_target]!")
|
||||
living_target.investigate_log("has been killed by [key_name(hunter)].", INVESTIGATE_DEATHS)
|
||||
living_target.death()
|
||||
|
||||
else if(isfood(hunted))
|
||||
hunted.attack_animal(hunter)
|
||||
|
||||
else // We're hunting an object, and should delete it instead of killing it. Mostly useful for decal bugs like ants or spider webs.
|
||||
hunter.manual_emote("chomps [hunted]!")
|
||||
qdel(hunted)
|
||||
|
||||
/datum/ai_behavior/hunt_target/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
|
||||
. = ..()
|
||||
if(succeeded && hunting_cooldown_key)
|
||||
controller.set_blackboard_key(hunting_cooldown_key, world.time + hunt_cooldown)
|
||||
else if(hunting_target_key)
|
||||
controller.clear_blackboard_key(hunting_target_key)
|
||||
if(always_reset_target && hunting_target_key)
|
||||
controller.clear_blackboard_key(hunting_target_key)
|
||||
|
||||
/datum/ai_behavior/hunt_target/interact_with_target
|
||||
/// Which intent should we use to interact with
|
||||
var/behavior_intent = INTENT_HARM
|
||||
|
||||
/datum/ai_behavior/hunt_target/interact_with_target/target_caught(mob/living/hunter, obj/structure/cable/hunted)
|
||||
var/datum/ai_controller/controller = hunter.ai_controller
|
||||
controller.ai_interact(target = hunted, intent = behavior_intent)
|
||||
@@ -29,3 +29,15 @@
|
||||
var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths)
|
||||
if(length(found))
|
||||
return pick(found)
|
||||
|
||||
/datum/ai_behavior/find_and_set/in_list/turf_types
|
||||
|
||||
/datum/ai_behavior/find_and_set/in_list/turf_types/search_tactic(datum/ai_controller/controller, locate_paths, search_range)
|
||||
var/list/found = RANGE_TURFS(search_range, controller.pawn)
|
||||
shuffle_inplace(found)
|
||||
for(var/turf/possible_turf as anything in found)
|
||||
if(!is_type_in_typecache(possible_turf, locate_paths))
|
||||
continue
|
||||
if(can_see(controller.pawn, possible_turf, search_range))
|
||||
return possible_turf
|
||||
return null
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/// Come to a complete stop for a set amount of time.
|
||||
/datum/ai_movement/complete_stop
|
||||
max_pathing_attempts = INFINITY // path all you want, you can not escape your fate
|
||||
|
||||
/datum/ai_movement/complete_stop/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance)
|
||||
. = ..()
|
||||
var/atom/movable/moving = controller.pawn
|
||||
var/stopping_time = controller.blackboard[BB_STATIONARY_SECONDS]
|
||||
var/delay_time = (stopping_time * 0.5) // no real reason to fire any more often than this really
|
||||
// assume that the current_movement_target is our location
|
||||
var/datum/move_loop/loop = GLOB.move_manager.freeze(moving, current_movement_target, delay = delay_time, timeout = stopping_time, subsystem = SSai_movement, extra_info = controller)
|
||||
RegisterSignal(loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(pre_move))
|
||||
|
||||
/datum/ai_movement/complete_stop/allowed_to_move(datum/move_loop/source)
|
||||
return FALSE
|
||||
@@ -45,17 +45,19 @@
|
||||
desc = "A crate designed for safe transport of animals. The contents are a mystery."
|
||||
|
||||
/obj/structure/closet/critter/random/populate_contents()
|
||||
content_mob = pick(/mob/living/simple_animal/pet/dog/corgi,
|
||||
/mob/living/simple_animal/pet/dog/corgi/lisa,
|
||||
content_mob = pick(
|
||||
/mob/living/basic/cow,
|
||||
/mob/living/basic/deer,
|
||||
/mob/living/basic/pig,
|
||||
|
||||
/mob/living/simple_animal/pet/dog/corgi,
|
||||
/mob/living/simple_animal/pet/dog/corgi/lisa,
|
||||
/mob/living/simple_animal/hostile/retaliate/goat,
|
||||
/mob/living/simple_animal/turkey,
|
||||
/mob/living/simple_animal/chick,
|
||||
/mob/living/simple_animal/pet/cat,
|
||||
/mob/living/simple_animal/pet/dog/pug,
|
||||
/mob/living/simple_animal/pet/dog/fox,
|
||||
/mob/living/simple_animal/deer,
|
||||
/mob/living/simple_animal/bunny)
|
||||
|
||||
/obj/structure/closet/critter/corgi
|
||||
@@ -115,7 +117,7 @@
|
||||
|
||||
/obj/structure/closet/critter/deer
|
||||
name = "deer crate"
|
||||
content_mob = /mob/living/simple_animal/deer
|
||||
content_mob = /mob/living/basic/deer
|
||||
|
||||
/obj/structure/closet/critter/bunny
|
||||
name = "bunny crate"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/mob/living/basic/deer
|
||||
name = "deer"
|
||||
desc = "A strong, brave deer."
|
||||
icon_state = "deer"
|
||||
icon_living = "deer"
|
||||
icon_dead = "deer_dead"
|
||||
faction = list("neutral", "jungle")
|
||||
see_in_dark = 0 //I'm so funny
|
||||
butcher_results = list(/obj/item/food/meat = 4)
|
||||
mob_biotypes = MOB_ORGANIC | MOB_BEAST
|
||||
gold_core_spawnable = FRIENDLY_SPAWN
|
||||
ai_controller = /datum/ai_controller/basic_controller/deer
|
||||
/// Things that will scare us into being stationary. Vehicles are scary to deers because they might have headlights.
|
||||
var/static/list/stationary_scary_things = list(
|
||||
/obj/vehicle,
|
||||
/obj/tgvehicle,
|
||||
)
|
||||
|
||||
/mob/living/basic/deer/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/wears_collar)
|
||||
AddElement(/datum/element/ai_retaliate)
|
||||
AddComponent(/datum/component/footstep, FOOTSTEP_MOB_SHOE)
|
||||
var/time_to_freeze_for = (rand(5, 10) SECONDS)
|
||||
ai_controller.set_blackboard_key(BB_STATIONARY_SECONDS, time_to_freeze_for)
|
||||
ai_controller.set_blackboard_key(BB_STATIONARY_COOLDOWN, (time_to_freeze_for * (rand(3, 5))))
|
||||
ai_controller.set_blackboard_key(BB_STATIONARY_TARGETS, typecacheof(stationary_scary_things))
|
||||
@@ -0,0 +1,147 @@
|
||||
/datum/ai_planning_subtree/random_speech/deer
|
||||
speech_chance = 2
|
||||
speak_verbs = list("grunts", "snorts", "bleats")
|
||||
emote_hear = list("grunts.", "snorts.", "bleats.")
|
||||
emote_see = list("shakes its head.")
|
||||
|
||||
/datum/ai_controller/basic_controller/deer
|
||||
blackboard = list(
|
||||
BB_STATIONARY_MOVE_TO_TARGET = TRUE,
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
)
|
||||
ai_traits = AI_FLAG_STOP_MOVING_WHEN_PULLED
|
||||
ai_movement = /datum/ai_movement/basic_avoidance
|
||||
idle_behavior = /datum/idle_behavior/idle_random_walk
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/random_speech/deer,
|
||||
/datum/ai_planning_subtree/stare_at_thing,
|
||||
/datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee,
|
||||
/datum/ai_planning_subtree/flee_target,
|
||||
/datum/ai_planning_subtree/rest_at_home,
|
||||
/datum/ai_planning_subtree/play_with_friends,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/mark_territory,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/graze,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/drink_water,
|
||||
)
|
||||
|
||||
/// subtree to go around drinking water
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/drink_water
|
||||
target_key = BB_DEER_WATER_TARGET
|
||||
finding_behavior = /datum/ai_behavior/find_and_set/in_list/turf_types
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/drink_water
|
||||
hunt_targets = list(
|
||||
/turf/simulated/floor/beach/away/water,
|
||||
)
|
||||
hunt_range = 7
|
||||
hunt_chance = 5
|
||||
|
||||
/datum/ai_behavior/hunt_target/drink_water
|
||||
always_reset_target = TRUE
|
||||
hunt_cooldown = 20 SECONDS
|
||||
|
||||
/datum/ai_behavior/hunt_target/drink_water/target_caught(mob/living/hunter, atom/hunted)
|
||||
var/static/list/possible_emotes = list("drinks the water!", "dances in the water!", "splashes around happily!")
|
||||
hunter.custom_emote(EMOTE_VISIBLE, pick(possible_emotes))
|
||||
|
||||
/// subtree to go around grazing
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/graze
|
||||
target_key = BB_DEER_GRASS_TARGET
|
||||
finding_behavior = /datum/ai_behavior/find_and_set/in_list/turf_types
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/eat_grass
|
||||
hunt_targets = list(
|
||||
/turf/simulated/floor/grass,
|
||||
)
|
||||
hunt_range = 7
|
||||
hunt_chance = 45
|
||||
|
||||
/datum/ai_behavior/hunt_target/eat_grass
|
||||
always_reset_target = TRUE
|
||||
hunt_cooldown = 15 SECONDS
|
||||
|
||||
/datum/ai_behavior/hunt_target/eat_grass/target_caught(mob/living/hunter, atom/hunted)
|
||||
var/static/list/possible_emotes = list("eats the grass!", "munches down the grass!", "chews on the grass!")
|
||||
hunter.custom_emote(EMOTE_VISIBLE, pick(possible_emotes))
|
||||
|
||||
/// subtree to go around playing with other deers
|
||||
/datum/ai_planning_subtree/play_with_friends
|
||||
|
||||
/datum/ai_planning_subtree/play_with_friends/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/static/list/emote_list = list("plays with", "dances with", "celebrates with")
|
||||
var/static/list/friend_types = typecacheof(list(/mob/living/basic/deer))
|
||||
if(controller.blackboard_key_exists(BB_DEER_PLAYFRIEND))
|
||||
controller.queue_behavior(/datum/ai_behavior/emote_on_target, BB_DEER_PLAYFRIEND, emote_list)
|
||||
if(SPT_PROB(3, seconds_per_tick))
|
||||
controller.queue_behavior(/datum/ai_behavior/find_hunt_target/valid_deer, BB_DEER_PLAYFRIEND, friend_types)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/datum/ai_behavior/emote_on_target/deer_play
|
||||
|
||||
/datum/ai_behavior/emote_on_target/deer_play/run_emote(mob/living/living_pawn, atom/target, list/emote_list)
|
||||
. = ..()
|
||||
living_pawn.spin(spintime = 4, speed = 1)
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/valid_deer/valid_dinner(mob/living/source, mob/living/deer, radius, datum/ai_controller/controller, seconds_per_tick)
|
||||
if(deer.stat == DEAD)
|
||||
return FALSE
|
||||
if(!can_see(source, deer, radius))
|
||||
return FALSE
|
||||
deer.ai_controller?.set_blackboard_key(BB_DEER_PLAYFRIEND, source)
|
||||
return can_see(source, deer, radius)
|
||||
|
||||
/// subtree to mark trees as territories
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/mark_territory
|
||||
target_key = BB_DEER_TREE_TARGET
|
||||
finding_behavior = /datum/ai_behavior/find_hunt_target
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/mark_territory
|
||||
hunt_targets = list(/obj/structure/flora/tree)
|
||||
hunt_range = 7
|
||||
hunt_chance = 75
|
||||
|
||||
/datum/ai_behavior/hunt_target/mark_territory
|
||||
always_reset_target = TRUE
|
||||
hunt_cooldown = 15 SECONDS
|
||||
|
||||
/datum/ai_behavior/hunt_target/mark_territory/target_caught(mob/living/hunter, atom/hunted)
|
||||
hunter.custom_emote(EMOTE_VISIBLE, "marks [hunted] with its hooves!")
|
||||
hunter.ai_controller.set_blackboard_key(BB_DEER_TREEHOME, hunted)
|
||||
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/mark_territory/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard_key_exists(BB_DEER_TREEHOME)) // already found our home, abort!
|
||||
return
|
||||
return ..()
|
||||
|
||||
/datum/ai_planning_subtree/rest_at_home/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard[BB_DEER_RESTING] > world.time) // we're resting for now, nothing more to do
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
if(!controller.blackboard_key_exists(BB_DEER_TREEHOME) || controller.blackboard[BB_DEER_NEXT_REST_TIMER] > world.time)
|
||||
return
|
||||
controller.queue_behavior(/datum/ai_behavior/return_home, BB_DEER_TREEHOME)
|
||||
|
||||
/datum/ai_behavior/return_home
|
||||
required_distance = 0
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
/// minimum time till next rest
|
||||
var/minimum_time = 2 MINUTES
|
||||
/// maximum time till next rest
|
||||
var/maximum_time = 4 MINUTES
|
||||
|
||||
/datum/ai_behavior/return_home/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
var/list/possible_turfs = get_adjacent_open_turfs(target)
|
||||
shuffle_inplace(possible_turfs)
|
||||
for(var/turf/possible_turf as anything in possible_turfs)
|
||||
if(!is_blocked_turf(possible_turf))
|
||||
set_movement_target(controller, possible_turf)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/ai_behavior/return_home/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
var/static/list/possible_emotes = list("rests its legs...", "yawns and naps...", "curls up and rests...")
|
||||
living_pawn.custom_emote(EMOTE_VISIBLE, pick(possible_emotes))
|
||||
controller.set_blackboard_key(BB_DEER_RESTING, world.time + 15 SECONDS)
|
||||
controller.set_blackboard_key(BB_DEER_NEXT_REST_TIMER, world.time + rand(minimum_time, maximum_time))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
@@ -1,23 +0,0 @@
|
||||
/mob/living/simple_animal/deer
|
||||
name = "deer"
|
||||
desc = "A strong, brave deer."
|
||||
icon_state = "deer"
|
||||
icon_living = "deer"
|
||||
icon_dead = "deer_dead"
|
||||
speak = list("snorts")
|
||||
speak_emote = list("snorts")
|
||||
emote_see = list("shakes its head")
|
||||
faction = list("neutral", "jungle")
|
||||
speak_chance = 1
|
||||
turns_per_move = 5
|
||||
see_in_dark = 0 //I'm so funny
|
||||
butcher_results = list(/obj/item/food/meat = 4)
|
||||
response_help = "pets"
|
||||
response_disarm = "gently pushes aside"
|
||||
response_harm = "kicks"
|
||||
mob_biotypes = MOB_ORGANIC | MOB_BEAST
|
||||
gold_core_spawnable = FRIENDLY_SPAWN
|
||||
|
||||
/mob/living/simple_animal/deer/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/wears_collar)
|
||||
+10
-3
@@ -41,6 +41,7 @@
|
||||
#include "code\__DEFINES\asset_defines.dm"
|
||||
#include "code\__DEFINES\atmospherics_defines.dm"
|
||||
#include "code\__DEFINES\atom_states.dm"
|
||||
#include "code\__DEFINES\basic_mob_defines.dm"
|
||||
#include "code\__DEFINES\basic_mob_flags.dm"
|
||||
#include "code\__DEFINES\bio_chip_defines.dm"
|
||||
#include "code\__DEFINES\bots.dm"
|
||||
@@ -450,9 +451,11 @@
|
||||
#include "code\datums\ai\ai_planning_subtree.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_controller.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\basic_attacking.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\emote_with_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\interact_with_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\nearest_targeting.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\run_away_from_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\stop_and_stare.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\targeting.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\find_food.dm"
|
||||
@@ -460,14 +463,17 @@
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\random_speech.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_nearest_target_to_flee.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\stare_at_thing.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\target_retaliate.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\tip_reaction.dm"
|
||||
#include "code\datums\ai\basic_mobs\hunting_behavior\hunting_behaviors.dm"
|
||||
#include "code\datums\ai\generic\find_and_set.dm"
|
||||
#include "code\datums\ai\generic\generic_behaviors.dm"
|
||||
#include "code\datums\ai\idle_behaviors\idle_behavior.dm"
|
||||
#include "code\datums\ai\idle_behaviors\idle_random_walk.dm"
|
||||
#include "code\datums\ai\movement\ai_movement.dm"
|
||||
#include "code\datums\ai\movement\basic_avoidance.dm"
|
||||
#include "code\datums\ai\movement\complete_stop.dm"
|
||||
#include "code\datums\ai\targeting_strategy\basic_targeting_strategy.dm"
|
||||
#include "code\datums\ai\targeting_strategy\targeting_strategy.dm"
|
||||
#include "code\datums\cache\air_alarm.dm"
|
||||
@@ -2351,8 +2357,10 @@
|
||||
#include "code\modules\mob\living\stat_states.dm"
|
||||
#include "code\modules\mob\living\taste.dm"
|
||||
#include "code\modules\mob\living\basic\basic_mob.dm"
|
||||
#include "code\modules\mob\living\basic\cow.dm"
|
||||
#include "code\modules\mob\living\basic\pig.dm"
|
||||
#include "code\modules\mob\living\basic\farm_animals\cow.dm"
|
||||
#include "code\modules\mob\living\basic\farm_animals\deer.dm"
|
||||
#include "code\modules\mob\living\basic\farm_animals\deer_ai.dm"
|
||||
#include "code\modules\mob\living\basic\farm_animals\pig.dm"
|
||||
#include "code\modules\mob\living\brain\brain_death.dm"
|
||||
#include "code\modules\mob\living\brain\brain_emote.dm"
|
||||
#include "code\modules\mob\living\brain\brain_life.dm"
|
||||
@@ -2533,7 +2541,6 @@
|
||||
#include "code\modules\mob\living\simple_animal\friendly\cockroach.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\corgi_stripping.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\crab.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\deer.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\diona_nymph.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\dog.dm"
|
||||
#include "code\modules\mob\living\simple_animal\friendly\farm_animals.dm"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/mob/living/simple_animal/deer/@SUBTYPES : /mob/living/basic/deer/@SUBTYPES{@OLD}
|
||||
Reference in New Issue
Block a user