Files

364 lines
16 KiB
Plaintext

/datum/ai_controller/basic_controller/minebot
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends,
BB_BASIC_MOB_FLEE_DISTANCE = 3,
BB_MINIMUM_SHOOTING_DISTANCE = 3,
BB_MINEBOT_PLANT_MINES = TRUE,
BB_MINEBOT_REPAIR_DRONE = TRUE,
BB_MINEBOT_AUTO_DEFEND = TRUE,
BB_BLACKLIST_MINERAL_TURFS = list(/turf/closed/mineral/gibtonite),
BB_AUTOMATED_MINING = FALSE,
BB_OWNER_SELF_HARM_RESPONSES = list(
"Please stop hurting yourself.",
"There is no need to do that.",
"Your actions are illogical.",
"Please make better choices.",
"Remember, you have beaten your worst days before."
)
)
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/launch_missiles,
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/befriend_miners,
/datum/ai_planning_subtree/defend_node,
/datum/ai_planning_subtree/minebot_maintain_distance,
/datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot,
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot,
/datum/ai_planning_subtree/minebot_mining,
/datum/ai_planning_subtree/locate_dead_humans,
)
/datum/ai_planning_subtree/launch_missiles
/datum/ai_planning_subtree/launch_missiles/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/action/cooldown/missile_ability = controller.blackboard[BB_MINEBOT_MISSILE_ABILITY]
if(!missile_ability?.IsAvailable())
return
if(!controller.blackboard_key_exists(BB_MINEBOT_MISSILE_TARGET))
controller.queue_behavior(/datum/ai_behavior/find_and_set/clear_bombing_zone, BB_MINEBOT_MISSILE_TARGET, /obj/effect/temp_visual/minebot_target, 7)
return
controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target, BB_MINEBOT_MISSILE_ABILITY, BB_MINEBOT_MISSILE_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/ai_behavior/find_and_set/clear_bombing_zone
/datum/ai_behavior/find_and_set/clear_bombing_zone/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
for(var/obj/effect/temp_visual/minebot_target/target in oview(search_range, controller.pawn))
if(isclosedturf(get_turf(target)))
continue
return target
return null
/datum/ai_planning_subtree/befriend_miners/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(!controller.blackboard_key_exists(BB_MINER_FRIEND))
controller.queue_behavior(/datum/ai_behavior/find_and_set/miner_to_befriend, BB_MINER_FRIEND)
return
controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_MINER_FRIEND)
/datum/ai_behavior/find_and_set/miner_to_befriend
/datum/ai_behavior/find_and_set/miner_to_befriend/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn))
if(HAS_TRAIT(target, TRAIT_ROCK_STONER))
return target
return null
/datum/ai_planning_subtree/defend_node/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/target = controller.blackboard[BB_DRONE_DEFEND]
if(QDELETED(target))
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_DRONE_DEFEND, /mob/living/basic/node_drone)
return
var/mob/living/living_pawn = controller.pawn
if(!living_pawn.has_ally(target))
controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_DRONE_DEFEND)
return
if(target.health < (target.maxHealth * 0.75) && controller.blackboard[BB_MINEBOT_REPAIR_DRONE])
controller.queue_behavior(/datum/ai_behavior/repair_drone, BB_DRONE_DEFEND)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/ai_behavior/repair_drone
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH
/datum/ai_behavior/repair_drone/setup(datum/ai_controller/controller, target_key)
. = ..()
var/turf/target = controller.blackboard[target_key]
if(isnull(target))
return FALSE
set_movement_target(controller, target)
/datum/ai_behavior/repair_drone/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/mob/living/living_pawn = controller.pawn
living_pawn.say("REPAIRING [target]!")
controller.ai_interact(target = target)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/repair_drone/finish_action(datum/ai_controller/controller, success, target_key)
. = ..()
if(!success)
controller.clear_blackboard_key(target_key)
///find dead humans and report their location on the radio
/datum/ai_planning_subtree/locate_dead_humans/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(controller.blackboard_key_exists(BB_NEARBY_DEAD_MINER))
controller.queue_behavior(/datum/ai_behavior/send_sos_message, BB_NEARBY_DEAD_MINER)
return SUBTREE_RETURN_FINISH_PLANNING
controller.queue_behavior(/datum/ai_behavior/find_and_set/unconscious_human, BB_NEARBY_DEAD_MINER, /mob/living/carbon/human)
/datum/ai_behavior/find_and_set/unconscious_human/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn))
if(target.stat >= UNCONSCIOUS && target.mind)
return target
return null
/datum/ai_behavior/send_sos_message
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
action_cooldown = 2 MINUTES
/datum/ai_behavior/send_sos_message/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
var/mob/living/carbon/target = controller.blackboard[target_key]
var/mob/living/living_pawn = controller.pawn
if(QDELETED(target) || is_station_level(target.z))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/turf/target_turf = get_turf(target)
var/obj/item/implant/radio/radio_implant = locate(/obj/item/implant/radio) in living_pawn.contents
if(!radio_implant)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/message = "ALERT, [target] in need of help at coordinates: [target_turf.x], [target_turf.y], [target_turf.z]!"
radio_implant.radio.talk_into(living_pawn, message, RADIO_CHANNEL_SUPPLY)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/send_sos_message/finish_action(datum/ai_controller/controller, success, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
///operational datums is null because we dont use a ranged component, we use a gun in our contents
/datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot
operational_datums = null
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/minebot
/datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if(QDELETED(target))
return
var/mob/living/living_pawn = controller.pawn
if(!living_pawn.combat_mode) //we are not on attack mode
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
/datum/ai_planning_subtree/minebot_maintain_distance/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if(QDELETED(target))
return
var/mob/living/living_pawn = controller.pawn
if(get_dist(living_pawn, target) <= controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE])
controller.queue_behavior(/datum/ai_behavior/run_away_from_target/run_and_shoot/minebot, BB_BASIC_MOB_CURRENT_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/ai_behavior/run_away_from_target/run_and_shoot/minebot
/datum/ai_behavior/run_away_from_target/run_and_shoot/minebot/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hiding_location_key)
if(!controller.blackboard[BB_MINEBOT_PLANT_MINES])
return ..()
var/datum/action/cooldown/mine_ability = controller.blackboard[BB_MINEBOT_LANDMINE_ABILITY]
mine_ability?.Trigger()
return ..()
/datum/ai_behavior/basic_ranged_attack/minebot
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
avoid_friendly_fire = TRUE
///if our target is closer than this distance, finish action
var/minimum_distance = 3
/datum/ai_behavior/basic_ranged_attack/minebot/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key)
. = ..()
minimum_distance = controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE] ? controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE] : initial(minimum_distance)
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/mob/living/living_pawn = controller.pawn
if(get_dist(living_pawn, target) <= minimum_distance)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
///mine walls if we are on automated mining mode
/datum/ai_planning_subtree/minebot_mining/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(!controller.blackboard[BB_AUTOMATED_MINING])
return
if(controller.blackboard_key_exists(BB_TARGET_MINERAL_TURF))
controller.queue_behavior(/datum/ai_behavior/minebot_mine_turf, BB_TARGET_MINERAL_TURF)
return SUBTREE_RETURN_FINISH_PLANNING
controller.queue_behavior(/datum/ai_behavior/find_mineral_wall/minebot, BB_TARGET_MINERAL_TURF)
/datum/ai_behavior/find_mineral_wall/minebot
/datum/ai_behavior/find_mineral_wall/minebot/check_if_mineable(datum/ai_controller/controller, turf/target_wall)
var/list/forbidden_turfs = controller.blackboard[BB_BLACKLIST_MINERAL_TURFS]
var/turf/previous_unreachable_wall = controller.blackboard[BB_PREVIOUS_UNREACHABLE_WALL]
if(is_type_in_list(target_wall, forbidden_turfs) || target_wall == previous_unreachable_wall)
return FALSE
controller.clear_blackboard_key(BB_PREVIOUS_UNREACHABLE_WALL)
return ..()
/datum/ai_behavior/minebot_mine_turf
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
required_distance = 2
action_cooldown = 3 SECONDS
/datum/ai_behavior/minebot_mine_turf/setup(datum/ai_controller/controller, target_key)
. = ..()
var/turf/target = controller.blackboard[target_key]
if(isnull(target))
return FALSE
set_movement_target(controller, target)
/datum/ai_behavior/minebot_mine_turf/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
var/mob/living/basic/living_pawn = controller.pawn
var/turf/target = controller.blackboard[target_key]
if(QDELETED(target))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
if(check_obstacles_in_path(controller, target))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
if(!living_pawn.combat_mode)
living_pawn.set_combat_mode(TRUE)
living_pawn.RangedAttack(target)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/minebot_mine_turf/proc/check_obstacles_in_path(datum/ai_controller/controller, turf/target)
var/mob/living/source = controller.pawn
var/list/turfs_in_path = get_line(source, target) - target
for(var/turf/turf in turfs_in_path)
if(turf.is_blocked_turf(ignore_atoms = list(source)))
controller.set_blackboard_key(BB_PREVIOUS_UNREACHABLE_WALL, target)
return TRUE
return FALSE
/datum/ai_behavior/minebot_mine_turf/finish_action(datum/ai_controller/controller, success, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
///store ores in our body
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot
hunt_chance = 100
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/automated_mining = controller.blackboard[BB_AUTOMATED_MINING]
var/mob/living/living_pawn = controller.pawn
if(!automated_mining && living_pawn.combat_mode) //are we not on automated mining or collect mode?
return
return ..()
/datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot
hunt_cooldown = 2 SECONDS
/datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot/target_caught(mob/living/hunter, obj/item/stack/ore/hunted)
if(hunter.combat_mode)
hunter.set_combat_mode(FALSE)
return ..()
///pet commands
/datum/pet_command/free/minebot
/datum/pet_command/free/minebot/execute_action(datum/ai_controller/controller)
controller.set_blackboard_key(BB_AUTOMATED_MINING, FALSE)
return ..()
/datum/pet_command/automate_mining
command_name = "Automate mining"
command_desc = "Make your minebot automatically mine!"
radial_icon_state = "mine"
speech_commands = list("mine")
callout_type = /datum/callout_option/mine
/datum/pet_command/automate_mining/valid_callout_target(mob/living/speaker, datum/callout_option/callout, atom/target)
return ismineralturf(target)
/datum/pet_command/automate_mining/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to start mining!"
/datum/pet_command/automate_mining/execute_action(datum/ai_controller/controller)
controller.set_blackboard_key(BB_AUTOMATED_MINING, TRUE)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
/datum/pet_command/minebot_ability
command_name = "Minebot ability"
command_desc = "Make your minebot use one of its abilities."
radial_icon = 'icons/mob/actions/actions_mecha.dmi'
///the ability we will use
var/ability_key
/datum/pet_command/minebot_ability/execute_action(datum/ai_controller/controller)
var/datum/action/cooldown/ability = controller.blackboard[ability_key]
if(!ability?.IsAvailable())
return
controller.queue_behavior(/datum/ai_behavior/use_mob_ability, ability_key)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/pet_command/minebot_ability/light
command_name = "Toggle lights"
command_desc = "Make your minebot toggle its lights."
speech_commands = list("light")
radial_icon_state = "mech_lights_off"
ability_key = BB_MINEBOT_LIGHT_ABILITY
/datum/pet_command/minebot_ability/light/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to toggle its lights!"
/datum/pet_command/minebot_ability/dump
command_name = "Dump ore"
command_desc = "Make your minebot dump all its ore!"
speech_commands = list("dump", "ore")
radial_icon_state = "mech_eject"
ability_key = BB_MINEBOT_DUMP_ABILITY
/datum/pet_command/minebot_ability/dump/retrieve_command_text(atom/living_pet, atom/target)
return "signals [living_pet] to dump its ore!"
/datum/pet_command/attack/minebot
attack_behaviour = /datum/ai_behavior/basic_ranged_attack/minebot
/datum/pet_command/attack/minebot/execute_action(datum/ai_controller/controller)
controller.set_blackboard_key(BB_AUTOMATED_MINING, FALSE)
var/mob/living/living_pawn = controller.pawn
if(!living_pawn.combat_mode)
living_pawn.set_combat_mode(TRUE)
return ..()
/datum/pet_command/idle/minebot
/datum/pet_command/idle/minebot/execute_action(datum/ai_controller/controller)
controller.set_blackboard_key(BB_AUTOMATED_MINING, FALSE)
return ..()
/datum/pet_command/protect_owner/minebot
/datum/pet_command/protect_owner/minebot/set_command_target(mob/living/parent, atom/target)
if(!parent.ai_controller.blackboard[BB_MINEBOT_AUTO_DEFEND])
return FALSE
if(!parent.ai_controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) && !QDELETED(target)) //we are already dealing with something,
parent.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target)
return TRUE
/datum/pet_command/protect_owner/minebot/execute_action(datum/ai_controller/controller)
if(controller.blackboard[BB_MINEBOT_AUTO_DEFEND])
var/mob/living/living_pawn = controller.pawn
living_pawn.set_combat_mode(TRUE)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)