mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-13 09:05:11 +01:00
364 lines
16 KiB
Plaintext
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)
|
|
|
|
|