minebot buff (#82001)

## About The Pull Request
this pr buffs non-sentient minebots a bit to make them more helpful with
the new arcmining changes.
Minebots now have a better overall AI, they will maintain distance from
enemies and shoot while running. they will also plant landmines while
theyre running away from enemies. these landmines are carefully
programmed by the bot not to trigger when any of its miner friends step
on it. u no longer need to feed minebots an ore to get them to listen to
you, as they now automatically listen to any miners around.
minebots can now repair damaged node drones 

![image](https://github.com/tgstation/tgstation/assets/138636438/12e67eb2-3711-465c-a3ac-54fdadbed5e4)
they also have a new autodefend feature, which makes them automatically
attack any mob that attacks its miner friends or the drone. They also
have some new upgrades!
First is the regenerative shield, this shield allows minebots to tank a
limited amount of hits before breaking. minebots will then need to wait
sometime before the shield re-activates.
Second is the rocket launcher remote control, this allows players to
direct minebots to fire anti-fauna missiles at their target

https://github.com/tgstation/tgstation/assets/138636438/3ec3605e-8e11-4a31-acaa-1382bed98294

Also minebots are now highly customizable, you can rename them, change
their colors, or program their AI through their new user interface

![image](https://github.com/tgstation/tgstation/assets/138636438/d2e1c39d-f9d2-4da7-a5fa-5a41cea31d6e)


## Why It's Good For The Game
Improves minebot AI a bit, and makes it a more viable option for mining
solo players

## Changelog
🆑
balance: minebots have been buffed and have recieved new upgrades
/🆑
This commit is contained in:
Ben10Omintrix
2024-03-29 22:26:35 -06:00
committed by GitHub
parent edc0f54a59
commit 55267e1334
20 changed files with 871 additions and 26 deletions
+120 -22
View File
@@ -31,13 +31,18 @@
light_on = FALSE
combat_mode = FALSE
ai_controller = /datum/ai_controller/basic_controller/minebot
///the access card we use to access mining
var/obj/item/card/id/access_card
///the gun we use to kill
var/obj/item/gun/energy/recharge/kinetic_accelerator/minebot/stored_gun
///our normal overlay
var/mutable_appearance/neutral_overlay
///our combat mode overlay
var/mutable_appearance/combat_overlay
///our current color, if any
var/selected_color
///the commands our owner can give us
var/list/pet_commands = list(
var/static/list/pet_commands = list(
/datum/pet_command/idle/minebot,
/datum/pet_command/protect_owner/minebot,
/datum/pet_command/minebot_ability/light,
/datum/pet_command/minebot_ability/dump,
/datum/pet_command/automate_mining,
@@ -45,17 +50,27 @@
/datum/pet_command/follow,
/datum/pet_command/point_targeting/attack/minebot,
)
///possible colors the bot can have
var/static/list/possible_colors= list(
"Default" = null, //default color state
"Blue" = "#70d5e7",
"Red" = "#ee7fb9",
"Green" = "#5fea94",
)
/mob/living/basic/mining_drone/Initialize(mapload)
. = ..()
neutral_overlay = mutable_appearance(icon = 'icons/mob/silicon/aibots.dmi', icon_state = "mining_drone_grey")
combat_overlay = mutable_appearance(icon = 'icons/mob/silicon/aibots.dmi', icon_state = "mining_drone_offense_grey")
AddComponent(/datum/component/obeys_commands, pet_commands)
var/static/list/death_drops = list(/obj/effect/decal/cleanable/robot_debris/old)
AddElement(/datum/element/death_drops, death_drops)
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), INNATE_TRAIT)
AddElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE)
AddComponent(/datum/component/tameable, food_types = list(/obj/item/stack/ore), tame_chance = 100, bonus_tame_chance = 5)
var/static/list/innate_actions = list(
/datum/action/cooldown/mob_cooldown/missile_launcher = BB_MINEBOT_MISSILE_ABILITY,
/datum/action/cooldown/mob_cooldown/drop_landmine = BB_MINEBOT_LANDMINE_ABILITY,
/datum/action/cooldown/mob_cooldown/minedrone/toggle_light = BB_MINEBOT_LIGHT_ABILITY,
/datum/action/cooldown/mob_cooldown/minedrone/toggle_meson_vision = null,
/datum/action/cooldown/mob_cooldown/minedrone/dump_ore = BB_MINEBOT_DUMP_ABILITY,
@@ -66,10 +81,11 @@
stored_gun = new(src)
var/obj/item/implant/radio/mining/comms = new(src)
comms.implant(src)
access_card = new /obj/item/card/id/advanced/gold(src)
SSid_access.apply_trim_to_card(access_card, /datum/id_trim/job/shaft_miner)
RegisterSignal(src, COMSIG_MOB_TRIED_ACCESS, PROC_REF(attempt_access))
var/static/list/accesses = list(
/datum/id_trim/job/shaft_miner,
)
AddElement(/datum/element/mob_access, accesses)
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack))
/mob/living/basic/mining_drone/set_combat_mode(new_mode, silent = TRUE)
. = ..()
@@ -115,8 +131,82 @@
return ..()
/mob/living/basic/mining_drone/attack_hand(mob/living/carbon/human/user, list/modifiers)
if(!user.combat_mode)
ui_interact(user)
return
return ..()
/mob/living/basic/mining_drone/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "MineBot", name)
ui.open()
/mob/living/basic/mining_drone/ui_data(mob/user)
var/list/data = list()
data["auto_defend"] = ai_controller.blackboard[BB_MINEBOT_AUTO_DEFEND]
data["repair_node_drone"] = ai_controller.blackboard[BB_MINEBOT_REPAIR_DRONE]
data["plant_mines"] = ai_controller.blackboard[BB_MINEBOT_PLANT_MINES]
data["bot_maintain_distance"] = ai_controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE]
data["bot_name"] = name
data["bot_mode"] = combat_mode
data["bot_health"] = health
data["bot_maxhealth"] = maxHealth
data["bot_color"] = ""
var/color_value = neutral_overlay.color
for(var/index in possible_colors)
if(possible_colors[index] == color_value)
data["bot_color"] = index
break
return data
/mob/living/basic/mining_drone/ui_static_data(mob/user)
var/list/data = list()
data["bot_icon"] = icon2base64(getFlatIcon(src))
data["possible_colors"] = list()
for(var/color in possible_colors)
data["possible_colors"] += list(list(
"color_name" = color,
"color_value" = possible_colors[color],
))
return data
/mob/living/basic/mining_drone/ui_act(action, params, datum/tgui/ui)
. = ..()
switch(action)
if("change_min_distance")
var/new_distance = clamp(params["distance"], 0, 5)
ai_controller.set_blackboard_key(BB_MINIMUM_SHOOTING_DISTANCE, new_distance)
if("toggle_defend")
var/new_toggle = !ai_controller.blackboard[BB_MINEBOT_AUTO_DEFEND]
ai_controller.set_blackboard_key(BB_MINEBOT_AUTO_DEFEND, new_toggle)
if("toggle_repair")
var/new_defend = !ai_controller.blackboard[BB_MINEBOT_REPAIR_DRONE]
ai_controller.set_blackboard_key(BB_MINEBOT_REPAIR_DRONE, new_defend)
if("toggle_mines")
var/new_mines = !ai_controller.blackboard[BB_MINEBOT_PLANT_MINES]
ai_controller.set_blackboard_key(BB_MINEBOT_PLANT_MINES, new_mines)
if("set_name")
var/input_name = sanitize_name(params["chosen_name"], allow_numbers = TRUE)
name = (input_name ? input_name : initial(name))
if("toggle_mode")
set_combat_mode(!combat_mode)
if("set_color")
change_color(params["chosen_color"])
update_static_data(ui.user, ui)
return TRUE
/mob/living/basic/mining_drone/proc/change_color(new_color)
selected_color = new_color
if(!isnull(selected_color))
neutral_overlay.color = selected_color
combat_overlay.color = selected_color
update_appearance()
/mob/living/basic/mining_drone/AltClick(mob/living/user)
. = ..()
if(user.combat_mode)
return ..()
return
set_combat_mode(!combat_mode)
balloon_alert(user, "now [combat_mode ? "attacking wildlife" : "collecting loose ore"]")
@@ -125,7 +215,6 @@
return
stored_gun.afterattack(target, src)
/mob/living/basic/mining_drone/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
. = ..()
@@ -141,16 +230,6 @@
for(var/obj/item/stack/ore/dropped_item in contents)
dropped_item.forceMove(get_turf(src))
/mob/living/basic/mining_drone/proc/attempt_access(mob/drone, obj/door_attempt)
SIGNAL_HANDLER
if(door_attempt.check_access(access_card))
return ACCESS_ALLOWED
return ACCESS_DISALLOWED
/mob/living/basic/mining_drone/tamed(mob/living/tamer, atom/food)
AddComponent(/datum/component/obeys_commands, pet_commands)
/mob/living/basic/mining_drone/death(gibbed)
drop_ore()
@@ -164,6 +243,25 @@
/mob/living/basic/mining_drone/Destroy()
QDEL_NULL(stored_gun)
QDEL_NULL(access_card)
return ..()
/mob/living/basic/mining_drone/proc/pre_attack(datum/source, atom/target)
SIGNAL_HANDLER
if(!istype(target, /mob/living/basic/node_drone))
return NONE
INVOKE_ASYNC(src, PROC_REF(repair_node_drone), target)
return COMPONENT_HOSTILE_NO_ATTACK
/mob/living/basic/mining_drone/proc/repair_node_drone(mob/living/my_target)
do_sparks(5, FALSE, source = my_target)
if(!do_after(src, 6 SECONDS, my_target))
return
my_target.heal_overall_damage(brute = 50)
/mob/living/basic/mining_drone/update_overlays()
. = ..()
if(stat == DEAD || isnull(selected_color))
return
. += combat_mode ? combat_overlay : neutral_overlay
@@ -49,3 +49,131 @@
to_chat(owner, span_notice("You toggle your meson vision [(owner.sight & SEE_TURFS) ? "on" : "off"]."))
/datum/action/cooldown/mob_cooldown/missile_launcher
name = "Launch Missile"
button_icon = 'icons/obj/weapons/guns/projectiles.dmi'
button_icon_state = "84mm-heap"
background_icon_state = "bg_default"
overlay_icon_state = "bg_default_border"
desc = "Launch a missile towards the target!"
cooldown_time = 10 SECONDS
shared_cooldown = NONE
melee_cooldown_time = 0 SECONDS
///how long before we launch said missile
var/wind_up_timer = 1 SECONDS
/datum/action/cooldown/mob_cooldown/missile_launcher/IsAvailable(feedback = TRUE)
if(is_mining_level(owner.z))
return TRUE
if(feedback)
owner.balloon_alert(owner, "cant be used here!")
return FALSE
/datum/action/cooldown/mob_cooldown/missile_launcher/Activate(atom/target)
var/turf/target_turf = get_turf(target)
if(isnull(target_turf) || !can_see(owner, target_turf, 7))
return FALSE
owner.Shake(duration = wind_up_timer)
addtimer(CALLBACK(src, PROC_REF(launch_missile), target_turf), wind_up_timer)
StartCooldown()
return TRUE
/datum/action/cooldown/mob_cooldown/missile_launcher/proc/launch_missile(turf/target_turf)
new /obj/effect/temp_visual/mook_dust(get_turf(owner))
var/obj/effect/temp_visual/rising_rocket/new_rocket = new(get_turf(owner))
addtimer(CALLBACK(src, PROC_REF(drop_missile), target_turf), new_rocket.duration)
/datum/action/cooldown/mob_cooldown/missile_launcher/proc/drop_missile(turf/target_turf)
new /obj/effect/temp_visual/falling_rocket(target_turf)
var/obj/effect/temp_visual/falling_shadow = new /obj/effect/temp_visual/shadow_telegraph(target_turf)
animate(falling_shadow, transform = matrix().Scale(0.1, 0.1), time = falling_shadow.duration)
/datum/action/cooldown/mob_cooldown/drop_landmine
name = "Landmine"
desc = "Drop a landmine!"
button_icon = 'icons/obj/weapons/grenade.dmi'
button_icon_state = "landmine"
background_icon_state = "bg_default"
overlay_icon_state = "bg_default_border"
cooldown_time = 10 SECONDS
shared_cooldown = NONE
melee_cooldown_time = 0 SECONDS
click_to_activate = FALSE
/datum/action/cooldown/mob_cooldown/drop_landmine/IsAvailable(feedback = TRUE)
if(is_mining_level(owner.z))
return TRUE
if(feedback)
owner.balloon_alert(owner, "cant be used here!")
return FALSE
/datum/action/cooldown/mob_cooldown/drop_landmine/Activate(atom/target)
var/turf/my_turf = get_turf(owner)
if(isgroundlessturf(my_turf))
return FALSE
var/obj/effect/mine/minebot/my_mine = new(my_turf)
my_mine.ignore_list = owner.faction.Copy()
playsound(my_turf, 'sound/weapons/armbomb.ogg', 20)
StartCooldown()
return TRUE
/obj/effect/temp_visual/rising_rocket
name = "Missile"
icon = 'icons/obj/weapons/guns/projectiles.dmi'
icon_state = "84mm-heap"
layer = ABOVE_ALL_MOB_LAYER
duration = 2 SECONDS
/obj/effect/temp_visual/rising_rocket/Initialize(mapload)
. = ..()
playsound(src, 'sound/weapons/minebot_rocket.ogg', 100, FALSE)
animate(src, pixel_y = base_pixel_y + 500, time = duration, easing = EASE_IN)
/obj/effect/temp_visual/falling_rocket
name = "Missile"
icon = 'icons/obj/weapons/guns/projectiles.dmi'
icon_state = "84mm-heap"
layer = ABOVE_ALL_MOB_LAYER
duration = 0.7 SECONDS
pixel_y = 60
///the radius of our explosion
var/explosion_radius = 2
///damage of our explosion
var/explosion_damage = 100
/obj/effect/temp_visual/falling_rocket/Initialize(mapload)
. = ..()
transform = transform.Turn(180)
addtimer(CALLBACK(src, PROC_REF(create_explosion)), duration)
animate(src, pixel_y = 0, time = duration)
/obj/effect/temp_visual/falling_rocket/proc/create_explosion()
playsound(src, 'sound/weapons/minebot_rocket.ogg', 100, FALSE)
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(1, holder = src)
smoke.start()
for(var/mob/living/living_target in oview(explosion_radius, src))
if(living_target.incorporeal_move)
continue
living_target.apply_damage(explosion_damage)
/obj/effect/mine/minebot
name = "Landmine"
///we dont detonate if any of these people step on us
var/list/ignore_list = list()
///the damage we apply to whoever steps on us
var/damage_to_apply = 50
/obj/effect/mine/minebot/mineEffect(mob/living/victim)
if(!istype(victim))
return
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(0, holder = src)
smoke.start()
playsound(src, 'sound/effects/explosion3.ogg', 100)
victim.apply_damage(damage_to_apply)
/obj/effect/mine/minebot/can_trigger(atom/movable/on_who)
if(REF(on_who) in ignore_list)
return FALSE
return ..()
@@ -2,6 +2,11 @@
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,
)
@@ -11,11 +16,90 @@
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/pet_planning,
/datum/ai_planning_subtree/befriend_miners,
/datum/ai_planning_subtree/defend_node,
/datum/ai_planning_subtree/launch_missiles,
/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,
)
ai_traits = PAUSE_DURING_DO_AFTER
/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)
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)
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.faction.Find(REF(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))
finish_action(controller, FALSE, target_key)
return
var/mob/living/living_pawn = controller.pawn
living_pawn.say("REPAIRING [target]!")
living_pawn.UnarmedAttack(target)
finish_action(controller, TRUE, target_key)
/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)
@@ -28,6 +112,7 @@
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
@@ -58,16 +143,51 @@
operational_datums = null
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/minebot
/datum/ai_behavior/basic_ranged_attack/minebot
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
avoid_friendly_fire = TRUE
/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))
finish_action(controller, target_key, FALSE)
return
var/mob/living/living_pawn = controller.pawn
if(get_dist(living_pawn, target) <= minimum_distance)
finish_action(controller, target_key, TRUE)
///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])
@@ -216,3 +336,19 @@
/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
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)
/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)
@@ -0,0 +1,72 @@
#define BOMB_COOLDOWN 20 SECONDS
/obj/item/minebot_remote_control
name = "Remote Control"
desc = "Requesting stratagem!"
icon = 'icons/obj/mining.dmi'
icon_state = "minebot_bomb_control"
item_flags = NOBLUDGEON
///are we currently primed to drop a bomb?
var/primed = FALSE
///our last user
var/datum/weakref/last_user
///cooldown till we can drop the next bomb
COOLDOWN_DECLARE(bomb_timer)
/obj/item/minebot_remote_control/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
. = ..()
clear_priming()
/obj/item/minebot_remote_control/proc/clear_priming()
var/mob/living/living_user = last_user?.resolve()
last_user = null
primed = FALSE
if(isnull(living_user))
return
living_user.client?.mouse_override_icon = initial(living_user.client?.mouse_override_icon)
living_user.update_mouse_pointer()
/obj/item/minebot_remote_control/attack_self(mob/user)
. = ..()
if(.)
return .
if(!COOLDOWN_FINISHED(src, bomb_timer))
balloon_alert(user, "on cooldown!")
return TRUE
prime_bomb(user)
return TRUE
/obj/item/minebot_remote_control/proc/prime_bomb(mob/user)
primed = TRUE
last_user = WEAKREF(user)
user.client?.mouse_override_icon = 'icons/effects/mouse_pointers/weapon_pointer.dmi'
user.update_mouse_pointer()
/obj/item/minebot_remote_control/afterattack(atom/attacked_atom, mob/living/user, proximity)
. = ..()
. |= AFTERATTACK_PROCESSED_ITEM
if(!primed)
user.balloon_alert(user, "not primed!")
return
var/turf/target_turf = get_turf(attacked_atom)
if(isnull(target_turf) || isclosedturf(target_turf) || isgroundlessturf(target_turf))
user.balloon_alert(user, "invalid target!")
return
playsound(src, 'sound/machines/beep.ogg', 30)
clear_priming()
new /obj/effect/temp_visual/minebot_target(target_turf)
COOLDOWN_START(src, bomb_timer, BOMB_COOLDOWN)
/obj/effect/temp_visual/minebot_target
name = "Rocket Target"
icon = 'icons/mob/actions/actions_items.dmi'
icon_state = "sniper_zoom"
layer = BELOW_MOB_LAYER
plane = GAME_PLANE
light_range = 2
duration = 5 SECONDS
#undef BOMB_COOLDOWN
@@ -3,6 +3,7 @@
desc = "A minebot upgrade."
icon_state = "door_electronics"
icon = 'icons/obj/devices/circuitry_n_data.dmi'
item_flags = NOBLUDGEON
/obj/item/mine_bot_upgrade/afterattack(mob/living/basic/mining_drone/minebot, mob/user, proximity)
. = ..()
@@ -58,3 +59,35 @@
minebot.melee_damage_lower = initial(minebot.melee_damage_lower) + base_damage_add
minebot.melee_damage_upper = initial(minebot.melee_damage_upper) + base_damage_add
minebot.stored_gun?.recharge_time += base_cooldown_add
/obj/item/mine_bot_upgrade/regnerative_shield
name = "regenerative shield"
desc = "Allows your minebot to tank many hits before going down!"
/obj/item/mine_bot_upgrade/regnerative_shield/upgrade_bot(mob/living/basic/mining_drone/minebot, mob/user)
if(HAS_TRAIT(minebot, TRAIT_REGEN_SHIELD))
user.balloon_alert(minebot, "already has it!")
return
var/static/list/shield_layers = list(
/obj/effect/overlay/minebot_top_shield,
/obj/effect/overlay/minebot_bottom_shield
)
minebot.AddComponent(/datum/component/regenerative_shield, shield_overlays = shield_layers)
qdel(src)
/obj/effect/overlay/minebot_top_shield
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
vis_flags = VIS_INHERIT_DIR
icon = 'icons/mob/silicon/aibots.dmi'
icon_state = "minebot_shield_top_layer"
layer = ABOVE_ALL_MOB_LAYER
/obj/effect/overlay/minebot_bottom_shield
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
vis_flags = VIS_INHERIT_DIR
icon = 'icons/mob/silicon/aibots.dmi'
icon_state = "minebot_shield_bottom_layer"
layer = BELOW_MOB_LAYER