mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-27 18:12:00 +00:00
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  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  ## 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:
@@ -128,6 +128,10 @@
|
||||
#define BB_MINEBOT_DUMP_ABILITY "minebot_dump_ability"
|
||||
/// key that stores our target turf
|
||||
#define BB_TARGET_MINERAL_TURF "target_mineral_turf"
|
||||
///key that holds our missile ability
|
||||
#define BB_MINEBOT_MISSILE_ABILITY "minebot_missile_ability"
|
||||
///key that holds our landmine ability
|
||||
#define BB_MINEBOT_LANDMINE_ABILITY "minebot_landmine_ability"
|
||||
/// key that stores list of the turfs we ignore
|
||||
#define BB_BLACKLIST_MINERAL_TURFS "blacklist_mineral_turfs"
|
||||
/// key that stores the previous blocked wall
|
||||
@@ -136,6 +140,20 @@
|
||||
#define BB_AUTOMATED_MINING "automated_mining"
|
||||
/// key that stores the nearest dead human
|
||||
#define BB_NEARBY_DEAD_MINER "nearby_dead_miner"
|
||||
///key that holds the drone we defend
|
||||
#define BB_DRONE_DEFEND "defend_drone"
|
||||
///key that holds the minimum distance before we flee
|
||||
#define BB_MINIMUM_SHOOTING_DISTANCE "minimum_shooting_distance"
|
||||
///key that holds the miner we must befriend
|
||||
#define BB_MINER_FRIEND "miner_friend"
|
||||
///key that holds the missile target
|
||||
#define BB_MINEBOT_MISSILE_TARGET "minebot_missile_target"
|
||||
///should we auto protect?
|
||||
#define BB_MINEBOT_AUTO_DEFEND "minebot_auto_defend"
|
||||
///should we repair drones?
|
||||
#define BB_MINEBOT_REPAIR_DRONE "minebot_repair_drone"
|
||||
///should we plant mines?
|
||||
#define BB_MINEBOT_PLANT_MINES "minebot_plant_mines"
|
||||
|
||||
//seedling keys
|
||||
/// the water can we will pick up
|
||||
|
||||
@@ -1080,6 +1080,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
///Trait granted by janitor skillchip, allows communication with cleanbots
|
||||
#define TRAIT_CLEANBOT_WHISPERER "cleanbot_whisperer"
|
||||
|
||||
///Trait granted by the miner skillchip, allows communication with minebots
|
||||
#define TRAIT_ROCK_STONER "rock_stoner"
|
||||
|
||||
///Trait given by the regenerative shield component
|
||||
#define TRAIT_REGEN_SHIELD "regen_shield"
|
||||
|
||||
/// Trait given when a mob is currently in invisimin mode
|
||||
#define TRAIT_INVISIMIN "invisimin"
|
||||
|
||||
|
||||
@@ -386,6 +386,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_RDS_SUPPRESSED" = TRAIT_RDS_SUPPRESSED,
|
||||
"TRAIT_REAGENT_SCANNER" = TRAIT_REAGENT_SCANNER,
|
||||
"TRAIT_RECENTLY_BLOCKED_MAGIC" = TRAIT_RECENTLY_BLOCKED_MAGIC,
|
||||
"TRAIT_REGEN_SHIELD" = TRAIT_REGEN_SHIELD,
|
||||
"TRAIT_RELAYING_ATTACKER" = TRAIT_RELAYING_ATTACKER,
|
||||
"TRAIT_REMOTE_TASTING" = TRAIT_REMOTE_TASTING,
|
||||
"TRAIT_RESEARCH_SCANNER" = TRAIT_RESEARCH_SCANNER,
|
||||
@@ -401,6 +402,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_RIFT_FAILURE" = TRAIT_RIFT_FAILURE,
|
||||
"TRAIT_ROCK_EATER" = TRAIT_ROCK_EATER,
|
||||
"TRAIT_ROCK_METAMORPHIC" = TRAIT_ROCK_METAMORPHIC,
|
||||
"TRAIT_ROCK_STONER" = TRAIT_ROCK_STONER,
|
||||
"TRAIT_ROD_SUPLEX" = TRAIT_ROD_SUPLEX,
|
||||
"TRAIT_SABRAGE_PRO" = TRAIT_SABRAGE_PRO,
|
||||
"TRAIT_SECURITY_HUD" = TRAIT_SECURITY_HUD,
|
||||
|
||||
@@ -64,3 +64,15 @@
|
||||
. = ..()
|
||||
if (clear_failed_targets)
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
/datum/ai_behavior/run_away_from_target/run_and_shoot
|
||||
clear_failed_targets = FALSE
|
||||
|
||||
/datum/ai_behavior/run_away_from_target/run_and_shoot/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hiding_location_key)
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
finish_action(controller, succeeded = FALSE, target_key = target_key, hiding_location_key = hiding_location_key)
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
living_pawn.RangedAttack(target)
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -247,6 +247,8 @@
|
||||
set_command_target(parent, victim)
|
||||
|
||||
/datum/pet_command/protect_owner/proc/set_attacking_target(atom/source, mob/living/attacker)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/mob/living/basic/owner = weak_parent.resolve()
|
||||
if(isnull(owner))
|
||||
return
|
||||
|
||||
91
code/datums/components/regenerative_shield.dm
Normal file
91
code/datums/components/regenerative_shield.dm
Normal file
@@ -0,0 +1,91 @@
|
||||
#define SHIELD_FILTER "shield filter"
|
||||
|
||||
/// gives the mobs a regenerative shield, it will tank hits for them and then need to recharge for a bit
|
||||
/datum/component/regenerative_shield
|
||||
///number of hits we can tank
|
||||
var/number_of_hits = 15
|
||||
///the limit of the damage we can tank
|
||||
var/damage_threshold
|
||||
///the overlay of the shield
|
||||
var/list/shield_overlays = list()
|
||||
///how long before the shield can regenerate
|
||||
var/regeneration_time
|
||||
|
||||
/datum/component/regenerative_shield/Initialize(number_of_hits = 15, damage_threshold = 50, regeneration_time = 2 MINUTES, list/shield_overlays)
|
||||
if(!isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
src.number_of_hits = number_of_hits
|
||||
src.damage_threshold = damage_threshold
|
||||
src.regeneration_time = regeneration_time
|
||||
|
||||
var/atom/movable/living_parent = parent
|
||||
for(var/type_path as anything in shield_overlays)
|
||||
if(!ispath(type_path))
|
||||
continue
|
||||
var/obj/effect/overlay/new_effect = new type_path()
|
||||
living_parent.vis_contents += new_effect
|
||||
apply_filter_effects(new_effect)
|
||||
src.shield_overlays += new_effect
|
||||
|
||||
/datum/component/regenerative_shield/RegisterWithParent()
|
||||
. = ..()
|
||||
ADD_TRAIT(parent, TRAIT_REGEN_SHIELD, REF(src))
|
||||
RegisterSignal(parent, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(block_attack))
|
||||
|
||||
/datum/component/regenerative_shield/UnregisterFromParent()
|
||||
var/atom/movable/living_parent = parent
|
||||
for(var/obj/effect/overlay as anything in shield_overlays)
|
||||
living_parent.vis_contents -= overlay
|
||||
QDEL_LIST(shield_overlays)
|
||||
UnregisterSignal(parent, COMSIG_LIVING_CHECK_BLOCK)
|
||||
REMOVE_TRAIT(parent, TRAIT_REGEN_SHIELD, REF(src))
|
||||
return ..()
|
||||
|
||||
/datum/component/regenerative_shield/proc/block_attack(
|
||||
mob/living/source,
|
||||
atom/hitby,
|
||||
damage,
|
||||
attack_text,
|
||||
attack_type,
|
||||
armour_penetration,
|
||||
damage_type,
|
||||
attack_flag,
|
||||
)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(damage <= 0 ||damage_type == STAMINA)
|
||||
return NONE
|
||||
|
||||
if(damage >= damage_threshold || number_of_hits <= 0)
|
||||
return NONE
|
||||
|
||||
playsound(get_turf(parent), 'sound/weapons/tap.ogg', 20)
|
||||
new /obj/effect/temp_visual/guardian/phase/out(get_turf(parent))
|
||||
number_of_hits = max(0, number_of_hits - 1)
|
||||
if(number_of_hits <= 0)
|
||||
disable_shield()
|
||||
return SUCCESSFUL_BLOCK
|
||||
|
||||
/datum/component/regenerative_shield/proc/disable_shield()
|
||||
addtimer(CALLBACK(src, PROC_REF(enable_shield)), regeneration_time)
|
||||
for(var/obj/effect/my_effect as anything in shield_overlays)
|
||||
animate(my_effect, alpha = 0, time = 3 SECONDS)
|
||||
my_effect.remove_filter(SHIELD_FILTER)
|
||||
playsound(parent, 'sound/mecha/mech_shield_drop.ogg', 20)
|
||||
|
||||
/datum/component/regenerative_shield/proc/enable_shield()
|
||||
number_of_hits = initial(number_of_hits)
|
||||
for(var/obj/effect/my_effect as anything in shield_overlays)
|
||||
animate(my_effect, alpha = 255, time = 3 SECONDS)
|
||||
addtimer(CALLBACK(src, PROC_REF(apply_filter_effects), my_effect), 5 SECONDS)
|
||||
playsound(parent, 'sound/mecha/mech_shield_raise.ogg', 20)
|
||||
|
||||
/datum/component/regenerative_shield/proc/apply_filter_effects(obj/effect/new_effect)
|
||||
if(isnull(new_effect))
|
||||
return
|
||||
new_effect.add_filter(SHIELD_FILTER, 1, list("type" = "outline", "color" = "#b6e6f3", "alpha" = 0, "size" = 1))
|
||||
var/filter = new_effect.get_filter(SHIELD_FILTER)
|
||||
animate(filter, alpha = 200, time = 0.5 SECONDS, loop = -1)
|
||||
animate(alpha = 0, time = 0.5 SECONDS)
|
||||
|
||||
#undef SHIELD_FILTER
|
||||
29
code/datums/elements/mob_access.dm
Normal file
29
code/datums/elements/mob_access.dm
Normal file
@@ -0,0 +1,29 @@
|
||||
///element given to mobs that have levels of access
|
||||
/datum/element/mob_access
|
||||
element_flags = ELEMENT_BESPOKE
|
||||
argument_hash_start_idx = 2
|
||||
/// What can this mob access?
|
||||
var/list/my_access
|
||||
|
||||
/datum/element/mob_access/Attach(datum/target, list/accesses)
|
||||
. = ..()
|
||||
if(!isliving(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
for(var/access_path in accesses)
|
||||
if(!ispath(access_path))
|
||||
continue
|
||||
var/datum/id_trim/job/trim = SSid_access.trim_singletons_by_path[access_path]
|
||||
if(isnull(trim))
|
||||
continue
|
||||
my_access += trim.access
|
||||
|
||||
RegisterSignal(target, COMSIG_MOB_TRIED_ACCESS, PROC_REF(attempt_access))
|
||||
|
||||
/datum/element/mob_access/proc/attempt_access(datum/source, obj/door_attempt)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
return (door_attempt.check_access_list(my_access)) ? ACCESS_ALLOWED : ACCESS_DISALLOWED
|
||||
|
||||
/datum/element/mob_access/Detach(datum/source, ...)
|
||||
UnregisterSignal(source, COMSIG_MOB_TRIED_ACCESS)
|
||||
return ..()
|
||||
@@ -21,6 +21,14 @@
|
||||
item_path = /obj/item/mine_bot_upgrade/health
|
||||
cost_per_order = 350
|
||||
|
||||
/datum/orderable_item/toys_drones/drone_shield
|
||||
item_path = /obj/item/mine_bot_upgrade/regnerative_shield
|
||||
cost_per_order = 500
|
||||
|
||||
/datum/orderable_item/toys_drones/drone_shield
|
||||
item_path = /obj/item/minebot_remote_control
|
||||
cost_per_order = 500
|
||||
|
||||
/datum/orderable_item/toys_drones/drone_pka
|
||||
item_path = /obj/item/borg/upgrade/modkit/cooldown/minebot
|
||||
cost_per_order = 525
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
id_trim = /datum/id_trim/job/shaft_miner
|
||||
uniform = /obj/item/clothing/under/rank/cargo/miner/lavaland
|
||||
skillchips = list(/obj/item/skillchip/job/miner)
|
||||
backpack_contents = list(
|
||||
/obj/item/flashlight/seclite = 1,
|
||||
/obj/item/knife/combat/survival = 1,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/obj/item/skillchip/job/miner
|
||||
name = "TUNN3L_R4T skillchip"
|
||||
desc = "Gain control of minebots."
|
||||
auto_traits = list(TRAIT_ROCK_STONER)
|
||||
skill_name = "Battlebot enthusiast"
|
||||
skill_description = "Lead minebots into war."
|
||||
activate_message = span_notice("A newfound obsession with battlebots fosters within you.")
|
||||
deactivate_message = span_notice("You finally get over your battlebots phase, now go get a job.")
|
||||
@@ -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
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 81 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 64 KiB |
BIN
sound/weapons/minebot_rocket.ogg
Normal file
BIN
sound/weapons/minebot_rocket.ogg
Normal file
Binary file not shown.
@@ -1153,6 +1153,7 @@
|
||||
#include "code\datums\components\recharging_attacks.dm"
|
||||
#include "code\datums\components\redirect_attack_hand_from_turf.dm"
|
||||
#include "code\datums\components\reflection.dm"
|
||||
#include "code\datums\components\regenerative_shield.dm"
|
||||
#include "code\datums\components\regenerator.dm"
|
||||
#include "code\datums\components\religious_tool.dm"
|
||||
#include "code\datums\components\rename.dm"
|
||||
@@ -1442,6 +1443,7 @@
|
||||
#include "code\datums\elements\light_eater.dm"
|
||||
#include "code\datums\elements\loomable.dm"
|
||||
#include "code\datums\elements\mirage_border.dm"
|
||||
#include "code\datums\elements\mob_access.dm"
|
||||
#include "code\datums\elements\mob_grabber.dm"
|
||||
#include "code\datums\elements\mob_killed_tally.dm"
|
||||
#include "code\datums\elements\move_force_on_death.dm"
|
||||
@@ -4337,6 +4339,7 @@
|
||||
#include "code\modules\library\skill_learning\job_skillchips\_job.dm"
|
||||
#include "code\modules\library\skill_learning\job_skillchips\chef.dm"
|
||||
#include "code\modules\library\skill_learning\job_skillchips\janitor.dm"
|
||||
#include "code\modules\library\skill_learning\job_skillchips\miner.dm"
|
||||
#include "code\modules\library\skill_learning\job_skillchips\psychologist.dm"
|
||||
#include "code\modules\library\skill_learning\job_skillchips\research_director.dm"
|
||||
#include "code\modules\library\skill_learning\job_skillchips\roboticist.dm"
|
||||
@@ -4717,6 +4720,7 @@
|
||||
#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_remote_control.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"
|
||||
|
||||
197
tgui/packages/tgui/interfaces/MineBot.tsx
Normal file
197
tgui/packages/tgui/interfaces/MineBot.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
import { BooleanLike } from 'common/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useBackend } from '../backend';
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
Image,
|
||||
LabeledList,
|
||||
NumberInput,
|
||||
ProgressBar,
|
||||
Section,
|
||||
Stack,
|
||||
} from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
type Data = {
|
||||
auto_defend: BooleanLike;
|
||||
repair_node_drone: BooleanLike;
|
||||
plant_mines: BooleanLike;
|
||||
bot_mode: BooleanLike;
|
||||
bot_name: string;
|
||||
bot_health: number;
|
||||
bot_maintain_distance: number;
|
||||
bot_maxhealth: number;
|
||||
bot_icon: string;
|
||||
bot_color: string;
|
||||
possible_colors: Possible_Colors[];
|
||||
};
|
||||
|
||||
type Possible_Colors = {
|
||||
color_name: string;
|
||||
color_value: string;
|
||||
};
|
||||
|
||||
export const MineBot = (props) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
const {
|
||||
auto_defend,
|
||||
repair_node_drone,
|
||||
plant_mines,
|
||||
bot_name,
|
||||
bot_health,
|
||||
bot_mode,
|
||||
bot_maxhealth,
|
||||
possible_colors,
|
||||
bot_maintain_distance,
|
||||
bot_color,
|
||||
bot_icon,
|
||||
} = data;
|
||||
const possibleColorList = {};
|
||||
for (const index in possible_colors) {
|
||||
const color = possible_colors[index];
|
||||
possibleColorList[color.color_name] = color;
|
||||
}
|
||||
const [selectedDistance, setSelectedDistance] = useState(
|
||||
bot_maintain_distance,
|
||||
);
|
||||
const [selectedColor, setSelectedColor] = useState(
|
||||
possibleColorList[bot_color],
|
||||
);
|
||||
return (
|
||||
<Window title="Minebot Settings" width={625} height={328} theme="hackerman">
|
||||
<Window.Content>
|
||||
<Stack>
|
||||
<Stack.Item width="50%">
|
||||
<Section
|
||||
textAlign="center"
|
||||
title={bot_name}
|
||||
buttons={
|
||||
<Button.Input
|
||||
color="transparent"
|
||||
onCommit={(e, value) =>
|
||||
act('set_name', {
|
||||
chosen_name: value,
|
||||
})
|
||||
}
|
||||
>
|
||||
Rename
|
||||
</Button.Input>
|
||||
}
|
||||
>
|
||||
<Stack vertical>
|
||||
<Stack.Item>
|
||||
<Image
|
||||
m={1}
|
||||
src={`data:image/jpeg;base64,${bot_icon}`}
|
||||
height="160px"
|
||||
width="160px"
|
||||
style={{
|
||||
verticalAlign: 'middle',
|
||||
borderRadius: '1em',
|
||||
border: '1px solid green',
|
||||
}}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item ml="25%">
|
||||
<Dropdown
|
||||
width="65%"
|
||||
displayText={selectedColor?.color_name}
|
||||
options={possible_colors.map((possible_color) => {
|
||||
return possible_color.color_name;
|
||||
})}
|
||||
onSelected={(selected) =>
|
||||
setSelectedColor(possibleColorList[selected])
|
||||
}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item textAlign="center">
|
||||
<Button
|
||||
textAlign="center"
|
||||
width="50%"
|
||||
style={{ padding: '3px' }}
|
||||
onClick={() =>
|
||||
act('set_color', {
|
||||
chosen_color: selectedColor?.color_value,
|
||||
})
|
||||
}
|
||||
>
|
||||
Apply Color
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item width="50%" textAlign="center">
|
||||
<Section title="Configurations">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Health">
|
||||
<ProgressBar
|
||||
value={bot_health}
|
||||
maxValue={bot_maxhealth}
|
||||
color="white"
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Mode">
|
||||
<Button
|
||||
textAlign="center"
|
||||
width="50%"
|
||||
style={{ padding: '3px' }}
|
||||
onClick={() => act('toggle_mode')}
|
||||
>
|
||||
{bot_mode ? 'Combat' : 'Safe'}
|
||||
</Button>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Repair Node Drones">
|
||||
<Button
|
||||
textAlign="center"
|
||||
width="50%"
|
||||
style={{ padding: '3px' }}
|
||||
onClick={() => act('toggle_repair')}
|
||||
>
|
||||
{repair_node_drone ? 'Repair' : 'Ignore'}
|
||||
</Button>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Plant Mines">
|
||||
<Button
|
||||
textAlign="center"
|
||||
width="50%"
|
||||
style={{ padding: '3px' }}
|
||||
onClick={() => act('toggle_mines')}
|
||||
>
|
||||
{plant_mines ? 'On' : 'Off'}
|
||||
</Button>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Auto protect">
|
||||
<Button
|
||||
textAlign="center"
|
||||
width="50%"
|
||||
style={{ padding: '3px' }}
|
||||
onClick={() => act('toggle_defend')}
|
||||
>
|
||||
{auto_defend ? 'On' : 'Off'}
|
||||
</Button>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Distance To Maintain">
|
||||
<NumberInput
|
||||
width="50%"
|
||||
value={selectedDistance}
|
||||
minValue={0}
|
||||
step={1}
|
||||
maxValue={5}
|
||||
onChange={(value) =>
|
||||
act('change_min_distance', {
|
||||
distance: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user