mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-20 03:25:49 +01:00
port minebots to basic mobs and add some behavior (#29319)
* port minebots to basic mobs and add some behavior * remove unused define * update is_blocked_turf usage * fix radio, goldgrub, and armor upgrade * standardize blackboard names * Apply suggestions from code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: warriorstar-orion <orion@snowfrost.garden> * lewc review * whoops * Apply suggestions from code review Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: warriorstar-orion <orion@snowfrost.garden> --------- Signed-off-by: warriorstar-orion <orion@snowfrost.garden> Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
586e2e6c4d
commit
e63415a483
@@ -48,6 +48,8 @@ RESTRICT_TYPE(/mob/living/basic)
|
||||
var/gold_core_spawnable = NO_SPAWN
|
||||
/// Holding var for determining who own/controls a sentient simple animal (for sentience potions).
|
||||
var/mob/living/carbon/human/master_commander = null
|
||||
/// Sentience type, for slime potions
|
||||
var/sentience_type = SENTIENCE_ORGANIC
|
||||
|
||||
/// Higher speed is slower, negative speed is faster
|
||||
var/speed = 1
|
||||
@@ -299,3 +301,35 @@ RESTRICT_TYPE(/mob/living/basic)
|
||||
else
|
||||
apply_damage(damage, damagetype, null, getarmor(null, armorcheck))
|
||||
return TRUE
|
||||
|
||||
// Health/Damage adjustment, cribbed straight from simplemobs
|
||||
|
||||
/mob/living/basic/adjustHealth(amount, updating_health = TRUE)
|
||||
. = ..()
|
||||
if(!ckey && stat == CONSCIOUS)
|
||||
if(ai_controller?.ai_status == AI_STATUS_IDLE)
|
||||
ai_controller.set_ai_status(AI_STATUS_ON)
|
||||
|
||||
/mob/living/basic/adjustBruteLoss(amount, updating_health = TRUE)
|
||||
if(damage_coeff[BRUTE])
|
||||
return adjustHealth(amount * damage_coeff[BRUTE], updating_health)
|
||||
|
||||
/mob/living/basic/adjustFireLoss(amount, updating_health = TRUE)
|
||||
if(damage_coeff[BURN])
|
||||
return adjustHealth(amount * damage_coeff[BURN], updating_health)
|
||||
|
||||
/mob/living/basic/adjustOxyLoss(amount, updating_health = TRUE)
|
||||
if(damage_coeff[OXY])
|
||||
return adjustHealth(amount * damage_coeff[OXY], updating_health)
|
||||
|
||||
/mob/living/basic/adjustToxLoss(amount, updating_health = TRUE)
|
||||
if(damage_coeff[TOX])
|
||||
return adjustHealth(amount * damage_coeff[TOX], updating_health)
|
||||
|
||||
/mob/living/basic/adjustCloneLoss(amount, updating_health = TRUE)
|
||||
if(damage_coeff[CLONE])
|
||||
return adjustHealth(amount * damage_coeff[CLONE], updating_health)
|
||||
|
||||
/mob/living/basic/adjustStaminaLoss(amount, updating_health = TRUE)
|
||||
if(damage_coeff[STAMINA])
|
||||
return ..(amount * damage_coeff[STAMINA], updating_health)
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/mob/living/basic/mining_drone
|
||||
name = "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. A mining scanner can instruct it to drop loose ore. Field repairs can be done with a welder."
|
||||
gender = NEUTER
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "mining_drone"
|
||||
icon_living = "mining_drone"
|
||||
status_flags = CANSTUN|CANWEAKEN|CANPUSH
|
||||
mouse_opacity = MOUSE_OPACITY_ICON
|
||||
basic_mob_flags = DEL_ON_DEATH
|
||||
sentience_type = SENTIENCE_MINEBOT
|
||||
faction = list("neutral", "goldgrub") // goldgrubs are invulnerable to PKA fire
|
||||
weather_immunities = list("ash")
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 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
|
||||
maximum_survivable_temperature = INFINITY
|
||||
attack_sound = 'sound/weapons/circsawhit.ogg'
|
||||
speak_emote = list("states")
|
||||
healable = FALSE
|
||||
ai_controller = /datum/ai_controller/basic_controller/minebot
|
||||
hud_type = /datum/hud/simple_animal
|
||||
var/light_on = FALSE
|
||||
var/obj/item/gun/energy/kinetic_accelerator/minebot/stored_gun
|
||||
var/obj/item/radio/radio
|
||||
/// The commands our owner can give us
|
||||
var/static/list/pet_commands = list(
|
||||
/datum/pet_command/idle/minebot,
|
||||
/datum/pet_command/move,
|
||||
/datum/pet_command/protect_owner/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/attack/minebot,
|
||||
)
|
||||
|
||||
/mob/living/basic/mining_drone/Initialize(mapload)
|
||||
. = ..()
|
||||
stored_gun = new(src)
|
||||
var/static/list/innate_actions = list(
|
||||
/datum/action/innate/minedrone/toggle_light = BB_MINEBOT_LIGHT_ABILITY,
|
||||
/datum/action/innate/minedrone/toggle_meson_vision = null,
|
||||
/datum/action/innate/minedrone/dump_ore = BB_MINEBOT_DUMP_ABILITY,
|
||||
)
|
||||
|
||||
grant_actions_by_list(innate_actions)
|
||||
|
||||
radio = new(src)
|
||||
radio.listening = FALSE
|
||||
radio.follow_target = src
|
||||
radio.config(list("Supply" = TRUE))
|
||||
|
||||
AddComponent(/datum/component/footstep, FOOTSTEP_OBJ_ROBOT)
|
||||
AddComponent(/datum/component/obeys_commands, pet_commands)
|
||||
|
||||
/mob/living/basic/mining_drone/Destroy()
|
||||
QDEL_NULL(stored_gun)
|
||||
drop_ore(message = FALSE)
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/mining_drone/emp_act(severity)
|
||||
adjustHealth(100 / severity)
|
||||
to_chat(src, "<span class='userdanger'>NOTICE: EMP detected, systems damaged!</span>")
|
||||
visible_message("<span class='warning'>[src] crackles and buzzes violently!</span>")
|
||||
|
||||
/mob/living/basic/mining_drone/examine(mob/user)
|
||||
. = ..()
|
||||
var/t_He = p_they(TRUE)
|
||||
var/t_s = p_s()
|
||||
if(health < maxHealth)
|
||||
if(health >= maxHealth * 0.5)
|
||||
. += "<span class='warning'>[t_He] look[t_s] slightly dented.</span>"
|
||||
else
|
||||
. += "<span class='boldwarning'>[t_He] look[t_s] severely dented!</span>"
|
||||
var/ore_count = 0
|
||||
for(var/obj/item/stack/ore/ore_stack in contents)
|
||||
ore_count += ore_stack.amount
|
||||
|
||||
. += "<span class='notice'>[t_He] is currently storing [ore_count] ore.</span>"
|
||||
|
||||
if(stored_gun && stored_gun.max_mod_capacity)
|
||||
. += "<span class='notice'><b>[stored_gun.get_remaining_mod_capacity()]%</b> mod capacity remaining.</span>"
|
||||
for(var/A in stored_gun.get_modkits())
|
||||
var/obj/item/borg/upgrade/modkit/M = A
|
||||
. += "<span class='notice'>There is \a [M] installed, using <b>[M.cost]%</b> capacity.</span>"
|
||||
|
||||
/mob/living/basic/mining_drone/item_interaction(mob/living/user, obj/item/I, list/modifiers)
|
||||
if(istype(I, /obj/item/mining_scanner) || istype(I, /obj/item/t_scanner/adv_mining_scanner))
|
||||
to_chat(user, "<span class='notice'>You instruct [src] to drop any collected ore.</span>")
|
||||
drop_ore()
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
if(istype(I, /obj/item/borg/upgrade/modkit))
|
||||
I.melee_attack_chain(user, stored_gun, list2params(modifiers))
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/mining_drone/crowbar_act(mob/user, obj/item/I)
|
||||
if(user.a_intent != INTENT_HELP)
|
||||
return
|
||||
. = TRUE
|
||||
if(!I.tool_start_check(src, user, 0))
|
||||
return
|
||||
I.melee_attack_chain(user, stored_gun)
|
||||
|
||||
/mob/living/basic/mining_drone/welder_act(mob/user, obj/item/I)
|
||||
if(user.a_intent != INTENT_HELP)
|
||||
return
|
||||
. = TRUE
|
||||
if(health == maxHealth)
|
||||
to_chat(user, "<span class='notice'>[src] doesn't need repairing!</span>")
|
||||
return
|
||||
if(!I.tool_use_check(user, 1))
|
||||
return
|
||||
WELDER_ATTEMPT_REPAIR_MESSAGE
|
||||
if(I.use_tool(src, user, 15, 1, volume = I.tool_volume) && health != maxHealth)
|
||||
adjustBruteLoss(-20)
|
||||
WELDER_REPAIR_SUCCESS_MESSAGE
|
||||
|
||||
/mob/living/basic/mining_drone/death()
|
||||
drop_ore(message = FALSE)
|
||||
if(stored_gun)
|
||||
for(var/obj/item/borg/upgrade/modkit/M in stored_gun.modkits)
|
||||
M.uninstall(stored_gun)
|
||||
robogibs(loc)
|
||||
deathmessage = "blows apart!"
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/mining_drone/CanPass(atom/movable/O)
|
||||
if(istype(O, /obj/item/projectile/kinetic))
|
||||
var/obj/item/projectile/kinetic/K = O
|
||||
if(K.kinetic_gun)
|
||||
for(var/A in K.kinetic_gun.get_modkits())
|
||||
var/obj/item/borg/upgrade/modkit/M = A
|
||||
if(istype(M, /obj/item/borg/upgrade/modkit/minebot_passthrough))
|
||||
return TRUE
|
||||
if(istype(O, /obj/item/projectile/destabilizer))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/mining_drone/RangedAttack(atom/target, list/modifiers)
|
||||
if(a_intent != INTENT_HARM)
|
||||
return
|
||||
stored_gun.afterattack__legacy__attackchain(target, src)
|
||||
|
||||
/mob/living/basic/mining_drone/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
|
||||
. = ..()
|
||||
|
||||
if(!. || !proximity_flag || (a_intent == INTENT_HARM))
|
||||
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(message = TRUE)
|
||||
if(!length(contents))
|
||||
if(message)
|
||||
to_chat(src, "<span class='warning'>You attempt to dump your stored ore, but you have none.</span>")
|
||||
return
|
||||
if(message)
|
||||
to_chat(src, "<span class='notice'>You dump your stored ore.</span>")
|
||||
for(var/obj/item/stack/ore/O in contents)
|
||||
O.forceMove(drop_location())
|
||||
|
||||
// MARK: Minebot Cube
|
||||
|
||||
/obj/item/mining_drone_cube
|
||||
name = "mining drone cube"
|
||||
desc = "Compressed mining drone, ready for deployment. Just press the button to activate!"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "minedronecube"
|
||||
item_state = "electronic"
|
||||
|
||||
/obj/item/mining_drone_cube/attack_self__legacy__attackchain(mob/user)
|
||||
user.visible_message("<span class='warning'>\The [src] suddenly expands into a fully functional mining drone!</span>", \
|
||||
"<span class='warning'>You press center button on [src]. The device suddenly expands into a fully functional mining drone!</span>")
|
||||
var/mob/living/basic/mining_drone/drone = new(get_turf(src))
|
||||
drone.befriend(user)
|
||||
qdel(src)
|
||||
@@ -0,0 +1,48 @@
|
||||
/datum/action/innate/minedrone
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
button_background_icon_state = "bg_default"
|
||||
|
||||
/datum/action/innate/minedrone/toggle_light
|
||||
name = "Toggle Light"
|
||||
button_overlay_icon_state = "mech_lights_off"
|
||||
|
||||
/datum/action/innate/minedrone/toggle_light/Activate()
|
||||
var/mob/living/basic/mining_drone/user = owner
|
||||
|
||||
if(user.light_on)
|
||||
user.set_light(0)
|
||||
else
|
||||
user.set_light(6)
|
||||
user.light_on = !user.light_on
|
||||
to_chat(user, "<span class='notice'>You toggle your light [user.light_on ? "on" : "off"].</span>")
|
||||
|
||||
/datum/action/innate/minedrone/toggle_meson_vision
|
||||
name = "Toggle Meson Vision"
|
||||
button_overlay_icon_state = "meson"
|
||||
|
||||
/datum/action/innate/minedrone/toggle_meson_vision/Activate()
|
||||
var/mob/living/user = owner
|
||||
var/active = TRUE
|
||||
if(HAS_TRAIT_FROM(user, TRAIT_MESON_VISION, "minebot"))
|
||||
active = FALSE
|
||||
REMOVE_TRAIT(user, TRAIT_MESON_VISION, "minebot")
|
||||
else
|
||||
ADD_TRAIT(user, TRAIT_MESON_VISION, "minebot")
|
||||
|
||||
update_user_sight(user)
|
||||
to_chat(user, "<span class='notice'>You toggle your meson vision [!active ? "on" : "off"].</span>")
|
||||
|
||||
/datum/action/innate/minedrone/toggle_meson_vision/proc/update_user_sight(mob/living/user)
|
||||
user.sight = initial(user.sight)
|
||||
user.lighting_alpha = initial(user.lighting_alpha)
|
||||
if(HAS_TRAIT(user, TRAIT_MESON_VISION))
|
||||
user.sight |= SEE_TURFS
|
||||
user.lighting_alpha = min(user.lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
|
||||
|
||||
/datum/action/innate/minedrone/dump_ore
|
||||
name = "Dump Ore"
|
||||
button_overlay_icon_state = "mech_eject"
|
||||
|
||||
/datum/action/innate/minedrone/dump_ore/Activate()
|
||||
var/mob/living/basic/mining_drone/user = owner
|
||||
user.drop_ore()
|
||||
@@ -0,0 +1,306 @@
|
||||
/datum/ai_controller/basic_controller/minebot
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/always_check_factions,
|
||||
BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends,
|
||||
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(),
|
||||
BB_AUTOMATED_MINING = FALSE,
|
||||
BB_OWNER_SELF_HARM_RESPONSES = list(
|
||||
"Please stop hurting yourself.",
|
||||
"There is no need to do that.",
|
||||
"Your actions are illogical.",
|
||||
"Please make better choices.",
|
||||
"Remember, you have beaten your worst days before.",
|
||||
),
|
||||
BB_OWNER_FRIENDLY_FIRE_APOLOGIES = list(
|
||||
"Sorry.",
|
||||
"My fault.",
|
||||
"Oops.",
|
||||
),
|
||||
)
|
||||
|
||||
ai_movement = /datum/ai_movement/jps
|
||||
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/befriend_miners,
|
||||
/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/flee_target/minebot_gibtonite,
|
||||
/datum/ai_planning_subtree/locate_dead_humans,
|
||||
)
|
||||
ai_traits = AI_FLAG_PAUSE_DURING_DO_AFTER
|
||||
|
||||
/datum/ai_planning_subtree/befriend_miners/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(!controller.blackboard_key_exists(BB_MINER_FRIEND))
|
||||
return
|
||||
controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_MINER_FRIEND)
|
||||
|
||||
/// Find dead humans and report their location on the radio
|
||||
/datum/ai_planning_subtree/locate_dead_humans/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard_key_exists(BB_NEARBY_DEAD_MINER))
|
||||
controller.queue_behavior(/datum/ai_behavior/send_sos_message, BB_NEARBY_DEAD_MINER)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
controller.queue_behavior(/datum/ai_behavior/find_and_set/unconscious_human, BB_NEARBY_DEAD_MINER, /mob/living/carbon/human)
|
||||
|
||||
/datum/ai_behavior/find_and_set/unconscious_human/search_tactic(datum/ai_controller/controller, locate_path, search_range)
|
||||
// For now, we only look for friends, and we only befriend the person
|
||||
// who created us, so we're not screaming on comms about every single
|
||||
// dead player that we find
|
||||
if(!controller.blackboard_key_exists(BB_FRIENDS_LIST))
|
||||
return null
|
||||
for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn))
|
||||
if(!(target in controller.blackboard[BB_FRIENDS_LIST]))
|
||||
continue
|
||||
if(target.stat >= UNCONSCIOUS && target.mind)
|
||||
return target
|
||||
return null
|
||||
|
||||
/datum/ai_behavior/send_sos_message
|
||||
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
action_cooldown = 2 MINUTES
|
||||
|
||||
/datum/ai_behavior/send_sos_message/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
var/mob/living/carbon/target = controller.blackboard[target_key]
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
if(QDELETED(target) || is_station_level(target.z))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
var/turf/target_turf = get_turf(target)
|
||||
var/obj/item/radio/radio = locate(/obj/item/radio) in living_pawn.contents
|
||||
if(!radio)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
var/message = message_to_multilingual("Miner in need of help at coordinates: [target_turf.x], [target_turf.y], [target_turf.z]!")
|
||||
radio.talk_into(living_pawn, message, "Supply")
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/send_sos_message/finish_action(datum/ai_controller/controller, success, target_key)
|
||||
. = ..()
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
/// operational datums is null because we dont use a ranged component, we use a gun in our contents
|
||||
/datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot
|
||||
operational_datums = null
|
||||
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/minebot
|
||||
|
||||
/datum/ai_planning_subtree/basic_ranged_attack_subtree/minebot/select_behaviors(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.a_intent != INTENT_HARM)
|
||||
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/select_behaviors(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, BB_BASIC_MOB_CURRENT_TARGET)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/datum/ai_behavior/basic_ranged_attack/minebot
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
avoid_friendly_fire = TRUE
|
||||
/// if our target is closer than this distance, finish action
|
||||
var/minimum_distance = 3
|
||||
|
||||
/datum/ai_behavior/basic_ranged_attack/minebot/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key)
|
||||
. = ..()
|
||||
minimum_distance = controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE] ? controller.blackboard[BB_MINIMUM_SHOOTING_DISTANCE] : initial(minimum_distance)
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
if(get_dist(living_pawn, target) <= minimum_distance)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/// mine walls if we are on automated mining mode
|
||||
/datum/ai_planning_subtree/minebot_mining/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(!controller.blackboard[BB_AUTOMATED_MINING])
|
||||
return
|
||||
if(controller.blackboard_key_exists(BB_MINEBOT_GIBTONITE_RUN))
|
||||
return
|
||||
if(controller.blackboard_key_exists(BB_TARGET_MINERAL_TURF))
|
||||
controller.queue_behavior(/datum/ai_behavior/minebot_mine_turf, BB_TARGET_MINERAL_TURF)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
controller.queue_behavior(/datum/ai_behavior/find_mineral_wall/minebot, BB_TARGET_MINERAL_TURF)
|
||||
|
||||
/datum/ai_behavior/find_mineral_wall/minebot
|
||||
|
||||
/datum/ai_behavior/find_mineral_wall/minebot/check_if_mineable(datum/ai_controller/controller, turf/target_wall)
|
||||
var/list/forbidden_turfs = controller.blackboard[BB_BLACKLIST_MINERAL_TURFS]
|
||||
var/turf/previous_unreachable_wall = controller.blackboard[BB_PREVIOUS_UNREACHABLE_WALL]
|
||||
if(is_type_in_list(target_wall, forbidden_turfs) || target_wall == previous_unreachable_wall)
|
||||
return FALSE
|
||||
controller.clear_blackboard_key(BB_PREVIOUS_UNREACHABLE_WALL)
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/minebot_mine_turf
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
required_distance = 2
|
||||
action_cooldown = 3 SECONDS
|
||||
|
||||
/datum/ai_behavior/minebot_mine_turf/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/turf/target = controller.blackboard[target_key]
|
||||
if(isnull(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/minebot_mine_turf/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
var/mob/living/basic/living_pawn = controller.pawn
|
||||
var/turf/target = controller.blackboard[target_key]
|
||||
|
||||
if(QDELETED(target))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
if(check_obstacles_in_path(controller, target))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
var/old_intent = living_pawn.a_intent
|
||||
if(living_pawn.a_intent != INTENT_HARM)
|
||||
living_pawn.a_intent = INTENT_HARM
|
||||
|
||||
living_pawn.face_atom(target)
|
||||
|
||||
RegisterSignal(target, COMSIG_MINE_EXPOSE_GIBTONITE, PROC_REF(on_mine_expose_gibtonite))
|
||||
|
||||
living_pawn.RangedAttack(target)
|
||||
living_pawn.a_intent = old_intent
|
||||
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/minebot_mine_turf/proc/on_mine_expose_gibtonite(datum/source, mob/living/instigator)
|
||||
SIGNAL_HANDLER // COMSIG_MINE_EXPOSE_GIBTONITE
|
||||
|
||||
instigator.emote("me", EMOTE_VISIBLE, "yelps!")
|
||||
instigator.ai_controller.set_blackboard_key(BB_MINEBOT_GIBTONITE_RUN, source)
|
||||
|
||||
/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 as anything in turfs_in_path)
|
||||
if(turf.is_blocked_turf(exclude_mobs = TRUE))
|
||||
controller.set_blackboard_key(BB_PREVIOUS_UNREACHABLE_WALL, target)
|
||||
return TRUE
|
||||
for(var/mob/living/potential_friend in turf)
|
||||
if(source == potential_friend)
|
||||
continue
|
||||
if(controller.blackboard_key_exists(BB_FRIENDS_LIST) && (potential_friend in controller.blackboard[BB_FRIENDS_LIST]))
|
||||
return TRUE
|
||||
if(source.faction_check_mob(potential_friend))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/ai_behavior/minebot_mine_turf/finish_action(datum/ai_controller/controller, success, target_key)
|
||||
. = ..()
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
/// store ores in our body
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot
|
||||
hunt_chance = 100
|
||||
hunt_range = 4
|
||||
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
// If we're not on automated mining mode, don't pick up ore
|
||||
// This is so we don't constantly pick up ore we were just told to drop
|
||||
// while in another mode
|
||||
if(!controller.blackboard[BB_AUTOMATED_MINING])
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/ai_planning_subtree/flee_target/minebot_gibtonite
|
||||
target_key = BB_MINEBOT_GIBTONITE_RUN
|
||||
|
||||
/// 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!"
|
||||
speech_commands = list("mine")
|
||||
|
||||
/datum/pet_command/automate_mining/retrieve_command_text(atom/living_pet, atom/target)
|
||||
return "signals [living_pet] to start mining!"
|
||||
|
||||
/datum/pet_command/automate_mining/execute_action(datum/ai_controller/controller)
|
||||
controller.set_blackboard_key(BB_AUTOMATED_MINING, TRUE)
|
||||
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
||||
|
||||
/datum/pet_command/minebot_ability
|
||||
command_name = "Minebot ability"
|
||||
command_desc = "Make your minebot use one of its abilities."
|
||||
/// the ability we will use
|
||||
var/ability_key
|
||||
|
||||
/datum/pet_command/minebot_ability/execute_action(datum/ai_controller/controller)
|
||||
var/datum/action/ability = controller.blackboard[ability_key]
|
||||
if(!ability?.IsAvailable())
|
||||
return
|
||||
controller.queue_behavior(/datum/ai_behavior/use_mob_ability, ability_key)
|
||||
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/datum/pet_command/minebot_ability/light
|
||||
command_name = "Toggle lights"
|
||||
command_desc = "Make your minebot toggle its lights."
|
||||
speech_commands = list("light")
|
||||
ability_key = BB_MINEBOT_LIGHT_ABILITY
|
||||
|
||||
/datum/pet_command/minebot_ability/light/retrieve_command_text(atom/living_pet, atom/target)
|
||||
return "signals [living_pet] to toggle its lights!"
|
||||
|
||||
/datum/pet_command/minebot_ability/dump
|
||||
command_name = "Dump ore"
|
||||
command_desc = "Make your minebot dump all its ore!"
|
||||
speech_commands = list("dump", "ore")
|
||||
ability_key = BB_MINEBOT_DUMP_ABILITY
|
||||
|
||||
/datum/pet_command/minebot_ability/dump/retrieve_command_text(atom/living_pet, atom/target)
|
||||
return "signals [living_pet] to dump its ore!"
|
||||
|
||||
/datum/pet_command/attack/minebot
|
||||
attack_behaviour = /datum/ai_behavior/basic_ranged_attack/minebot
|
||||
|
||||
/datum/pet_command/attack/minebot/execute_action(datum/ai_controller/controller)
|
||||
controller.set_blackboard_key(BB_AUTOMATED_MINING, FALSE)
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
if(living_pawn.a_intent != INTENT_HARM)
|
||||
living_pawn.a_intent = INTENT_HARM
|
||||
return ..()
|
||||
|
||||
/datum/pet_command/idle/minebot
|
||||
|
||||
/datum/pet_command/idle/minebot/execute_action(datum/ai_controller/controller)
|
||||
controller.set_blackboard_key(BB_AUTOMATED_MINING, FALSE)
|
||||
return ..()
|
||||
|
||||
/datum/pet_command/protect_owner/minebot
|
||||
|
||||
/datum/pet_command/protect_owner/minebot/set_command_target(mob/living/parent, atom/target)
|
||||
if(!parent.ai_controller.blackboard[BB_MINEBOT_AUTO_DEFEND])
|
||||
return FALSE
|
||||
if(!parent.ai_controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) && !QDELETED(target)) //we are already dealing with something,
|
||||
parent.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target)
|
||||
return TRUE
|
||||
|
||||
/datum/pet_command/protect_owner/minebot/execute_action(datum/ai_controller/controller)
|
||||
if(controller.blackboard[BB_MINEBOT_AUTO_DEFEND])
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
living_pawn.a_intent = INTENT_HARM
|
||||
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
||||
@@ -0,0 +1,61 @@
|
||||
/// Minebot Melee Damage Upgrade
|
||||
/obj/item/mine_bot_upgrade
|
||||
name = "minebot melee upgrade"
|
||||
desc = "A minebot upgrade."
|
||||
icon_state = "door_electronics"
|
||||
icon = 'icons/obj/doors/door_assembly.dmi'
|
||||
new_attack_chain = TRUE
|
||||
|
||||
/obj/item/mine_bot_upgrade/interact_with_atom(atom/target, mob/living/user, list/modifiers)
|
||||
var/mob/living/basic/mining_drone/minebot = target
|
||||
if(!istype(minebot))
|
||||
return ..()
|
||||
|
||||
upgrade_bot(minebot, user)
|
||||
return ITEM_INTERACT_COMPLETE
|
||||
|
||||
/obj/item/mine_bot_upgrade/proc/upgrade_bot(mob/living/basic/mining_drone/M, mob/user)
|
||||
if(M.melee_damage_upper != initial(M.melee_damage_upper))
|
||||
to_chat(user, "<span class='warning'>[M] already has a combat upgrade installed!</span>")
|
||||
return
|
||||
M.melee_damage_lower += 7
|
||||
M.melee_damage_upper += 7
|
||||
to_chat(user, "<span class='notice'>You upgrade [M]'s combat module.</span>")
|
||||
qdel(src)
|
||||
|
||||
/// Minebot Health Upgrade
|
||||
/obj/item/mine_bot_upgrade/health
|
||||
name = "minebot armor upgrade"
|
||||
|
||||
/obj/item/mine_bot_upgrade/health/upgrade_bot(mob/living/basic/mining_drone/M, mob/user)
|
||||
if(M.maxHealth != initial(M.maxHealth))
|
||||
to_chat(user, "<span class='warning'>[M] already has a reinforced chassis!</span>")
|
||||
return
|
||||
M.maxHealth += 45
|
||||
M.updatehealth()
|
||||
to_chat(user, "<span class='notice'>You upgrade [M]'s chassis.</span>")
|
||||
qdel(src)
|
||||
|
||||
/// Minebot AI upgrade/sentience potion
|
||||
/obj/item/slimepotion/sentience/mining
|
||||
name = "minebot AI upgrade"
|
||||
desc = "Can be used to grant sentience to minebots."
|
||||
icon_state = "door_electronics"
|
||||
icon = 'icons/obj/doors/door_assembly.dmi'
|
||||
sentience_type = SENTIENCE_MINEBOT
|
||||
origin_tech = "programming=6"
|
||||
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/sentience/mining/after_success(mob/living/user, mob/living/simple_animal/SM)
|
||||
if(istype(SM, /mob/living/basic/mining_drone))
|
||||
var/mob/living/basic/mining_drone/M = SM
|
||||
M.maxHealth = initial(M.maxHealth) + base_health_add
|
||||
M.melee_damage_lower = initial(M.melee_damage_lower) + base_damage_add
|
||||
M.melee_damage_upper = initial(M.melee_damage_upper) + base_damage_add
|
||||
if(M.stored_gun)
|
||||
M.stored_gun.overheat_time += base_cooldown_add
|
||||
if(M.mind)
|
||||
M.mind.offstation_role = TRUE
|
||||
Reference in New Issue
Block a user