From 18b0abe4b3bb89d869097413ff9ba73ca29647ee Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Tue, 5 Sep 2023 12:46:11 +0200 Subject: [PATCH] [MIRROR] minebots basic bots [MDB IGNORE] (#23529) * minebots basic bots (#78032) ## About The Pull Request Transforms the minebots into basic robots. You can now command these minebots to extract minerals for you. To activate automated mining mode, simply instruct them with the command "mine." They will then proceed to autonomously mine walls and gather ores. If you wish to make the bot deposit all the collected ores, use the command "drop." Alternatively, you can leave it in collection mode, and it will gather all the ores you mine. Additionally, the bot now responds to several more commands; you can instruct it to follow you, toggle its lights on or off by saying "lights." In attack mode, it refrains from mining or collecting ores but will engage in combat alongside you. If it detects you as deceased or unconscious, it will alert all miners, request assistance, and relay your coordinates via the mining communication channel. to power it on u will need to feed it any type of ore first so it may listen to ur commands ## Why It's Good For The Game makes the non sapiant minebots more useful ## Changelog :cl: refactor: the minebots have been refactored please report any bugs add: minebots can now mine walls and collect ores automatically and they will alert everyone if they find u dead /:cl: * minebots basic bots * Modular --------- Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Co-authored-by: Giz <13398309+vinylspiders@users.noreply.github.com> --- .../SpaceRuins/skyrat/piratefort.dmm | 2 +- _maps/RandomRuins/SpaceRuins/the_faceoff.dmm | 2 +- _maps/RandomZLevels/caves.dmm | 4 +- code/__DEFINES/ai/monsters.dm | 16 + .../basic_ai_behaviors/find_mineable_wall.dm | 33 ++ code/datums/components/appearance_on_aggro.dm | 35 +- .../pet_commands/pet_commands_basic.dm | 2 + .../orders/order_items/mining/order_toys.dm | 2 +- code/modules/mining/minebot.dm | 331 ------------------ code/modules/mining/voucher_sets.dm | 2 +- .../basic/lavaland/goldgrub/goldgrub_ai.dm | 34 -- .../mob/living/basic/minebots/minebot.dm | 175 +++++++++ .../basic/minebots/minebot_abilities.dm | 51 +++ .../mob/living/basic/minebots/minebot_ai.dm | 227 ++++++++++++ .../living/basic/minebots/minebot_upgrades.dm | 60 ++++ .../regal_rat/regal_rat_actions.dm | 2 +- .../guns/energy/kinetic_accelerator.dm | 4 +- .../unit_tests/simple_animal_freeze.dm | 2 - .../modules/mapping/code/interdyne_mining.dm | 8 +- tgstation.dme | 6 +- .../UpdatePaths/Scripts/78032_miningdrone.txt | 1 + 21 files changed, 612 insertions(+), 387 deletions(-) create mode 100644 code/datums/ai/basic_mobs/basic_ai_behaviors/find_mineable_wall.dm delete mode 100644 code/modules/mining/minebot.dm create mode 100644 code/modules/mob/living/basic/minebots/minebot.dm create mode 100644 code/modules/mob/living/basic/minebots/minebot_abilities.dm create mode 100644 code/modules/mob/living/basic/minebots/minebot_ai.dm create mode 100644 code/modules/mob/living/basic/minebots/minebot_upgrades.dm create mode 100644 tools/UpdatePaths/Scripts/78032_miningdrone.txt diff --git a/_maps/RandomRuins/SpaceRuins/skyrat/piratefort.dmm b/_maps/RandomRuins/SpaceRuins/skyrat/piratefort.dmm index 8a0bb4b3eec..da0c9652a5e 100644 --- a/_maps/RandomRuins/SpaceRuins/skyrat/piratefort.dmm +++ b/_maps/RandomRuins/SpaceRuins/skyrat/piratefort.dmm @@ -629,7 +629,7 @@ /turf/open/floor/iron/dark, /area/ruin/space/has_grav/powered) "vU" = ( -/mob/living/simple_animal/hostile/mining_drone{ +/mob/living/basic/mining_drone{ faction = list("pirate") }, /obj/effect/turf_decal/bot, diff --git a/_maps/RandomRuins/SpaceRuins/the_faceoff.dmm b/_maps/RandomRuins/SpaceRuins/the_faceoff.dmm index 08f2fe4d8f4..2560cefa8a1 100644 --- a/_maps/RandomRuins/SpaceRuins/the_faceoff.dmm +++ b/_maps/RandomRuins/SpaceRuins/the_faceoff.dmm @@ -735,7 +735,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/full, -/mob/living/simple_animal/hostile/mining_drone, +/mob/living/basic/mining_drone, /obj/item/grenade/c4, /turf/open/misc/asteroid/basalt/airless, /area/ruin/space) diff --git a/_maps/RandomZLevels/caves.dmm b/_maps/RandomZLevels/caves.dmm index 697b0f4feef..a1e4de62b48 100644 --- a/_maps/RandomZLevels/caves.dmm +++ b/_maps/RandomZLevels/caves.dmm @@ -1206,7 +1206,7 @@ "he" = ( /obj/structure/window/spawner/directional/west, /obj/structure/window/spawner/directional/south, -/mob/living/simple_animal/hostile/mining_drone, +/mob/living/basic/mining_drone, /turf/open/floor/plating, /area/awaymission/caves/listeningpost) "hq" = ( @@ -1728,7 +1728,7 @@ /area/awaymission/caves/bmp_asteroid/level_three) "IH" = ( /obj/structure/window/spawner/directional/west, -/mob/living/simple_animal/hostile/mining_drone, +/mob/living/basic/mining_drone, /turf/open/floor/plating, /area/awaymission/caves/listeningpost) "IN" = ( diff --git a/code/__DEFINES/ai/monsters.dm b/code/__DEFINES/ai/monsters.dm index 4915ff9cbc8..4040bdb23a8 100644 --- a/code/__DEFINES/ai/monsters.dm +++ b/code/__DEFINES/ai/monsters.dm @@ -119,3 +119,19 @@ #define BB_ORE_TARGET "BB_ore_target" /// which ore types we will not eat #define BB_ORE_IGNORE_TYPES "BB_ore_ignore_types" + +// minebot keys +/// key that stores our toggle light ability +#define BB_MINEBOT_LIGHT_ABILITY "minebot_light_ability" +/// key that stores our dump ore ability +#define BB_MINEBOT_DUMP_ABILITY "minebot_dump_ability" +/// key that stores our target turf +#define BB_TARGET_MINERAL_TURF "target_mineral_turf" +/// key that stores list of the turfs we ignore +#define BB_BLACKLIST_MINERAL_TURFS "blacklist_mineral_turfs" +/// key that stores the previous blocked wall +#define BB_PREVIOUS_UNREACHABLE_WALL "previous_unreachable_wall" +/// key that stores our mining mode +#define BB_AUTOMATED_MINING "automated_mining" +/// key that stores the nearest dead human +#define BB_NEARBY_DEAD_MINER "nearby_dead_miner" diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/find_mineable_wall.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/find_mineable_wall.dm new file mode 100644 index 00000000000..ad5749c9161 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/find_mineable_wall.dm @@ -0,0 +1,33 @@ +//behavior to find mineable mineral walls +/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/closed/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! + finish_action(controller, TRUE) + return + + finish_action(controller, FALSE) + +/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(!ISDIAGONALDIR(direction_to_turf)) + return TRUE + var/list/directions_to_check = list() + for(var/direction_check in GLOB.cardinals) + 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(ignore_atoms = list(source))) + return TRUE + return FALSE diff --git a/code/datums/components/appearance_on_aggro.dm b/code/datums/components/appearance_on_aggro.dm index 85690a579ae..affb926d28d 100644 --- a/code/datums/components/appearance_on_aggro.dm +++ b/code/datums/components/appearance_on_aggro.dm @@ -10,6 +10,8 @@ var/alpha_on_aggro /// visibility of our icon when deaggroed var/alpha_on_deaggro + /// do we currently have a target + var/atom/current_target /datum/component/appearance_on_aggro/Initialize(overlay_icon, overlay_state, alpha_on_aggro, alpha_on_deaggro) if(!isliving(parent)) @@ -22,28 +24,49 @@ src.alpha_on_deaggro = alpha_on_deaggro /datum/component/appearance_on_aggro/RegisterWithParent() - RegisterSignal(parent, COMSIG_AI_BLACKBOARD_KEY_SET(target_key), PROC_REF(change_appearance)) - RegisterSignal(parent, COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key), PROC_REF(revert_appearance)) + RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(change_overlays)) + RegisterSignal(parent, COMSIG_AI_BLACKBOARD_KEY_SET(target_key), PROC_REF(on_set_target)) + RegisterSignal(parent, COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key), PROC_REF(on_clear_target)) /datum/component/appearance_on_aggro/UnregisterFromParent() . = ..() UnregisterSignal(parent, list(COMSIG_AI_BLACKBOARD_KEY_SET(target_key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key))) -/datum/component/appearance_on_aggro/proc/change_appearance(mob/living/source) +/datum/component/appearance_on_aggro/proc/on_set_target(mob/living/source) SIGNAL_HANDLER var/atom/target = source.ai_controller.blackboard[target_key] if(isnull(target)) return + current_target = target + RegisterSignal(target, COMSIG_QDELETING, PROC_REF(on_clear_target)) if(overlay_path) - source.add_overlay(overlay_path) + source.update_appearance(UPDATE_OVERLAYS) if(alpha_on_aggro) animate(source, alpha = alpha_on_aggro, time = 2 SECONDS) -/datum/component/appearance_on_aggro/proc/revert_appearance(mob/living/source) +/datum/component/appearance_on_aggro/Destroy() + if(current_target) + revert_appearance(parent) + return ..() + +/datum/component/appearance_on_aggro/proc/on_clear_target(atom/source) SIGNAL_HANDLER + revert_appearance(parent) + +/datum/component/appearance_on_aggro/proc/revert_appearance(mob/living/source) + UnregisterSignal(current_target, COMSIG_QDELETING) + current_target = null if(overlay_path) - source.cut_overlay(overlay_path) + source.update_appearance(UPDATE_OVERLAYS) if(alpha_on_deaggro) animate(source, alpha = alpha_on_deaggro, time = 2 SECONDS) + +/datum/component/appearance_on_aggro/proc/change_overlays(atom/source, list/overlays) + SIGNAL_HANDLER + + if(isnull(current_target)) + return + + overlays += overlay_path diff --git a/code/datums/components/pet_commands/pet_commands_basic.dm b/code/datums/components/pet_commands/pet_commands_basic.dm index 40ec41aa3d6..a07a94fff3f 100644 --- a/code/datums/components/pet_commands/pet_commands_basic.dm +++ b/code/datums/components/pet_commands/pet_commands_basic.dm @@ -185,6 +185,8 @@ /datum/pet_command/protect_owner/execute_action(datum/ai_controller/controller) var/datum/targetting_datum/basic/targetting = controller.blackboard[BB_TARGETTING_DATUM] var/mob/living/victim = controller.blackboard[BB_CURRENT_PET_TARGET] + if(QDELETED(victim)) + return if(victim.stat > targetting.stat_attack) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) return diff --git a/code/game/machinery/computer/orders/order_items/mining/order_toys.dm b/code/game/machinery/computer/orders/order_items/mining/order_toys.dm index 06bd9032e74..cd1dbf5c674 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_toys.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_toys.dm @@ -14,7 +14,7 @@ cost_per_order = 300 /datum/orderable_item/toys_drones/mining_drone - item_path = /mob/living/simple_animal/hostile/mining_drone + item_path = /mob/living/basic/mining_drone cost_per_order = 800 /datum/orderable_item/toys_drones/drone_health diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm deleted file mode 100644 index f7a6c030d2c..00000000000 --- a/code/modules/mining/minebot.dm +++ /dev/null @@ -1,331 +0,0 @@ -/**********************Mining drone**********************/ -#define MINEDRONE_COLLECT 1 -#define MINEDRONE_ATTACK 2 - -/mob/living/simple_animal/hostile/mining_drone - name = "\improper Nanotrasen minebot" - desc = "The instructions printed on the side read: This is a small robot used to support miners, can be set to search and collect loose ore, or to help fend off wildlife." - gender = NEUTER - icon = 'icons/mob/silicon/aibots.dmi' - icon_state = "mining_drone" - icon_living = "mining_drone" - status_flags = CANSTUN|CANKNOCKDOWN|CANPUSH - mouse_opacity = MOUSE_OPACITY_ICON - faction = list(FACTION_NEUTRAL) - combat_mode = TRUE - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - move_to_delay = 10 - health = 125 - maxHealth = 125 - melee_damage_lower = 15 - melee_damage_upper = 15 - obj_damage = 10 - environment_smash = ENVIRONMENT_SMASH_NONE - check_friendly_fire = TRUE - stop_automated_movement_when_pulled = TRUE - attack_verb_continuous = "drills" - attack_verb_simple = "drill" - attack_sound = 'sound/weapons/circsawhit.ogg' - sentience_type = SENTIENCE_MINEBOT - speak_emote = list("states") - wanted_objects = list(/obj/item/stack/ore/diamond, /obj/item/stack/ore/gold, /obj/item/stack/ore/silver, - /obj/item/stack/ore/plasma, /obj/item/stack/ore/uranium, /obj/item/stack/ore/iron, - /obj/item/stack/ore/bananium, /obj/item/stack/ore/titanium) - mob_biotypes = MOB_ROBOTIC - loot = list(/obj/effect/decal/cleanable/robot_debris) - del_on_death = TRUE - light_system = MOVABLE_LIGHT - light_range = 6 - light_on = FALSE - var/mode = MINEDRONE_COLLECT - var/obj/item/gun/energy/recharge/kinetic_accelerator/minebot/stored_gun - -/mob/living/simple_animal/hostile/mining_drone/Initialize(mapload) - . = ..() - - AddElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE) - - stored_gun = new(src) - var/datum/action/innate/minedrone/toggle_light/toggle_light_action = new() - toggle_light_action.Grant(src) - var/datum/action/innate/minedrone/toggle_meson_vision/toggle_meson_vision_action = new() - toggle_meson_vision_action.Grant(src) - var/datum/action/innate/minedrone/toggle_mode/toggle_mode_action = new() - toggle_mode_action.Grant(src) - var/datum/action/innate/minedrone/dump_ore/dump_ore_action = new() - dump_ore_action.Grant(src) - var/obj/item/implant/radio/mining/imp = new(src) - imp.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) - - SetCollectBehavior() - -/mob/living/simple_animal/hostile/mining_drone/Destroy() - QDEL_NULL(stored_gun) - for (var/datum/action/innate/minedrone/action in actions) - qdel(action) - return ..() - -/mob/living/simple_animal/hostile/mining_drone/sentience_act() - ..() - check_friendly_fire = 0 - -/mob/living/simple_animal/hostile/mining_drone/examine(mob/user) - . = ..() - var/t_He = p_They() - var/t_him = p_them() - var/t_s = p_s() - if(health < maxHealth) - if(health >= maxHealth * 0.5) - . += span_warning("[t_He] look[t_s] slightly dented.") - else - . += span_boldwarning("[t_He] look[t_s] severely dented!") - . += {"Using a mining scanner on [t_him] will instruct [t_him] to drop stored ore. [max(0, LAZYLEN(contents) - 1)] Stored Ore\n - Field repairs can be done with a welder."} - if(stored_gun?.max_mod_capacity) - . += "[stored_gun.get_remaining_mod_capacity()]% mod capacity remaining." - for(var/obj/item/borg/upgrade/modkit/modkit as anything in stored_gun.modkits) - . += span_notice("There is \a [modkit] installed, using [modkit.cost]% capacity.") - -/mob/living/simple_animal/hostile/mining_drone/welder_act(mob/living/user, obj/item/welder) - ..() - . = TRUE - if(mode == MINEDRONE_ATTACK) - to_chat(user, span_warning("[src] can't be repaired while in attack mode!")) - return - - if(maxHealth == health) - to_chat(user, span_info("[src] is at full integrity.")) - return - - if(welder.use_tool(src, user, 0, volume=40)) - adjustBruteLoss(-15) - to_chat(user, span_info("You repair some of the armor on [src].")) - -/mob/living/simple_animal/hostile/mining_drone/attackby(obj/item/item_used, mob/user, params) - if(istype(item_used, /obj/item/mining_scanner) || istype(item_used, /obj/item/t_scanner/adv_mining_scanner)) - to_chat(user, span_info("You instruct [src] to drop any collected ore.")) - DropOre() - return - if(item_used.tool_behaviour == TOOL_CROWBAR || istype(item_used, /obj/item/borg/upgrade/modkit)) - item_used.melee_attack_chain(user, stored_gun, params) - return - ..() - -/mob/living/simple_animal/hostile/mining_drone/death() - DropOre() - if(stored_gun) - for(var/obj/item/borg/upgrade/modkit/modkit as anything in stored_gun.modkits) - modkit.uninstall(stored_gun) - death_message = "blows apart!" - ..() - -/mob/living/simple_animal/hostile/mining_drone/attack_hand(mob/living/carbon/human/user, list/modifiers) - . = ..() - if(.) - return - if(!user.combat_mode) - toggle_mode() - switch(mode) - if(MINEDRONE_COLLECT) - to_chat(user, span_info("[src] has been set to search and store loose ore.")) - if(MINEDRONE_ATTACK) - to_chat(user, span_info("[src] has been set to attack hostile wildlife.")) - return - -/mob/living/simple_animal/hostile/mining_drone/CanAllowThrough(atom/movable/mover, border_dir) - . = ..() - if(istype(mover, /obj/projectile/kinetic)) - var/obj/projectile/kinetic/projectile = mover - if(projectile.kinetic_gun) - if (locate(/obj/item/borg/upgrade/modkit/minebot_passthrough) in projectile.kinetic_gun.modkits) - return TRUE - else if(istype(mover, /obj/projectile/destabilizer)) - return TRUE - -/mob/living/simple_animal/hostile/mining_drone/proc/SetCollectBehavior() - mode = MINEDRONE_COLLECT - vision_range = 9 - search_objects = 2 - wander = TRUE - ranged = FALSE - minimum_distance = 1 - retreat_distance = null - icon_state = "mining_drone" - to_chat(src, span_info("You are set to collect mode. You can now collect loose ore.")) - -/mob/living/simple_animal/hostile/mining_drone/proc/SetOffenseBehavior() - mode = MINEDRONE_ATTACK - vision_range = 7 - search_objects = 0 - wander = FALSE - ranged = TRUE - retreat_distance = 2 - minimum_distance = 1 - icon_state = "mining_drone_offense" - to_chat(src, span_info("You are set to attack mode. You can now attack from range.")) - -/mob/living/simple_animal/hostile/mining_drone/AttackingTarget() - if(istype(target, /obj/item/stack/ore) && mode == MINEDRONE_COLLECT) - CollectOre() - return - if(isliving(target)) - SetOffenseBehavior() - return ..() - -/mob/living/simple_animal/hostile/mining_drone/OpenFire(atom/target) - if(CheckFriendlyFire(target)) - return - stored_gun.afterattack(target, src) //of the possible options to allow minebots to have KA mods, would you believe this is the best? - -/mob/living/simple_animal/hostile/mining_drone/proc/CollectOre() - for(var/obj/item/stack/ore/O in range(1, src)) - O.forceMove(src) - -/mob/living/simple_animal/hostile/mining_drone/proc/DropOre(message = 1) - if(!contents.len) - if(message) - to_chat(src, span_warning("You attempt to dump your stored ore, but you have none!")) - return - if(message) - to_chat(src, span_notice("You dump your stored ore.")) - for(var/obj/item/stack/ore/O in contents) - O.forceMove(drop_location()) - -/mob/living/simple_animal/hostile/mining_drone/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - if(mode != MINEDRONE_ATTACK && amount > 0) - SetOffenseBehavior() - . = ..() - -/datum/action/innate/minedrone/toggle_meson_vision - name = "Toggle Meson Vision" - button_icon_state = "meson" - -/datum/action/innate/minedrone/toggle_meson_vision/Activate() - var/mob/living/simple_animal/hostile/mining_drone/user = owner - if(user.sight & SEE_TURFS) - user.clear_sight(SEE_TURFS) - user.lighting_cutoff_red += 5 - user.lighting_cutoff_green += 15 - user.lighting_cutoff_blue += 5 - else - user.add_sight(SEE_TURFS) - user.lighting_cutoff_red -= 5 - user.lighting_cutoff_green -= 15 - user.lighting_cutoff_blue -= 5 - - user.sync_lighting_plane_cutoff() - - to_chat(user, span_notice("You toggle your meson vision [(user.sight & SEE_TURFS) ? "on" : "off"].")) - - -/mob/living/simple_animal/hostile/mining_drone/proc/toggle_mode() - switch(mode) - if(MINEDRONE_ATTACK) - SetCollectBehavior() - else - SetOffenseBehavior() - -//Actions for sentient minebots - -/datum/action/innate/minedrone - check_flags = AB_CHECK_CONSCIOUS - button_icon = 'icons/mob/actions/actions_mecha.dmi' - background_icon_state = "bg_default" - overlay_icon_state = "bg_default_border" - -/datum/action/innate/minedrone/toggle_light - name = "Toggle Light" - button_icon_state = "mech_lights_off" - - -/datum/action/innate/minedrone/toggle_light/Activate() - var/mob/living/simple_animal/hostile/mining_drone/user = owner - user.set_light_on(!user.light_on) - to_chat(user, span_notice("You toggle your light [user.light_on ? "on" : "off"].")) - - -/datum/action/innate/minedrone/toggle_mode - name = "Toggle Mode" - button_icon_state = "mech_cycle_equip_off" - -/datum/action/innate/minedrone/toggle_mode/Activate() - var/mob/living/simple_animal/hostile/mining_drone/user = owner - user.toggle_mode() - -/datum/action/innate/minedrone/dump_ore - name = "Dump Ore" - button_icon_state = "mech_eject" - -/datum/action/innate/minedrone/dump_ore/Activate() - var/mob/living/simple_animal/hostile/mining_drone/user = owner - user.DropOre() - - -/**********************Minebot Upgrades**********************/ - -//Melee - -/obj/item/mine_bot_upgrade - name = "minebot melee upgrade" - desc = "A minebot upgrade." - icon_state = "door_electronics" - icon = 'icons/obj/assemblies/module.dmi' - -/obj/item/mine_bot_upgrade/afterattack(mob/living/simple_animal/hostile/mining_drone/minebot, mob/user, proximity) - . = ..() - if(!istype(minebot) || !proximity) - return - upgrade_bot(minebot, user) - -/obj/item/mine_bot_upgrade/proc/upgrade_bot(mob/living/simple_animal/hostile/mining_drone/minebot, mob/user) - if(minebot.melee_damage_upper != initial(minebot.melee_damage_upper)) - to_chat(user, span_warning("[minebot] already has a combat upgrade installed!")) - return - minebot.melee_damage_lower += 7 - minebot.melee_damage_upper += 7 - to_chat(user, "You increase the close-quarter combat abilities of [minebot].") - qdel(src) - -//Health - -/obj/item/mine_bot_upgrade/health - name = "minebot armor upgrade" - -/obj/item/mine_bot_upgrade/health/upgrade_bot(mob/living/simple_animal/hostile/mining_drone/minebot, mob/user) - if(minebot.maxHealth != initial(minebot.maxHealth)) - to_chat(user, span_warning("[minebot] already has reinforced armor!")) - return - minebot.maxHealth += 45 - minebot.updatehealth() - to_chat(user, "You reinforce the armor of [minebot].") - qdel(src) - -//AI - -/obj/item/slimepotion/slime/sentience/mining - name = "minebot AI upgrade" - desc = "Can be used to grant sentience to minebots. It's incompatible with minebot armor and melee upgrades, and will override them." - icon_state = "door_electronics" - icon = 'icons/obj/assemblies/module.dmi' - sentience_type = SENTIENCE_MINEBOT - var/base_health_add = 5 //sentient minebots are penalized for beign sentient; they have their stats reset to normal plus these values - var/base_damage_add = 1 //this thus disables other minebot upgrades - var/base_speed_add = 1 - var/base_cooldown_add = 10 //base cooldown isn't reset to normal, it's just added on, since it's not practical to disable the cooldown module - -/obj/item/slimepotion/slime/sentience/mining/after_success(mob/living/user, mob/living/simple_animal/simple_mob) - if(!istype(simple_mob, /mob/living/simple_animal/hostile/mining_drone)) - return - var/mob/living/simple_animal/hostile/mining_drone/minebot = simple_mob - minebot.maxHealth = initial(minebot.maxHealth) + base_health_add - 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.move_to_delay = initial(minebot.move_to_delay) + base_speed_add - minebot.stored_gun?.recharge_time += base_cooldown_add - -#undef MINEDRONE_COLLECT -#undef MINEDRONE_ATTACK diff --git a/code/modules/mining/voucher_sets.dm b/code/modules/mining/voucher_sets.dm index 86c6e407d67..071a8f11536 100644 --- a/code/modules/mining/voucher_sets.dm +++ b/code/modules/mining/voucher_sets.dm @@ -62,7 +62,7 @@ icon = 'icons/mob/silicon/aibots.dmi' icon_state = "mining_drone" set_items = list( - /mob/living/simple_animal/hostile/mining_drone, + /mob/living/basic/mining_drone, /obj/item/weldingtool/hugetank, /obj/item/clothing/head/utility/welding, /obj/item/borg/upgrade/modkit/minebot_passthrough, diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm index 12e7d4c8ff1..96c9b3d556e 100644 --- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm @@ -131,40 +131,6 @@ controller.queue_behavior(/datum/ai_behavior/mine_wall, BB_TARGET_MINERAL_WALL) return SUBTREE_RETURN_FINISH_PLANNING - -/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/closed/mineral/potential_wall in oview(9, living_pawn)) - if(!check_if_mineable(living_pawn, potential_wall)) //check if its surrounded by walls - continue - controller.set_blackboard_key(found_wall_key, potential_wall) //closest wall first! - finish_action(controller, TRUE) - return - - finish_action(controller, FALSE) - -/datum/ai_behavior/find_mineral_wall/proc/check_if_mineable(mob/living/source, turf/target_wall) - var/direction_to_turf = get_dir(target_wall, source) - if(!ISDIAGONALDIR(direction_to_turf)) - return TRUE - var/list/directions_to_check = list() - for(var/direction_check in GLOB.cardinals) - 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(ignore_atoms = list(source))) - return TRUE - return FALSE - /datum/ai_behavior/mine_wall behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION action_cooldown = 15 SECONDS diff --git a/code/modules/mob/living/basic/minebots/minebot.dm b/code/modules/mob/living/basic/minebots/minebot.dm new file mode 100644 index 00000000000..061c9a624f7 --- /dev/null +++ b/code/modules/mob/living/basic/minebots/minebot.dm @@ -0,0 +1,175 @@ +/mob/living/basic/mining_drone + name = "\improper Nanotrasen minebot" + desc = "The instructions printed on the side read: This is a small robot used to support miners, can be set to search and collect loose ore, or to help fend off wildlife. Insert any type of ore into it to make it start listening to your commands!" + gender = NEUTER + icon = 'icons/mob/silicon/aibots.dmi' + icon_state = "mining_drone" + icon_living = "mining_drone" + basic_mob_flags = DEL_ON_DEATH + status_flags = CANSTUN|CANKNOCKDOWN|CANPUSH + mouse_opacity = MOUSE_OPACITY_ICON + combat_mode = TRUE + habitable_atmos = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minimum_survivable_temperature = 0 + health = 125 + maxHealth = 125 + melee_damage_lower = 15 + melee_damage_upper = 15 + obj_damage = 10 + attack_verb_continuous = "drills" + attack_verb_simple = "drill" + attack_sound = 'sound/weapons/circsawhit.ogg' + sentience_type = SENTIENCE_MINEBOT + speak_emote = list("states") + mob_biotypes = MOB_ROBOTIC + death_message = "blows apart!" + light_system = MOVABLE_LIGHT + light_range = 6 + 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 + ///the commands our owner can give us + var/list/pet_commands = list( + /datum/pet_command/idle/minebot, + /datum/pet_command/minebot_ability/light, + /datum/pet_command/minebot_ability/dump, + /datum/pet_command/automate_mining, + /datum/pet_command/free/minebot, + /datum/pet_command/follow, + /datum/pet_command/point_targetting/attack/minebot, + ) + +/mob/living/basic/mining_drone/Initialize(mapload) + . = ..() + + 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,\ + after_tame = CALLBACK(src, PROC_REF(activate_bot)),\ + ) + + var/datum/action/cooldown/mob_cooldown/minedrone/toggle_light/toggle_light_action = new(src) + var/datum/action/cooldown/mob_cooldown/minedrone/toggle_meson_vision/toggle_meson_vision_action = new(src) + var/datum/action/cooldown/mob_cooldown/minedrone/dump_ore/dump_ore_action = new(src) + toggle_light_action.Grant(src) + toggle_meson_vision_action.Grant(src) + dump_ore_action.Grant(src) + ai_controller.set_blackboard_key(BB_MINEBOT_LIGHT_ABILITY, toggle_light_action) + ai_controller.set_blackboard_key(BB_MINEBOT_DUMP_ABILITY, dump_ore_action) + + 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)) + +/mob/living/basic/mining_drone/set_combat_mode(new_mode, silent = TRUE) + . = ..() + icon_state = combat_mode ? "mining_drone_offense" : "mining_drone" + balloon_alert(src, "now [combat_mode ? "attacking" : "collecting"]") + +/mob/living/basic/mining_drone/examine(mob/user) + . = ..() + if(health < maxHealth) + if(health >= maxHealth * 0.5) + . += span_warning("[p_They()] look slightly dented.") + else + . += span_boldwarning("[p_They()] look severely dented!") + + if(isnull(stored_gun) || !stored_gun.max_mod_capacity) + return + + . += "[stored_gun.get_remaining_mod_capacity()]% mod capacity remaining." + + for(var/obj/item/borg/upgrade/modkit/modkit as anything in stored_gun.modkits) + . += span_notice("There is \a [modkit] installed, using [modkit.cost]% capacity.") + + +/mob/living/basic/mining_drone/welder_act(mob/living/user, obj/item/welder) + if(user.combat_mode) + return FALSE + if(combat_mode) + user.balloon_alert(user, "can't repair in attack mode!") + return TRUE + if(maxHealth == health) + user.balloon_alert(user, "at full integrity!") + return TRUE + if(welder.use_tool(src, user, 0, volume=40)) + adjustBruteLoss(-15) + user.balloon_alert(user, "successfully repaired!") + return TRUE + +/mob/living/basic/mining_drone/attackby(obj/item/item_used, mob/user, params) + if(item_used.tool_behaviour == TOOL_CROWBAR || istype(item_used, /obj/item/borg/upgrade/modkit)) + item_used.melee_attack_chain(user, stored_gun, params) + return + + return ..() + +/mob/living/basic/mining_drone/attack_hand(mob/living/carbon/human/user, list/modifiers) + . = ..() + + if(. || user.combat_mode) + return + set_combat_mode(!combat_mode) + balloon_alert(user, "now [combat_mode ? "attacking wildlife" : "collecting loose ore"]") + +/mob/living/basic/mining_drone/RangedAttack(atom/target) + if(!combat_mode) + return + stored_gun.afterattack(target, src) + + +/mob/living/basic/mining_drone/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers) + . = ..() + + if(!. || !proximity_flag || combat_mode) + return + + if(istype(attack_target, /obj/item/stack/ore)) + var/obj/item/target_ore = attack_target + target_ore.forceMove(src) + +/mob/living/basic/mining_drone/proc/drop_ore() + to_chat(src, span_notice("You dump your stored ore.")) + 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/proc/activate_bot() + AddComponent(/datum/component/obeys_commands, pet_commands) + +/mob/living/basic/mining_drone/death(gibbed) + drop_ore() + + if(isnull(stored_gun)) + return ..() + + for(var/obj/item/borg/upgrade/modkit/modkit as anything in stored_gun.modkits) + modkit.uninstall(stored_gun) + + return ..() + +/mob/living/basic/mining_drone/Destroy() + QDEL_NULL(stored_gun) + QDEL_NULL(access_card) + return ..() + diff --git a/code/modules/mob/living/basic/minebots/minebot_abilities.dm b/code/modules/mob/living/basic/minebots/minebot_abilities.dm new file mode 100644 index 00000000000..4f119fd9b66 --- /dev/null +++ b/code/modules/mob/living/basic/minebots/minebot_abilities.dm @@ -0,0 +1,51 @@ + +/datum/action/cooldown/mob_cooldown/minedrone + button_icon = 'icons/mob/actions/actions_mecha.dmi' + background_icon_state = "bg_default" + overlay_icon_state = "bg_default_border" + click_to_activate = FALSE + +/datum/action/cooldown/mob_cooldown/minedrone/toggle_light + name = "Toggle Light" + button_icon_state = "mech_lights_off" + +/datum/action/cooldown/mob_cooldown/minedrone/Activate() + owner.set_light_on(!owner.light_on) + owner.balloon_alert(owner, "lights [owner.light_on ? "on" : "off"]!") + +/datum/action/cooldown/mob_cooldown/minedrone/dump_ore + name = "Dump Ore" + button_icon_state = "mech_eject" + +/datum/action/cooldown/mob_cooldown/minedrone/dump_ore/IsAvailable(feedback = TRUE) + if(locate(/obj/item/stack/ore) in owner.contents) + return TRUE + + if(feedback) + owner.balloon_alert(owner, "no ore!") + return FALSE + +/datum/action/cooldown/mob_cooldown/minedrone/dump_ore/Activate() + var/mob/living/basic/mining_drone/user = owner + user.drop_ore() + +/datum/action/cooldown/mob_cooldown/minedrone/toggle_meson_vision + name = "Toggle Meson Vision" + button_icon_state = "meson" + +/datum/action/cooldown/mob_cooldown/minedrone/toggle_meson_vision/Activate() + if(owner.sight & SEE_TURFS) + owner.clear_sight(SEE_TURFS) + owner.lighting_cutoff_red += 5 + owner.lighting_cutoff_green += 15 + owner.lighting_cutoff_blue += 5 + else + owner.add_sight(SEE_TURFS) + owner.lighting_cutoff_red -= 5 + owner.lighting_cutoff_green -= 15 + owner.lighting_cutoff_blue -= 5 + + owner.sync_lighting_plane_cutoff() + + to_chat(owner, span_notice("You toggle your meson vision [(owner.sight & SEE_TURFS) ? "on" : "off"].")) + diff --git a/code/modules/mob/living/basic/minebots/minebot_ai.dm b/code/modules/mob/living/basic/minebots/minebot_ai.dm new file mode 100644 index 00000000000..9ee1d93c1e0 --- /dev/null +++ b/code/modules/mob/living/basic/minebots/minebot_ai.dm @@ -0,0 +1,227 @@ +/datum/ai_controller/basic_controller/minebot + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_PET_TARGETTING_DATUM = new /datum/targetting_datum/not_friends, + BB_BLACKLIST_MINERAL_TURFS = list(/turf/closed/mineral/gibtonite), + BB_AUTOMATED_MINING = FALSE, + ) + + 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/pet_planning, + /datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot, + /datum/ai_planning_subtree/find_and_hunt_target/consume_ores/minebot, + /datum/ai_planning_subtree/minebot_mining, + /datum/ai_planning_subtree/locate_dead_humans, + ) + +///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) + var/mob/living/dead_target = controller.blackboard[BB_NEARBY_DEAD_MINER] + + if(QDELETED(dead_target)) + controller.queue_behavior(/datum/ai_behavior/find_and_set/unconscious_human, BB_NEARBY_DEAD_MINER, /mob/living/carbon/human) + return + + controller.queue_behavior(/datum/ai_behavior/send_sos_message, BB_NEARBY_DEAD_MINER) + return SUBTREE_RETURN_FINISH_PLANNING + +/datum/ai_behavior/find_and_set/unconscious_human/search_tactic(datum/ai_controller/controller, locate_path, search_range) + for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn)) + if(target.stat >= UNCONSCIOUS && target.mind) + return target + +/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)) + finish_action(controller, FALSE, target_key) + return + 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) + finish_action(controller, FALSE, target_key) + return + 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) + finish_action(controller, TRUE, target_key) + +/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_behavior/basic_ranged_attack/minebot + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT + +/datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + if(!living_pawn.combat_mode) //we are not on attack mode + return + return ..() + +///mine walls if we are on automated mining mode +/datum/ai_planning_subtree/minebot_mining/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/turf/mineral_target = controller.blackboard[BB_TARGET_MINERAL_TURF] + var/automated_mining = controller.blackboard[BB_AUTOMATED_MINING] + + if(!automated_mining) + return + + if(QDELETED(mineral_target)) + controller.queue_behavior(/datum/ai_behavior/find_mineral_wall/minebot, BB_TARGET_MINERAL_TURF) + return + + controller.queue_behavior(/datum/ai_behavior/minebot_mine_turf, BB_TARGET_MINERAL_TURF) + return SUBTREE_RETURN_FINISH_PLANNING + +/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)) + finish_action(controller, FALSE, target_key) + return + + if(check_obstacles_in_path(controller, target)) + finish_action(controller, FALSE, target_key) + return + + if(!living_pawn.combat_mode) + living_pawn.set_combat_mode(TRUE) + + living_pawn.RangedAttack(target) + finish_action(controller, TRUE, target_key) + return + +/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/consume_ores/minebot + hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/consume_ores/minebot + hunt_chance = 100 + +/datum/ai_planning_subtree/find_and_hunt_target/consume_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/unarmed_attack_target/consume_ores/minebot + hunt_cooldown = 2 SECONDS + +/datum/ai_behavior/hunt_target/unarmed_attack_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 = 'icons/obj/mining.dmi' + radial_icon_state = "pickaxe" + speech_commands = list("mine") + +/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(QDELETED(ability) || !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/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/point_targetting/attack/minebot + attack_behaviour = /datum/ai_behavior/basic_ranged_attack/minebot + +/datum/pet_command/point_targetting/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 ..() diff --git a/code/modules/mob/living/basic/minebots/minebot_upgrades.dm b/code/modules/mob/living/basic/minebots/minebot_upgrades.dm new file mode 100644 index 00000000000..6f5d43af1af --- /dev/null +++ b/code/modules/mob/living/basic/minebots/minebot_upgrades.dm @@ -0,0 +1,60 @@ +/obj/item/mine_bot_upgrade + name = "minebot melee upgrade" + desc = "A minebot upgrade." + icon_state = "door_electronics" + icon = 'icons/obj/assemblies/module.dmi' + +/obj/item/mine_bot_upgrade/afterattack(mob/living/basic/mining_drone/minebot, mob/user, proximity) + . = ..() + if(!istype(minebot) || !proximity) + return + upgrade_bot(minebot, user) + +/obj/item/mine_bot_upgrade/proc/upgrade_bot(mob/living/basic/mining_drone/minebot, mob/user) + if(minebot.melee_damage_upper != initial(minebot.melee_damage_upper)) + user.balloon_alert(user, "already has armor!") + return + minebot.melee_damage_lower += 7 + minebot.melee_damage_upper += 7 + to_chat(user, span_notice("You increase the close-quarter combat abilities of [minebot].")) + qdel(src) + +//Health + +/obj/item/mine_bot_upgrade/health + name = "minebot armor upgrade" + +/obj/item/mine_bot_upgrade/health/upgrade_bot(mob/living/basic/mining_drone/minebot, mob/user) + if(minebot.maxHealth != initial(minebot.maxHealth)) + to_chat(user, span_warning("[minebot] already has reinforced armor!")) + return + minebot.maxHealth += 45 + minebot.updatehealth() + to_chat(user, span_notice("You reinforce the armor of [minebot].")) + qdel(src) + +//AI + +/obj/item/slimepotion/slime/sentience/mining + name = "minebot AI upgrade" + desc = "Can be used to grant sentience to minebots. It's incompatible with minebot armor and melee upgrades, and will override them." + icon_state = "door_electronics" + icon = 'icons/obj/assemblies/module.dmi' + sentience_type = SENTIENCE_MINEBOT + ///health boost to add + var/base_health_add = 5 + ///damage boost to add + var/base_damage_add = 1 + ///speed boost to add + var/base_speed_add = 1 + ///cooldown boost to add + var/base_cooldown_add = 10 + +/obj/item/slimepotion/slime/sentience/mining/after_success(mob/living/user, mob/living/basic_mob) + if(!istype(basic_mob, /mob/living/basic/mining_drone)) + return + var/mob/living/basic/mining_drone/minebot = basic_mob + minebot.maxHealth = initial(minebot.maxHealth) + base_health_add + 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 diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_actions.dm b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_actions.dm index 609d80fc4df..e423b5ff852 100644 --- a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_actions.dm +++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat_actions.dm @@ -247,4 +247,4 @@ return ..() /datum/pet_command/protect_owner/glockroach - protect_behavior = /datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach + protect_behavior = /datum/ai_behavior/basic_ranged_attack/glockroach diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index dfb7bda21c7..8d9a29d3b80 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -263,10 +263,10 @@ /obj/item/borg/upgrade/modkit/proc/install(obj/item/gun/energy/recharge/kinetic_accelerator/KA, mob/user, transfer_to_loc = TRUE) . = TRUE if(minebot_upgrade) - if(minebot_exclusive && !istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone)) + if(minebot_exclusive && !istype(KA.loc, /mob/living/basic/mining_drone)) to_chat(user, span_notice("The modkit you're trying to install is only rated for minebot use.")) return FALSE - else if(istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone)) + else if(istype(KA.loc, /mob/living/basic/mining_drone)) to_chat(user, span_notice("The modkit you're trying to install is not rated for minebot use.")) return FALSE if(denied_type) diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 4f5c1db03e9..e6056b3e227 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -163,7 +163,6 @@ /mob/living/simple_animal/hostile/mimic/copy/ranged, /mob/living/simple_animal/hostile/mimic/crate, /mob/living/simple_animal/hostile/mimic/xenobio, - /mob/living/simple_animal/hostile/mining_drone, /mob/living/simple_animal/hostile/nanotrasen, /mob/living/simple_animal/hostile/nanotrasen/elite, /mob/living/simple_animal/hostile/nanotrasen/ranged, @@ -299,7 +298,6 @@ /mob/living/simple_animal/hostile/megafauna/bubblegum/no_gps, /mob/living/simple_animal/hostile/megafauna/gladiator, /mob/living/simple_animal/hostile/megafauna/hierophant/astrum, - /mob/living/simple_animal/hostile/mining_drone/interdyne, /mob/living/simple_animal/hostile/mutantliz, /mob/living/simple_animal/hostile/plantmutant, /mob/living/simple_animal/hostile/scorpion, diff --git a/modular_skyrat/modules/mapping/code/interdyne_mining.dm b/modular_skyrat/modules/mapping/code/interdyne_mining.dm index dee18909635..522b5cabe5b 100644 --- a/modular_skyrat/modules/mapping/code/interdyne_mining.dm +++ b/modular_skyrat/modules/mapping/code/interdyne_mining.dm @@ -20,14 +20,14 @@ /obj/machinery/computer/order_console/mining/interdyne/redeem_voucher(obj/item/mining_voucher/voucher, mob/redeemer) . = ..() - for(var/mob/living/simple_animal/hostile/mining_drone/drone in drop_location()) + for(var/mob/living/basic/mining_drone/drone in drop_location()) // There could already be an interdyne drone there - if(!istype(drone, /mob/living/simple_animal/hostile/mining_drone/interdyne)) + if(!istype(drone, /mob/living/basic/mining_drone/interdyne)) qdel(drone) - new /mob/living/simple_animal/hostile/mining_drone/interdyne(drop_location()) + new /mob/living/basic/mining_drone/interdyne(drop_location()) // Interdyne minebot -/mob/living/simple_animal/hostile/mining_drone/interdyne +/mob/living/basic/mining_drone/interdyne name = "\improper Interdyne minebot" faction = list(FACTION_NEUTRAL, ROLE_SYNDICATE) diff --git a/tgstation.dme b/tgstation.dme index 230d80ef563..eac0e377ae2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -878,6 +878,7 @@ #include "code\datums\ai\basic_mobs\base_basic_controller.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\basic_attacking.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\climb_tree.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\find_mineable_wall.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\find_parent.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\nearest_targetting.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\pick_up_item.dm" @@ -4154,7 +4155,6 @@ #include "code\modules\mining\machine_stacking.dm" #include "code\modules\mining\machine_unloading.dm" #include "code\modules\mining\mine_items.dm" -#include "code\modules\mining\minebot.dm" #include "code\modules\mining\money_bag.dm" #include "code\modules\mining\ores_coins.dm" #include "code\modules\mining\satchel_ore_boxdm.dm" @@ -4285,6 +4285,10 @@ #include "code\modules\mob\living\basic\lavaland\watcher\watcher_gaze.dm" #include "code\modules\mob\living\basic\lavaland\watcher\watcher_overwatch.dm" #include "code\modules\mob\living\basic\lavaland\watcher\watcher_projectiles.dm" +#include "code\modules\mob\living\basic\minebots\minebot.dm" +#include "code\modules\mob\living\basic\minebots\minebot_abilities.dm" +#include "code\modules\mob\living\basic\minebots\minebot_ai.dm" +#include "code\modules\mob\living\basic\minebots\minebot_upgrades.dm" #include "code\modules\mob\living\basic\pets\fox.dm" #include "code\modules\mob\living\basic\pets\penguin.dm" #include "code\modules\mob\living\basic\pets\pet.dm" diff --git a/tools/UpdatePaths/Scripts/78032_miningdrone.txt b/tools/UpdatePaths/Scripts/78032_miningdrone.txt new file mode 100644 index 00000000000..2ae5cefe8fb --- /dev/null +++ b/tools/UpdatePaths/Scripts/78032_miningdrone.txt @@ -0,0 +1 @@ +/mob/living/simple_animal/hostile/mining_drone : /mob/living/basic/mining_drone{@OLD} \ No newline at end of file