mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-24 04:27:35 +01:00
port minebots to basic mobs and add some behavior (#29319)
* port minebots to basic mobs and add some behavior * remove unused define * update is_blocked_turf usage * fix radio, goldgrub, and armor upgrade * standardize blackboard names * Apply suggestions from code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: warriorstar-orion <orion@snowfrost.garden> * lewc review * whoops * 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: Luc <89928798+lewcc@users.noreply.github.com> Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
This commit is contained in:
co-authored by
Luc
PollardTheDragon
parent
586e2e6c4d
commit
e63415a483
@@ -124,7 +124,7 @@
|
||||
var/turf/picked_turf = get_closest_atom(/turf, possible_turfs, target)
|
||||
step(living_pawn, get_dir(living_pawn, picked_turf))
|
||||
|
||||
/datum/ai_behavior/basic_ranged_attack/proc/calculate_trajectory(mob/living/source , atom/target)
|
||||
/datum/ai_behavior/basic_ranged_attack/proc/calculate_trajectory(mob/living/source, atom/target)
|
||||
var/list/turf_list = get_line(source, target)
|
||||
var/list_length = length(turf_list) - 1
|
||||
for(var/i in 1 to list_length)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
///behavior to befriend any targets
|
||||
/datum/ai_behavior/befriend_target
|
||||
|
||||
/datum/ai_behavior/befriend_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, befriend_message)
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
var/mob/living/living_target = controller.blackboard[target_key]
|
||||
if(QDELETED(living_target))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
living_pawn.befriend(living_target)
|
||||
var/befriend_text = controller.blackboard[befriend_message]
|
||||
if(befriend_text)
|
||||
to_chat(living_target, "<span class='notice'>[living_pawn] [befriend_text]</span>")
|
||||
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/befriend_target/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||
. = ..()
|
||||
controller.clear_blackboard_key(target_key)
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* # Travel Towards
|
||||
* Moves towards the atom in the passed blackboard key.
|
||||
* Planning continues during this action so it can be interrupted by higher priority actions.
|
||||
*/
|
||||
/datum/ai_behavior/travel_towards
|
||||
required_distance = 0
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
/// If true we will get rid of our target on completion
|
||||
var/clear_target = FALSE
|
||||
/// If desired, an alternate [/datum/ai_movement] to use for movement/pathfinding.
|
||||
var/new_movement_type
|
||||
|
||||
/datum/ai_behavior/travel_towards/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target, new_movement_type)
|
||||
|
||||
/datum/ai_behavior/travel_towards/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/travel_towards/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||
. = ..()
|
||||
if(clear_target)
|
||||
controller.clear_blackboard_key(target_key)
|
||||
if(new_movement_type)
|
||||
controller.change_ai_movement_type(initial(controller.ai_movement))
|
||||
|
||||
/datum/ai_behavior/travel_towards/stop_on_arrival
|
||||
clear_target = TRUE
|
||||
|
||||
/datum/ai_behavior/travel_towards/adjacent
|
||||
required_distance = 1
|
||||
|
||||
/**
|
||||
* # Travel Towards Atom
|
||||
* Travel towards an atom you pass directly from the controller rather than a blackboard key.
|
||||
* You might need to do this to avoid repeating some checks in both a controller and an action.
|
||||
*/
|
||||
/datum/ai_behavior/travel_towards_atom
|
||||
required_distance = 0
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
|
||||
|
||||
/datum/ai_behavior/travel_towards_atom/setup(datum/ai_controller/controller, atom/target_atom)
|
||||
. = ..()
|
||||
if(isnull(target_atom))
|
||||
return FALSE
|
||||
set_movement_target(controller, target_atom)
|
||||
|
||||
/datum/ai_behavior/travel_towards_atom/perform(seconds_per_tick, datum/ai_controller/controller, atom/target_atom)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/ai_behavior/find_mineral_wall
|
||||
|
||||
/datum/ai_behavior/find_mineral_wall/perform(seconds_per_tick, datum/ai_controller/controller, found_wall_key)
|
||||
var/mob/living_pawn = controller.pawn
|
||||
|
||||
for(var/turf/simulated/mineral/potential_wall in oview(9, living_pawn))
|
||||
if(!check_if_mineable(controller, potential_wall)) // check if its surrounded by walls
|
||||
continue
|
||||
controller.set_blackboard_key(found_wall_key, potential_wall) // closest wall first!
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
/// Check to see if we can get to and mine the turf the mineral is in
|
||||
/datum/ai_behavior/find_mineral_wall/proc/check_if_mineable(datum/ai_controller/controller, turf/target_wall)
|
||||
var/mob/living/source = controller.pawn
|
||||
var/direction_to_turf = get_dir(target_wall, source)
|
||||
if(!IS_DIR_DIAGONAL(direction_to_turf))
|
||||
return TRUE
|
||||
var/list/directions_to_check = list()
|
||||
for(var/direction_check in GLOB.cardinal)
|
||||
if(direction_check & direction_to_turf)
|
||||
directions_to_check += direction_check
|
||||
|
||||
for(var/direction in directions_to_check)
|
||||
var/turf/test_turf = get_step(target_wall, direction)
|
||||
if(isnull(test_turf))
|
||||
continue
|
||||
if(!test_turf.is_blocked_turf(source_atom = source))
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -11,3 +11,14 @@
|
||||
controller.queue_behavior(melee_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
|
||||
if(end_planning)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING // we are going into battle...no distractions.
|
||||
|
||||
/datum/ai_planning_subtree/basic_ranged_attack_subtree
|
||||
operational_datums = list(/datum/component/ranged_attacks)
|
||||
var/datum/ai_behavior/basic_ranged_attack/ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack
|
||||
|
||||
/datum/ai_planning_subtree/basic_ranged_attack_subtree/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
. = ..()
|
||||
if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))
|
||||
return
|
||||
controller.queue_behavior(ranged_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions.
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/ai_planning_subtree/simple_find_target
|
||||
/// Variable to store target in
|
||||
var/target_key = BB_BASIC_MOB_CURRENT_TARGET
|
||||
|
||||
/datum/ai_planning_subtree/simple_find_target/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
. = ..()
|
||||
controller.queue_behavior(/datum/ai_behavior/find_potential_targets, target_key, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
|
||||
|
||||
// Prevents finding a target if a human is nearby
|
||||
/datum/ai_planning_subtree/simple_find_target/not_while_observed
|
||||
|
||||
/datum/ai_planning_subtree/simple_find_target/not_while_observed/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
for(var/mob/living/carbon/human/watcher in hearers(11, controller.pawn))
|
||||
if(watcher.stat != DEAD)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/datum/ai_planning_subtree/simple_find_target/to_flee
|
||||
target_key = BB_BASIC_MOB_FLEE_TARGET
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Simple behaviours which simply try to use an ability whenever it is available.
|
||||
* For something which wants a target try `targeted_mob_ability`.
|
||||
*/
|
||||
/datum/ai_planning_subtree/use_mob_ability
|
||||
/// Blackboard key for the ability
|
||||
var/ability_key = BB_GENERIC_ACTION
|
||||
/// Behaviour to perform using ability
|
||||
var/use_ability_behavior = /datum/ai_behavior/use_mob_ability
|
||||
/// If true we terminate planning after trying to use the ability.
|
||||
var/finish_planning = FALSE
|
||||
|
||||
/datum/ai_planning_subtree/use_mob_ability/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(!ability_key)
|
||||
CRASH("You forgot to tell this mob where to find its ability")
|
||||
|
||||
var/datum/action/using_action = controller.blackboard[ability_key]
|
||||
if(!using_action?.IsAvailable())
|
||||
return
|
||||
|
||||
controller.queue_behavior(use_ability_behavior, ability_key)
|
||||
if(finish_planning)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/datum/ai_behavior/use_mob_ability
|
||||
|
||||
/datum/ai_behavior/use_mob_ability/perform(seconds_per_tick, datum/ai_controller/controller, ability_key)
|
||||
var/datum/action/using_action = controller.blackboard[ability_key]
|
||||
if(QDELETED(using_action))
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
if(using_action.Trigger())
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores
|
||||
target_key = BB_ORE_TARGET
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/hunt_ores
|
||||
finding_behavior = /datum/ai_behavior/find_hunt_target/hunt_ores
|
||||
hunt_targets = list(/obj/item/stack/ore)
|
||||
hunt_chance = 90
|
||||
hunt_range = 9
|
||||
@@ -0,0 +1,39 @@
|
||||
/// 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/hunt_ores
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/hunt_ores/valid_dinner(mob/living/basic/source, obj/item/stack/ore/target, radius, datum/ai_controller/controller)
|
||||
var/list/forbidden_ore = controller.blackboard[BB_ORE_IGNORE_TYPES]
|
||||
|
||||
if(is_type_in_list(target, forbidden_ore))
|
||||
return FALSE
|
||||
|
||||
if(!isturf(target.loc))
|
||||
return FALSE
|
||||
|
||||
var/obj/item/pet_target = controller.blackboard[BB_CURRENT_PET_TARGET]
|
||||
if(target == pet_target) // we are currently fetching this ore for master, dont eat it!
|
||||
return FALSE
|
||||
|
||||
return can_see(source, target, radius)
|
||||
@@ -0,0 +1,63 @@
|
||||
/// 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)
|
||||
add_attack_logs(hunter, hunted, "AI controller killed", ATKLOG_ALL)
|
||||
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, atom/hunted)
|
||||
var/datum/ai_controller/controller = hunter.ai_controller
|
||||
controller.ai_interact(target = hunted, intent = behavior_intent)
|
||||
|
||||
/datum/ai_behavior/hunt_target/interact_with_target/hunt_ores
|
||||
always_reset_target = TRUE
|
||||
|
||||
/datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot
|
||||
always_reset_target = TRUE
|
||||
hunt_cooldown = 2 SECONDS
|
||||
behavior_intent = INTENT_HELP
|
||||
@@ -1,125 +0,0 @@
|
||||
/**
|
||||
* 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)
|
||||
Reference in New Issue
Block a user