From b2ecd81be495279e08f2057bb226bb9559f2a266 Mon Sep 17 00:00:00 2001 From: Jacquerel Date: Sat, 23 Sep 2023 02:12:43 +0100 Subject: [PATCH] Basic Mob Brimdemon (#78424) ## About The Pull Request Fixes #71330 The brimdemon was basically already perfect (well, it has a novel means of attacking) so I didn't get too fancy with this one, it's _largely_ just a straightforward conversion. Following this change it's a little slower to back off, but better at lining up with people in order to blast them. Additionally, its beam is now a mob ability so you can give it to other mobs if you so desire. Because I can't help doing a _little_ tinkering, Brimdemons now explode 2.5 seconds after they die, after a brief warning animation. ## Why It's Good For The Game Simple mobs must die ## Changelog :cl: add: Brimdemon corpses release an explosion shortly after death, just to keep you on your toes. refactor: Brimdemons now use the basic mob framework which (should) improve their pathfinding somewhat. Please bug report any unusual behaviour. admin: The brimdemon's beam ability can be given to any mob, for your Binding of Isaac event /:cl: --- code/__DEFINES/ai/ai.dm | 4 +- code/datums/ai/_ai_controller.dm | 5 + code/datums/ai/babies/babies_subtrees.dm | 13 +- .../ai/basic_mobs/base_basic_controller.dm | 11 +- .../basic_ai_behaviors/basic_attacking.dm | 3 +- .../targeted_mob_ability.dm | 6 - .../basic_subtrees/attack_adjacent_target.dm | 33 +++ .../basic_mobs/basic_subtrees/climb_tree.dm | 11 +- .../ai/basic_mobs/basic_subtrees/find_food.dm | 3 +- .../basic_subtrees/find_paper_and_write.dm | 3 +- .../basic_mobs/basic_subtrees/flee_target.dm | 5 +- .../basic_subtrees/maintain_distance.dm | 3 - .../basic_subtrees/move_to_cardinal.dm | 71 +++++ .../basic_subtrees/ranged_skirmish.dm | 3 +- .../basic_subtrees/simple_attack_target.dm | 12 +- .../basic_subtrees/sleep_with_no_target.dm | 2 +- .../basic_subtrees/targeted_mob_ability.dm | 6 +- .../basic_subtrees/use_mob_ability.dm | 2 +- code/datums/ai/generic/find_and_set.dm | 3 +- .../ai/idle_behaviors/idle_random_walk.dm | 10 + .../mapgen/Cavegens/LavalandGenerator.dm | 2 +- .../effects/spawners/random/lavaland_mobs.dm | 2 +- .../heretic/magic/furious_steel.dm | 4 +- .../basic/icemoon/ice_whelp/ice_whelp_ai.dm | 25 +- .../basic/lavaland/bileworm/bileworm_ai.dm | 4 +- .../basic/lavaland/brimdemon/brimbeam.dm | 126 +++++++++ .../basic/lavaland/brimdemon/brimdemon.dm | 87 +++++++ .../basic/lavaland/brimdemon/brimdemon_ai.dm | 50 ++++ .../lavaland/brimdemon/brimdemon_loot.dm | 56 ++++ .../basic/lavaland/goldgrub/goldgrub_ai.dm | 23 +- .../basic/lavaland/goliath/goliath_ai.dm | 3 +- .../basic/lavaland/watcher/watcher_ai.dm | 1 + .../mob/living/basic/minebots/minebot_ai.dm | 30 +-- .../basic/space_fauna/carp/carp_ai_actions.dm | 4 +- .../space_fauna/carp/carp_ai_rift_actions.dm | 5 +- .../space_fauna/eyeball/eyeball_ai_subtree.dm | 12 +- .../space_fauna/hivebot/hivebot_subtree.dm | 12 +- .../spider/giant_spider/giant_spider_ai.dm | 2 +- .../giant_spider/giant_spider_subtrees.dm | 9 +- .../hostile/mining_mobs/brimdemon.dm | 242 ------------------ .../spells/spell_types/pointed/_pointed.dm | 10 +- .../spells/spell_types/pointed/swap.dm | 14 +- .../unit_tests/simple_animal_freeze.dm | 1 - tgstation.dme | 7 +- .../78424_simple_to_basic_brimdemon.txt | 1 + 45 files changed, 548 insertions(+), 393 deletions(-) create mode 100644 code/datums/ai/basic_mobs/basic_subtrees/attack_adjacent_target.dm create mode 100644 code/datums/ai/basic_mobs/basic_subtrees/move_to_cardinal.dm create mode 100644 code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm create mode 100644 code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.dm create mode 100644 code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm create mode 100644 code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm delete mode 100644 code/modules/mob/living/simple_animal/hostile/mining_mobs/brimdemon.dm create mode 100644 tools/UpdatePaths/Scripts/78424_simple_to_basic_brimdemon.txt diff --git a/code/__DEFINES/ai/ai.dm b/code/__DEFINES/ai/ai.dm index 1a6829946a1..467fc9d7c0f 100644 --- a/code/__DEFINES/ai/ai.dm +++ b/code/__DEFINES/ai/ai.dm @@ -29,7 +29,9 @@ /// Don't move if being pulled #define STOP_MOVING_WHEN_PULLED (1<<0) /// Continue processing even if dead -#define CAN_ACT_WHILE_DEAD (1<<1) +#define CAN_ACT_WHILE_DEAD (1<<1) +/// Stop processing while in a progress bar +#define PAUSE_DURING_DO_AFTER (1<<2) //Base Subtree defines diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index c5c5ac6b126..7a306480302 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -349,6 +349,11 @@ multiple modular subtrees with behaviors minimum_distance = iter_behavior.required_distance return minimum_distance +/// Returns true if we have a blackboard key with the provided key and it is not qdeleting +/datum/ai_controller/proc/blackboard_key_exists(key) + var/datum/key_value = blackboard[key] + return !QDELETED(key_value) + /** * Used to manage references to datum by AI controllers * diff --git a/code/datums/ai/babies/babies_subtrees.dm b/code/datums/ai/babies/babies_subtrees.dm index 0c71b6d757b..aad92f8422e 100644 --- a/code/datums/ai/babies/babies_subtrees.dm +++ b/code/datums/ai/babies/babies_subtrees.dm @@ -20,13 +20,8 @@ if(is_type_in_list(controller.pawn, baby_types)) return - var/atom/target = controller.blackboard[BB_BABIES_TARGET] - // Find target - if(QDELETED(target)) - controller.queue_behavior(/datum/ai_behavior/find_partner, BB_BABIES_TARGET, BB_BABIES_PARTNER_TYPES, BB_BABIES_CHILD_TYPES) - return - - // Do target - controller.queue_behavior(/datum/ai_behavior/make_babies, BB_BABIES_TARGET, BB_BABIES_CHILD_TYPES) - return SUBTREE_RETURN_FINISH_PLANNING + if(controller.blackboard_key_exists(BB_BABIES_TARGET)) + controller.queue_behavior(/datum/ai_behavior/make_babies, BB_BABIES_TARGET, BB_BABIES_CHILD_TYPES) + return SUBTREE_RETURN_FINISH_PLANNING + controller.queue_behavior(/datum/ai_behavior/find_partner, BB_BABIES_TARGET, BB_BABIES_PARTNER_TYPES, BB_BABIES_CHILD_TYPES) diff --git a/code/datums/ai/basic_mobs/base_basic_controller.dm b/code/datums/ai/basic_mobs/base_basic_controller.dm index b4f7c27c2f9..3eb79a815ad 100644 --- a/code/datums/ai/basic_mobs/base_basic_controller.dm +++ b/code/datums/ai/basic_mobs/base_basic_controller.dm @@ -15,10 +15,13 @@ /datum/ai_controller/basic_controller/able_to_run() . = ..() - if(isliving(pawn)) - var/mob/living/living_pawn = pawn - if(IS_DEAD_OR_INCAP(living_pawn)) - return FALSE + if(!isliving(pawn)) + return + var/mob/living/living_pawn = pawn + if(!(ai_traits & CAN_ACT_WHILE_DEAD) && IS_DEAD_OR_INCAP(living_pawn)) + return FALSE + if(ai_traits & PAUSE_DURING_DO_AFTER && LAZYLEN(living_pawn.do_afters)) + return FALSE /datum/ai_controller/basic_controller/proc/update_speed(mob/living/basic/basic_mob) SIGNAL_HANDLER diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm index a27d881a437..6683fb02f93 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm @@ -4,8 +4,7 @@ /datum/ai_behavior/basic_melee_attack/setup(datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key) . = ..() - var/datum/targetting_datum/targetting_datum = controller.blackboard[targetting_datum_key] - if(isnull(targetting_datum)) + if(!controller.blackboard_key_exists(targetting_datum_key)) CRASH("No target datum was supplied in the blackboard for [controller.pawn]") //Hiding location is priority diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm index c0bd9d1323b..04cb9b171dd 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm @@ -19,12 +19,6 @@ var/atom/target = controller.blackboard[target_key] if (QDELETED(target)) controller.clear_blackboard_key(target_key) - return - if (!isliving(target)) - return - var/mob/living/living_target = target - if(living_target.stat >= UNCONSCIOUS) - controller.clear_blackboard_key(target_key) /datum/ai_behavior/targeted_mob_ability/proc/get_ability_to_use(datum/ai_controller/controller, ability_key) return controller.blackboard[ability_key] diff --git a/code/datums/ai/basic_mobs/basic_subtrees/attack_adjacent_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/attack_adjacent_target.dm new file mode 100644 index 00000000000..c3d334165d9 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/attack_adjacent_target.dm @@ -0,0 +1,33 @@ +/// Attack something which is already adjacent to us, without ending planning +/datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic + melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/opportunistic + end_planning = FALSE + +/datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + . = ..() + var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + if(QDELETED(target) || !controller.pawn.Adjacent(target)) + return + if (isliving(controller.pawn)) + var/mob/living/pawn = controller.pawn + if (LAZYLEN(pawn.do_afters)) + return + controller.queue_behavior(melee_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) + +/// Attack something which is already adjacent to us without moving +/datum/ai_behavior/basic_melee_attack/opportunistic + action_cooldown = 0.2 SECONDS // We gotta check unfortunately often because we're in a race condition with nextmove + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/basic_melee_attack/opportunistic/setup(datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key) + if (!controller.blackboard_key_exists(targetting_datum_key)) + CRASH("No target datum was supplied in the blackboard for [controller.pawn]") + return controller.blackboard_key_exists(target_key) + +/datum/ai_behavior/basic_melee_attack/opportunistic/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key) + var/atom/movable/atom_pawn = controller.pawn + if(!atom_pawn.CanReach(controller.blackboard[target_key])) + finish_action(controller, TRUE, target_key) // Don't clear target + return FALSE + . = ..() + finish_action(controller, TRUE, target_key) // Try doing something else diff --git a/code/datums/ai/basic_mobs/basic_subtrees/climb_tree.dm b/code/datums/ai/basic_mobs/basic_subtrees/climb_tree.dm index 6827f3eee99..1beab6ec907 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/climb_tree.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/climb_tree.dm @@ -7,11 +7,8 @@ if(!SPT_PROB(climb_chance, seconds_per_tick)) return - var/obj/structure/flora/tree/target = controller.blackboard[BB_CLIMBED_TREE] + if(controller.blackboard_key_exists(BB_CLIMBED_TREE)) + controller.queue_behavior(/datum/ai_behavior/climb_tree, BB_CLIMBED_TREE) + return SUBTREE_RETURN_FINISH_PLANNING - if(QDELETED(target)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_tree, BB_CLIMBED_TREE, /obj/structure/flora/tree) - return - - controller.queue_behavior(/datum/ai_behavior/climb_tree, BB_CLIMBED_TREE) - return SUBTREE_RETURN_FINISH_PLANNING + controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_tree, BB_CLIMBED_TREE, /obj/structure/flora/tree) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/find_food.dm b/code/datums/ai/basic_mobs/basic_subtrees/find_food.dm index 8d20df7b9c7..b02ec8eaa85 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/find_food.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/find_food.dm @@ -3,8 +3,7 @@ /datum/ai_planning_subtree/find_food/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(!QDELETED(target)) + if(controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) // Busy with something return diff --git a/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.dm b/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.dm index 900d13e0a04..0d2d0c3e3b4 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/find_paper_and_write.dm @@ -1,10 +1,9 @@ /datum/ai_planning_subtree/find_paper_and_write /datum/ai_planning_subtree/find_paper_and_write/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/obj/item/inhand_paper = controller.blackboard[BB_SIMPLE_CARRY_ITEM] var/mob/living/basic/wizard = controller.pawn - if(!QDELETED(inhand_paper)) + if(controller.blackboard_key_exists(BB_SIMPLE_CARRY_ITEM)) controller.queue_behavior(/datum/ai_behavior/write_on_paper, BB_SIMPLE_CARRY_ITEM, BB_WRITING_LIST) return SUBTREE_RETURN_FINISH_PLANNING diff --git a/code/datums/ai/basic_mobs/basic_subtrees/flee_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/flee_target.dm index 6ae7885618b..a4982fa660e 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/flee_target.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/flee_target.dm @@ -9,10 +9,7 @@ /datum/ai_planning_subtree/flee_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() - if (!controller.blackboard[BB_BASIC_MOB_FLEEING]) - return - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) + if (!controller.blackboard[BB_BASIC_MOB_FLEEING] || !controller.blackboard_key_exists(target_key)) return controller.queue_behavior(flee_behaviour, target_key, hiding_place_key) return SUBTREE_RETURN_FINISH_PLANNING //we gotta get out of here. diff --git a/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm b/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm index 61631178163..b80a28836a0 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm @@ -11,9 +11,6 @@ /datum/ai_planning_subtree/maintain_distance/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() - var/mob/living/living_pawn = controller.pawn - if(LAZYLEN(living_pawn.do_afters)) - return var/atom/target = controller.blackboard[target_key] if (!isliving(target) || !can_see(controller.pawn, target, view_distance)) return // Don't run away from cucumbers, they're not snakes diff --git a/code/datums/ai/basic_mobs/basic_subtrees/move_to_cardinal.dm b/code/datums/ai/basic_mobs/basic_subtrees/move_to_cardinal.dm new file mode 100644 index 00000000000..c98878e0fd7 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/move_to_cardinal.dm @@ -0,0 +1,71 @@ +/// Try to line up with a cardinal direction of your target +/datum/ai_planning_subtree/move_to_cardinal + /// Behaviour to execute to line ourselves up + var/move_behaviour = /datum/ai_behavior/move_to_cardinal + /// Blackboard key in which to store selected target + var/target_key = BB_BASIC_MOB_CURRENT_TARGET + +/datum/ai_planning_subtree/move_to_cardinal/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + . = ..() + if(!controller.blackboard_key_exists(target_key)) + return + controller.queue_behavior(move_behaviour, target_key) + +/// Try to line up with a cardinal direction of your target +/datum/ai_behavior/move_to_cardinal + required_distance = 0 + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + /// How close to our target is too close? + var/minimum_distance = 1 + /// How far away is too far? + var/maximum_distance = 9 + +/datum/ai_behavior/move_to_cardinal/setup(datum/ai_controller/controller, target_key) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + target_nearest_cardinal(controller, target) + return TRUE + +/// Set our movement target to the closest cardinal space to our target +/datum/ai_behavior/move_to_cardinal/proc/target_nearest_cardinal(datum/ai_controller/controller, atom/target) + var/atom/move_target + var/closest = INFINITY + + for (var/dir in GLOB.cardinals) + var/turf/cardinal_turf = get_ranged_target_turf(target, dir, minimum_distance) + if (cardinal_turf.is_blocked_turf()) + continue + var/distance_to = get_dist(controller.pawn, cardinal_turf) + if (distance_to >= closest) + continue + closest = distance_to + move_target = cardinal_turf + + if (isnull(move_target)) + move_target = target + if (controller.current_movement_target == move_target) + return + set_movement_target(controller, move_target) + +/datum/ai_behavior/move_to_cardinal/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + var/atom/target = controller.blackboard[target_key] + if (QDELETED(target)) + finish_action(controller = controller, succeeded = FALSE, target_key = target_key) + return + if (!(get_dir(controller.pawn, target) in GLOB.cardinals)) + target_nearest_cardinal(controller, target) + return + var/distance_to_target = get_dist(controller.pawn, target) + if (distance_to_target < minimum_distance) + target_nearest_cardinal(controller, target) + return + if (distance_to_target > maximum_distance) + return + finish_action(controller = controller, succeeded = TRUE, target_key = target_key) + return + +/datum/ai_behavior/move_to_cardinal/finish_action(datum/ai_controller/controller, succeeded, target_key) + if (!succeeded) + controller.clear_blackboard_key(target_key) + return ..() diff --git a/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm b/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm index 07fb6f8b0dc..1ff752d925f 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm @@ -8,8 +8,7 @@ /datum/ai_planning_subtree/ranged_skirmish/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() - var/atom/target = controller.blackboard[target_key] - if(QDELETED(target)) + if(!controller.blackboard_key_exists(target_key)) return controller.queue_behavior(attack_behavior, target_key, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm index ed45de512ac..210aaf0aa8b 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm @@ -1,13 +1,16 @@ /datum/ai_planning_subtree/basic_melee_attack_subtree + /// What do we do in order to attack someone? var/datum/ai_behavior/basic_melee_attack/melee_attack_behavior = /datum/ai_behavior/basic_melee_attack + /// Is this the last thing we do? (if we set a movement target, this will usually be yes) + var/end_planning = TRUE /datum/ai_planning_subtree/basic_melee_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(target)) + if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) return controller.queue_behavior(melee_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions. + if (end_planning) + return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions. /datum/ai_planning_subtree/basic_ranged_attack_subtree operational_datums = list(/datum/component/ranged_attacks) @@ -15,8 +18,7 @@ /datum/ai_planning_subtree/basic_ranged_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() - var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(target)) + if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) return controller.queue_behavior(ranged_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions. diff --git a/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm index ccd67d6d1e7..93499cf673c 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/sleep_with_no_target.dm @@ -16,7 +16,7 @@ /datum/ai_behavior/sleep_after_targetless_time/perform(seconds_per_tick, datum/ai_controller/controller, target_key) var/atom/target = controller.blackboard[target_key] - finish_action(controller, succeeded = !target, seconds_per_tick = seconds_per_tick) + finish_action(controller, succeeded = QDELETED(target), seconds_per_tick = seconds_per_tick) /datum/ai_behavior/sleep_after_targetless_time/finish_action(datum/ai_controller/controller, succeeded, seconds_per_tick) . = ..() diff --git a/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm b/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm index b6b4d286df1..8d75f797163 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm @@ -13,9 +13,11 @@ if (!ability_key) CRASH("You forgot to tell this mob where to find its ability") - var/mob/living/target = controller.blackboard[target_key] + if (!controller.blackboard_key_exists(target_key)) + return + var/datum/action/cooldown/using_action = controller.blackboard[ability_key] - if (QDELETED(target) || QDELETED(using_action) || !using_action.IsAvailable()) + if (!using_action?.IsAvailable()) return controller.queue_behavior(use_ability_behaviour, ability_key, target_key) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm b/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm index 1d7bfd62ad7..fb83b8f8684 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm @@ -15,7 +15,7 @@ CRASH("You forgot to tell this mob where to find its ability") var/datum/action/cooldown/using_action = controller.blackboard[ability_key] - if (QDELETED(using_action) || !using_action.IsAvailable()) + if (!using_action?.IsAvailable()) return controller.queue_behavior(use_ability_behaviour, ability_key) diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm index 443febae2a0..eb09506a282 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -8,8 +8,7 @@ /datum/ai_behavior/find_and_set/perform(seconds_per_tick, datum/ai_controller/controller, set_key, locate_path, search_range) . = ..() - var/atom/current_target = controller.blackboard[set_key] - if (!QDELETED(current_target)) + if (controller.blackboard_key_exists(set_key)) return var/find_this_thing = search_tactic(controller, locate_path, search_range) if(find_this_thing) diff --git a/code/datums/ai/idle_behaviors/idle_random_walk.dm b/code/datums/ai/idle_behaviors/idle_random_walk.dm index b25f983f313..d5a3972a3d0 100644 --- a/code/datums/ai/idle_behaviors/idle_random_walk.dm +++ b/code/datums/ai/idle_behaviors/idle_random_walk.dm @@ -14,3 +14,13 @@ /datum/idle_behavior/idle_random_walk/less_walking walk_chance = 10 + +/// Only walk if we don't have a target +/datum/idle_behavior/idle_random_walk/no_target + /// Where do we look for a target? + var/target_key = BB_BASIC_MOB_CURRENT_TARGET + +/datum/idle_behavior/idle_random_walk/no_target/perform_idle_behavior(seconds_per_tick, datum/ai_controller/controller) + if (!controller.blackboard_key_exists(target_key)) + return + return ..() diff --git a/code/datums/mapgen/Cavegens/LavalandGenerator.dm b/code/datums/mapgen/Cavegens/LavalandGenerator.dm index be8294fada6..6f8aa7ed1bb 100644 --- a/code/datums/mapgen/Cavegens/LavalandGenerator.dm +++ b/code/datums/mapgen/Cavegens/LavalandGenerator.dm @@ -8,8 +8,8 @@ /obj/effect/spawner/random/lavaland_mob/legion = 30, /obj/effect/spawner/random/lavaland_mob/watcher = 40, /mob/living/basic/mining/bileworm = 20, + /mob/living/basic/mining/brimdemon = 20, /mob/living/basic/mining/lobstrosity/lava = 20, - /mob/living/simple_animal/hostile/asteroid/brimdemon = 20, /mob/living/basic/mining/goldgrub = 10, /obj/structure/spawner/lavaland = 2, /obj/structure/spawner/lavaland/goliath = 3, diff --git a/code/game/objects/effects/spawners/random/lavaland_mobs.dm b/code/game/objects/effects/spawners/random/lavaland_mobs.dm index 7ad2b3e6e30..8aa20cc90e7 100644 --- a/code/game/objects/effects/spawners/random/lavaland_mobs.dm +++ b/code/game/objects/effects/spawners/random/lavaland_mobs.dm @@ -7,11 +7,11 @@ icon_state = "large_egg" loot = list( /mob/living/basic/mining/bileworm = 1, + /mob/living/basic/mining/brimdemon = 1, /mob/living/basic/mining/goldgrub = 1, /mob/living/basic/mining/goliath = 1, /mob/living/basic/mining/lobstrosity/lava = 1, /mob/living/basic/mining/watcher = 1, - /mob/living/simple_animal/hostile/asteroid/brimdemon = 1, /mob/living/simple_animal/hostile/asteroid/hivelord/legion = 1, ) diff --git a/code/modules/antagonists/heretic/magic/furious_steel.dm b/code/modules/antagonists/heretic/magic/furious_steel.dm index f9b12612306..1c82f36e024 100644 --- a/code/modules/antagonists/heretic/magic/furious_steel.dm +++ b/code/modules/antagonists/heretic/magic/furious_steel.dm @@ -43,12 +43,12 @@ unset_click_ability(source, refund_cooldown = TRUE) -/datum/action/cooldown/spell/pointed/projectile/furious_steel/InterceptClickOn(mob/living/caller, params, atom/click_target) +/datum/action/cooldown/spell/pointed/projectile/furious_steel/InterceptClickOn(mob/living/caller, params, atom/target) // Let the caster prioritize using items like guns over blade casts if(caller.get_active_held_item()) return FALSE // Let the caster prioritize melee attacks like punches and shoves over blade casts - if(get_dist(caller, click_target) <= 1) + if(get_dist(caller, target) <= 1) return FALSE return ..() diff --git a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm index 9885ba3da59..5951bd6b7fe 100644 --- a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm +++ b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm @@ -69,14 +69,10 @@ /datum/ai_planning_subtree/sculpt_statues /datum/ai_planning_subtree/sculpt_statues/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/obj/target = controller.blackboard[BB_TARGET_ROCK] - - if(QDELETED(target)) - controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_TARGET_ROCK, /obj/structure/flora/rock/icy) - return - - controller.queue_behavior(/datum/ai_behavior/sculpt_statue, BB_TARGET_ROCK) - return SUBTREE_RETURN_FINISH_PLANNING + if(controller.blackboard_key_exists(BB_TARGET_ROCK)) + controller.queue_behavior(/datum/ai_behavior/sculpt_statue, BB_TARGET_ROCK) + return SUBTREE_RETURN_FINISH_PLANNING + controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_TARGET_ROCK, /obj/structure/flora/rock/icy) /datum/ai_behavior/sculpt_statue behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION @@ -135,16 +131,13 @@ /datum/ai_planning_subtree/burn_trees/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/datum/action/cooldown/using_action = controller.blackboard[BB_WHELP_STRAIGHTLINE_FIRE] - if (!using_action.IsAvailable()) + if (!using_action?.IsAvailable()) return - var/obj/structure/target = controller.blackboard[BB_TARGET_TREE] - if(QDELETED(target)) - controller.queue_behavior(/datum/ai_behavior/set_target_tree, BB_TARGET_TREE) - return - - controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees, BB_WHELP_STRAIGHTLINE_FIRE, BB_TARGET_TREE) - return SUBTREE_RETURN_FINISH_PLANNING + if(controller.blackboard_key_exists(BB_TARGET_TREE)) + controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees, BB_WHELP_STRAIGHTLINE_FIRE, BB_TARGET_TREE) + return SUBTREE_RETURN_FINISH_PLANNING + controller.queue_behavior(/datum/ai_behavior/set_target_tree, BB_TARGET_TREE) /datum/ai_behavior/set_target_tree diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm index 11d7168776b..0093e69220c 100644 --- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm +++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm @@ -15,9 +15,7 @@ /datum/ai_planning_subtree/bileworm_attack /datum/ai_planning_subtree/bileworm_attack/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - - var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] - if(QDELETED(target)) + if (!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) return var/datum/action/cooldown/mob_cooldown/resurface = controller.blackboard[BB_BILEWORM_RESURFACE] diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm new file mode 100644 index 00000000000..29a3de60029 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm @@ -0,0 +1,126 @@ +/// Fires a bloody beam. Brimdemon Blast! +/datum/action/cooldown/mob_cooldown/brimbeam + name = "brimstone blast" + desc = "Unleash a barrage of infernal energies in the targeted direction." + button_icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi' + button_icon_state = "brimdemon_firing" + background_icon_state = "bg_demon" + overlay_icon_state = "bg_demon_border" + click_to_activate = TRUE + cooldown_time = 5 SECONDS + melee_cooldown_time = 0 + check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED + /// How far does our beam go? + var/beam_range = 10 + /// How long does our beam last? + var/beam_duration = 2 SECONDS + /// How long do we wind up before firing? + var/charge_duration = 1 SECONDS + /// Overlay we show when we're about to fire + var/static/image/direction_overlay = image('icons/mob/simple/lavaland/lavaland_monsters.dmi', "brimdemon_telegraph_dir") + /// A list of all the beam parts. + var/list/beam_parts = list() + +/datum/action/cooldown/mob_cooldown/brimbeam/Destroy() + extinguish_laser() + return ..() + +/datum/action/cooldown/mob_cooldown/brimbeam/Activate(atom/target) + StartCooldown(360 SECONDS) + + owner.face_atom(target) + owner.move_resist = MOVE_FORCE_VERY_STRONG + owner.add_overlay(direction_overlay) + owner.balloon_alert_to_viewers("charging...") + + var/fully_charged = do_after(owner, delay = charge_duration, target = owner) + owner.cut_overlay(direction_overlay) + if (!fully_charged) + StartCooldown() + return TRUE + + if (!fire_laser()) + var/static/list/fail_emotes = list("coughs.", "wheezes.", "belches out a puff of black smoke.") + owner.manual_emote(pick(fail_emotes)) + StartCooldown() + return TRUE + + do_after(owner, delay = beam_duration, target = owner) + extinguish_laser() + StartCooldown() + return TRUE + +/// Create a laser in the direction we are facing +/datum/action/cooldown/mob_cooldown/brimbeam/proc/fire_laser() + owner.visible_message(span_danger("[owner] fires a brimbeam!")) + playsound(owner, 'sound/creatures/brimdemon.ogg', 150, FALSE, 0, 3) + var/turf/target_turf = get_ranged_target_turf(owner, owner.dir, beam_range) + var/turf/origin_turf = get_turf(owner) + var/list/affected_turfs = get_line(origin_turf, target_turf) - origin_turf + for(var/turf/affected_turf in affected_turfs) + if(affected_turf.opacity) + break + var/blocked = FALSE + for(var/obj/potential_block in affected_turf.contents) + if(potential_block.opacity) + blocked = TRUE + break + if(blocked) + break + var/atom/new_brimbeam = new /obj/effect/brimbeam(affected_turf) + new_brimbeam.dir = owner.dir + beam_parts += new_brimbeam + for(var/mob/living/hit_mob in affected_turf.contents) + hit_mob.apply_damage(damage = 25, damagetype = BURN) + to_chat(hit_mob, span_userdanger("You're blasted by [owner]'s brimbeam!")) + RegisterSignal(new_brimbeam, COMSIG_QDELETING, PROC_REF(extinguish_laser)) // In case idk a singularity eats it or something + if(!length(beam_parts)) + return FALSE + var/atom/last_brimbeam = beam_parts[length(beam_parts)] + last_brimbeam.icon_state = "brimbeam_end" + var/atom/first_brimbeam = beam_parts[1] + first_brimbeam.icon_state = "brimbeam_start" + return TRUE + +/// Get rid of our laser when we are done with it +/datum/action/cooldown/mob_cooldown/brimbeam/proc/extinguish_laser() + if(!length(beam_parts)) + return FALSE + owner.move_resist = initial(owner.move_resist) + for(var/obj/effect/brimbeam/beam in beam_parts) + beam.disperse() + beam_parts = list() + +/// Segments of the actual beam, these hurt if you stand in them +/obj/effect/brimbeam + name = "brimbeam" + icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi' + icon_state = "brimbeam_mid" + layer = ABOVE_MOB_LAYER + plane = ABOVE_GAME_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + light_color = LIGHT_COLOR_BLOOD_MAGIC + light_power = 3 + light_range = 2 + +/obj/effect/brimbeam/Initialize(mapload) + . = ..() + START_PROCESSING(SSfastprocess, src) + +/obj/effect/brimbeam/Destroy() + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/effect/brimbeam/process() + for(var/mob/living/hit_mob in get_turf(src)) + damage(hit_mob) + +/// Hurt the passed mob +/obj/effect/brimbeam/proc/damage(mob/living/hit_mob) + hit_mob.apply_damage(damage = 5, damagetype = BURN) + to_chat(hit_mob, span_danger("You're damaged by [src]!")) + +/// Disappear +/obj/effect/brimbeam/proc/disperse() + animate(src, time = 0.5 SECONDS, alpha = 0) + QDEL_IN(src, 0.5 SECONDS) diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.dm new file mode 100644 index 00000000000..2c22977d9c6 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.dm @@ -0,0 +1,87 @@ +/// Lavaland mob which tries to line up with its target and fire a laser +/mob/living/basic/mining/brimdemon + name = "brimdemon" + desc = "A volatile creature resembling an enormous horned skull. Its response to almost any stimulus is to unleash a beam of infernal energy." + icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi' + icon_state = "brimdemon" + icon_living = "brimdemon" + icon_dead = "brimdemon_dead" + speed = 3 + maxHealth = 250 + health = 250 + friendly_verb_continuous = "scratches at" + friendly_verb_simple = "scratch at" + speak_emote = list("cackles") + melee_damage_lower = 7.5 + melee_damage_upper = 7.5 + attack_sound = 'sound/weapons/bite.ogg' + melee_attack_cooldown = 0.6 SECONDS + attack_vis_effect = ATTACK_EFFECT_BITE + attack_verb_continuous = "bites" + attack_verb_simple = "bite" + death_message = "wails as infernal energy escapes from its wounds, leaving it an empty husk." + death_sound = 'sound/magic/demon_dies.ogg' + light_color = LIGHT_COLOR_BLOOD_MAGIC + light_power = 5 + light_range = 1.4 + + ai_controller = /datum/ai_controller/basic_controller/brimdemon + + crusher_loot = /obj/item/crusher_trophy/brimdemon_fang + butcher_results = list( + /obj/item/food/meat/slab = 2, + /obj/effect/decal/cleanable/brimdust = 1, + /obj/item/organ/internal/monster_core/brimdust_sac = 1, + ) + /// How we get blasting + var/datum/action/cooldown/mob_cooldown/brimbeam/beam + +/mob/living/basic/mining/brimdemon/Initialize(mapload) + . = ..() + AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW) + beam = new(src) + beam.Grant(src) + ai_controller.set_blackboard_key(BB_TARGETTED_ACTION, beam) + +/mob/living/basic/mining/brimdemon/Destroy() + QDEL_NULL(beam) + return ..() + +/mob/living/basic/mining/brimdemon/RangedAttack(atom/target, modifiers) + beam.Trigger(target = target) + +/mob/living/basic/mining/brimdemon/death(gibbed) + . = ..() + if (gibbed) + return + var/obj/effect/temp_visual/brim_burst/bang = new(loc) + forceMove(bang) + +/// Show a funny animation before doing an explosion +/obj/effect/temp_visual/brim_burst + name = "bursting brimdemon" + icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi' + icon_state = "brimdemon_dead" + duration = 1.9 SECONDS + +/obj/effect/temp_visual/brim_burst/Initialize(mapload) + . = ..() + addtimer(CALLBACK(src, PROC_REF(bang)), duration - (1 DECISECONDS), TIMER_DELETE_ME) + animate(src, color = "#ff8888", transform = matrix().Scale(1.1), time = 0.7 SECONDS) + animate(color = "#ffffff", transform = matrix(), time = 0.2 SECONDS) + animate(color = "#ff4444", transform = matrix().Scale(1.3), time = 0.5 SECONDS) + animate(color = "#ffffff", transform = matrix(), time = 0.2 SECONDS) + animate(color = "#ff0000", transform = matrix().Scale(1.5), time = 0.3 SECONDS) + +/// Make an explosion +/obj/effect/temp_visual/brim_burst/proc/bang() + var/turf/origin_turf = get_turf(src) + playsound(origin_turf, 'sound/effects/pop_expl.ogg', 50) + new /obj/effect/temp_visual/explosion/fast(origin_turf) + var/list/possible_targets = range(1, origin_turf) + for(var/mob/living/target in possible_targets) + var/armor = target.run_armor_check(attack_flag = BOMB) + target.apply_damage(20, damagetype = BURN, blocked = armor, spread_damage = TRUE) + + for (var/atom/movable/thing as anything in contents) + thing.forceMove(loc) diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm new file mode 100644 index 00000000000..e5d793fa150 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm @@ -0,0 +1,50 @@ +/** + * Slap someone who is nearby, line up with target, blast with a beam + */ +/datum/ai_controller/basic_controller/brimdemon + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/brimdemon, + ) + + ai_traits = PAUSE_DURING_DO_AFTER + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk/no_target + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic, + /datum/ai_planning_subtree/move_to_cardinal/brimdemon, + /datum/ai_planning_subtree/targeted_mob_ability/brimbeam, + ) + +/datum/targetting_datum/basic/brimdemon + stat_attack = HARD_CRIT + +/datum/ai_planning_subtree/move_to_cardinal/brimdemon + move_behaviour = /datum/ai_behavior/move_to_cardinal/brimdemon + +/datum/ai_behavior/move_to_cardinal/brimdemon + minimum_distance = 2 + +/datum/ai_behavior/move_to_cardinal/brimdemon/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + if (!succeeded) + return + var/mob/living/target = controller.blackboard[target_key] + var/datum/action/cooldown/ability = controller.blackboard[BB_TARGETTED_ACTION] + if(!ability?.IsAvailable()) + return + ability.InterceptClickOn(caller = controller.pawn, target = target) + +/datum/ai_planning_subtree/targeted_mob_ability/brimbeam + use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/brimbeam + +/datum/ai_behavior/targeted_mob_ability/brimbeam + /// Don't shoot if too far away + var/max_target_distance = 9 + +/datum/ai_behavior/targeted_mob_ability/brimbeam/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key) + var/mob/living/target = controller.blackboard[target_key] + if (QDELETED(target) || !(get_dir(controller.pawn, target) in GLOB.cardinals) || get_dist(controller.pawn, target) > max_target_distance) + finish_action(controller, succeeded = FALSE, ability_key = ability_key, target_key = target_key) + return + return ..() diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm new file mode 100644 index 00000000000..9a45ed99e1c --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm @@ -0,0 +1,56 @@ +/// Brimdemon crusher trophy, it... makes a funny sound? +/obj/item/crusher_trophy/brimdemon_fang + name = "brimdemon's fang" + icon_state = "brimdemon_fang" + desc = "A fang from a brimdemon's corpse." + denied_type = /obj/item/crusher_trophy/brimdemon_fang + /// Cartoon punching vfx + var/static/list/comic_phrases = list("BOOM", "BANG", "KABLOW", "KAPOW", "OUCH", "BAM", "KAPOW", "WHAM", "POW", "KABOOM") + +/obj/item/crusher_trophy/brimdemon_fang/effect_desc() + return "mark detonation creates visual and audiosensory effects on the target" + +/obj/item/crusher_trophy/brimdemon_fang/on_mark_detonation(mob/living/target, mob/living/user) + target.balloon_alert_to_viewers("[pick(comic_phrases)]!") + playsound(target, 'sound/lavaland/brimdemon_crush.ogg', 100) + +/// Reagent pool left by dying brimdemon +/obj/effect/decal/cleanable/brimdust + name = "brimdust" + desc = "Dust from a brimdemon. It is considered valuable for its' botanical abilities." + icon_state = "brimdust" + icon = 'icons/obj/mining.dmi' + layer = FLOOR_CLEAN_LAYER + mergeable_decal = FALSE + +/obj/effect/decal/cleanable/brimdust/Initialize(mapload) + . = ..() + reagents.add_reagent(/datum/reagent/brimdust, 15) + +/// Ashwalker ore sensor crafted from brimdemon ash +/obj/item/ore_sensor + name = "ore sensor" + desc = "Using demonic frequencies, this ear-mounted tool detects ores in the nearby terrain." + icon_state = "oresensor" + icon = 'icons/obj/mining.dmi' + slot_flags = ITEM_SLOT_EARS + var/range = 5 + var/cooldown = 4 SECONDS //between the standard and the advanced ore scanner in strength + COOLDOWN_DECLARE(ore_sensing_cooldown) + +/obj/item/ore_sensor/equipped(mob/user, slot, initial) + . = ..() + if(slot & ITEM_SLOT_EARS) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + +/obj/item/ore_sensor/dropped(mob/user, silent) + . = ..() + STOP_PROCESSING(SSobj, src) + +/obj/item/ore_sensor/process(seconds_per_tick) + if(!COOLDOWN_FINISHED(src, ore_sensing_cooldown)) + return + COOLDOWN_START(src, ore_sensing_cooldown, cooldown) + mineral_scan_pulse(get_turf(src), range) 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 96c9b3d556e..58efaf1f81b 100644 --- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm @@ -108,10 +108,15 @@ if(!dig_ability.IsAvailable()) return - var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + var/has_target = controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET) //a storm is coming or someone is nearby, its time to escape - if(currently_underground || !currently_underground && storm_approaching || !QDELETED(target)) + if(currently_underground) + if(has_target) + return + controller.queue_behavior(/datum/ai_behavior/use_mob_ability/burrow, BB_BURROW_ABILITY) + return SUBTREE_RETURN_FINISH_PLANNING + if(storm_approaching || has_target) controller.queue_behavior(/datum/ai_behavior/use_mob_ability/burrow, BB_BURROW_ABILITY) return SUBTREE_RETURN_FINISH_PLANNING @@ -122,14 +127,10 @@ /datum/ai_planning_subtree/grub_mine /datum/ai_planning_subtree/grub_mine/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/turf/target_wall = controller.blackboard[BB_TARGET_MINERAL_WALL] - - if(QDELETED(target_wall)) - controller.queue_behavior(/datum/ai_behavior/find_mineral_wall, BB_TARGET_MINERAL_WALL) - return - - controller.queue_behavior(/datum/ai_behavior/mine_wall, BB_TARGET_MINERAL_WALL) - return SUBTREE_RETURN_FINISH_PLANNING + if(controller.blackboard_key_exists(BB_TARGET_MINERAL_WALL)) + controller.queue_behavior(/datum/ai_behavior/mine_wall, BB_TARGET_MINERAL_WALL) + return SUBTREE_RETURN_FINISH_PLANNING + controller.queue_behavior(/datum/ai_behavior/find_mineral_wall, BB_TARGET_MINERAL_WALL) /datum/ai_behavior/mine_wall behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION @@ -168,7 +169,7 @@ /datum/pet_command/grub_spit/execute_action(datum/ai_controller/controller) var/datum/action/cooldown/spit_ability = controller.blackboard[BB_SPIT_ABILITY] - if(QDELETED(spit_ability) || !spit_ability.IsAvailable()) + if(!spit_ability?.IsAvailable()) return controller.queue_behavior(/datum/ai_behavior/use_mob_ability, BB_SPIT_ABILITY) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm index c433ec936ad..7e723f5d93a 100644 --- a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm +++ b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm @@ -96,8 +96,7 @@ var/target_key = BB_GOLIATH_HOLE_TARGET /datum/ai_planning_subtree/goliath_dig/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/turf/target_turf = controller.blackboard[target_key] - if (QDELETED(target_turf)) + if (!controller.blackboard_key_exists(target_key)) return controller.queue_behavior(/datum/ai_behavior/goliath_dig, target_key) return SUBTREE_RETURN_FINISH_PLANNING diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm index 9b2972a398f..1f310ac229f 100644 --- a/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm @@ -3,6 +3,7 @@ BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, ) + ai_traits = PAUSE_DURING_DO_AFTER ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( diff --git a/code/modules/mob/living/basic/minebots/minebot_ai.dm b/code/modules/mob/living/basic/minebots/minebot_ai.dm index 9ee1d93c1e0..897cddb1401 100644 --- a/code/modules/mob/living/basic/minebots/minebot_ai.dm +++ b/code/modules/mob/living/basic/minebots/minebot_ai.dm @@ -19,14 +19,10 @@ ///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 + 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(var/mob/living/carbon/human/target in oview(search_range, controller.pawn)) @@ -73,18 +69,12 @@ ///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) + if(!controller.blackboard[BB_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 + 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 @@ -190,7 +180,7 @@ /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()) + if(!ability?.IsAvailable()) return controller.queue_behavior(/datum/ai_behavior/use_mob_ability, ability_key) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm index b2dc866ce7c..b1343a7aeed 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm @@ -34,9 +34,7 @@ /datum/ai_planning_subtree/find_nearest_magicarp_spell_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/datum/action/cooldown/using_action = controller.blackboard[BB_MAGICARP_SPELL] - if (QDELETED(using_action)) - return - if (!using_action.IsAvailable()) + if (!using_action?.IsAvailable()) return var/spell_targetting = controller.blackboard[BB_MAGICARP_SPELL_SPECIAL_TARGETTING] diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm index 88a9f6706f2..d220325ca15 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm @@ -12,9 +12,10 @@ if (!rift_behaviour) CRASH("Forgot to specify rift behaviour for [src]") - var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + if (!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + return var/datum/action/cooldown/using_action = controller.blackboard[BB_CARP_RIFT] - if (QDELETED(target) || QDELETED(using_action) || !using_action.IsAvailable()) + if (!using_action?.IsAvailable()) return controller.queue_behavior(rift_behaviour, BB_CARP_RIFT, BB_BASIC_MOB_CURRENT_TARGET) diff --git a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm index d20873f0ce9..a3c8e22071d 100644 --- a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm +++ b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm @@ -17,14 +17,10 @@ /datum/ai_planning_subtree/heal_the_blind /datum/ai_planning_subtree/heal_the_blind/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/mob/living/carbon/target = controller.blackboard[BB_BLIND_TARGET] - - if(QDELETED(target)) - controller.queue_behavior(/datum/ai_behavior/find_the_blind, BB_BLIND_TARGET, BB_EYE_DAMAGE_THRESHOLD) - return - - controller.queue_behavior(/datum/ai_behavior/heal_eye_damage, BB_BLIND_TARGET) - return SUBTREE_RETURN_FINISH_PLANNING + if(controller.blackboard_key_exists(BB_BLIND_TARGET)) + controller.queue_behavior(/datum/ai_behavior/heal_eye_damage, BB_BLIND_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + controller.queue_behavior(/datum/ai_behavior/find_the_blind, BB_BLIND_TARGET, BB_EYE_DAMAGE_THRESHOLD) /datum/targetting_datum/basic/eyeball/can_attack(mob/living/owner, atom/target) . = ..() diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_subtree.dm b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_subtree.dm index c5173e3d7f9..347219d0ef0 100644 --- a/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_subtree.dm +++ b/code/modules/mob/living/basic/space_fauna/hivebot/hivebot_subtree.dm @@ -54,14 +54,10 @@ if(!SPT_PROB(relay_chance, seconds_per_tick)) return - var/mob/hive_target = controller.blackboard[BB_HIVE_PARTNER] - - if(QDELETED(hive_target)) - controller.queue_behavior(/datum/ai_behavior/find_and_set/hive_partner, BB_HIVE_PARTNER, /mob/living/basic/hivebot) - return - - controller.queue_behavior(/datum/ai_behavior/relay_message, BB_HIVE_PARTNER) - return SUBTREE_RETURN_FINISH_PLANNING + if (controller.blackboard_key_exists(BB_HIVE_PARTNER)) + controller.queue_behavior(/datum/ai_behavior/relay_message, BB_HIVE_PARTNER) + return SUBTREE_RETURN_FINISH_PLANNING + controller.queue_behavior(/datum/ai_behavior/find_and_set/hive_partner, BB_HIVE_PARTNER, /mob/living/basic/hivebot) /datum/ai_planning_subtree/find_and_hunt_target/repair_machines target_key = BB_MACHINE_TARGET diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm index 6838f3ba1ab..f998b7b53cd 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_ai.dm @@ -4,7 +4,7 @@ BB_TARGETTING_DATUM = new /datum/targetting_datum/basic(), ) - ai_traits = STOP_MOVING_WHEN_PULLED + ai_traits = STOP_MOVING_WHEN_PULLED | PAUSE_DURING_DO_AFTER ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_subtrees.dm b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_subtrees.dm index 4b65138b041..56aacb11a96 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_subtrees.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spider_subtrees.dm @@ -62,12 +62,9 @@ var/target_key = BB_SPIDER_WEB_TARGET /datum/ai_planning_subtree/spin_web/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - var/datum/action/cooldown/using_action = controller.blackboard[action_key] - var/turf/target_turf = controller.blackboard[target_key] - if (QDELETED(using_action) || QDELETED(target_turf)) - return - controller.queue_behavior(/datum/ai_behavior/spin_web, action_key, target_key) - return SUBTREE_RETURN_FINISH_PLANNING + if (controller.blackboard_key_exists(action_key) && controller.blackboard_key_exists(target_key)) + controller.queue_behavior(/datum/ai_behavior/spin_web, action_key, target_key) + return SUBTREE_RETURN_FINISH_PLANNING /// Move to an unwebbed nearby turf and web it up /datum/ai_behavior/spin_web diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/brimdemon.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/brimdemon.dm deleted file mode 100644 index d9bd88cb889..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/brimdemon.dm +++ /dev/null @@ -1,242 +0,0 @@ -#define BRIMBEAM_RANGE 10 - -/mob/living/simple_animal/hostile/asteroid/brimdemon - name = "brimdemon" - desc = "A misshapen demon with big, red eyes and a hinged mouth. Not much is known about the creatures \ - due to their response to any unexpected stimulus being \"brimbeam\", a deadly blood-laser barrage." - icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi' - icon_state = "brimdemon" - icon_living = "brimdemon" - icon_dead = "brimdemon_dead" - mob_biotypes = MOB_ORGANIC|MOB_BEAST - speak_emote = list("cackles") - emote_taunt = list("screeches") - emote_hear = list("cackles","screeches") - combat_mode = TRUE - stat_attack = HARD_CRIT - ranged_cooldown_time = 5 SECONDS - vision_range = 9 - retreat_distance = 2 - speed = 3 - move_to_delay = 5 - maxHealth = 250 - health = 250 - obj_damage = 15 - melee_damage_lower = 7.5 - melee_damage_upper = 7.5 - rapid_melee = 2 // every second attack - attack_verb_continuous = "bites" - attack_verb_simple = "bite" - attack_sound = 'sound/weapons/bite.ogg' - attack_vis_effect = ATTACK_EFFECT_BITE - butcher_results = list( - /obj/item/food/meat/slab = 2, - /obj/effect/decal/cleanable/brimdust = 1, - /obj/item/organ/internal/monster_core/brimdust_sac = 1, - ) - loot = list() - robust_searching = TRUE - footstep_type = FOOTSTEP_MOB_CLAW - death_message = "wails as infernal energy escapes from its wounds, leaving it an empty husk." - death_sound = 'sound/magic/demon_dies.ogg' - light_color = LIGHT_COLOR_BLOOD_MAGIC - light_power = 5 - light_range = 1.4 - crusher_loot = /obj/item/crusher_trophy/brimdemon_fang - /// Are we charging/firing? If yes stops our movement. - var/firing = FALSE - /// A list of all the beam parts. - var/list/beamparts = list() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/Destroy() - QDEL_LIST(beamparts) - return ..() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/Login() - ranged = TRUE - return ..() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/Logout() - ranged = FALSE - return ..() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/death() - firing = FALSE - cut_overlay("brimdemon_telegraph_dir") - move_resist = initial(move_resist) - return ..() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/Goto(target, delay, minimum_distance) - if(firing) - return FALSE - return ..() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/MoveToTarget(list/possible_targets) - if(firing) - return FALSE - return ..() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/AttackingTarget(atom/attacked_target) - if(firing) - return FALSE - return ..() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/Move(atom/newloc, dir, step_x , step_y) - if(firing) - return FALSE - return ..() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/OpenFire() - if(firing) - balloon_alert(src, "already firing!") - return - if(!COOLDOWN_FINISHED(src, ranged_cooldown)) - balloon_alert(src, "on cooldown!") - return - firing = TRUE - set_dir_on_move = FALSE - icon_state = "brimdemon_firing" - move_resist = MOVE_FORCE_VERY_STRONG - add_overlay("brimdemon_telegraph_dir") - visible_message(span_danger("[src] starts charging!")) - balloon_alert(src, "charging...") - addtimer(CALLBACK(src, PROC_REF(fire_laser)), 1 SECONDS) - COOLDOWN_START(src, ranged_cooldown, ranged_cooldown_time) - -/mob/living/simple_animal/hostile/asteroid/brimdemon/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) - . = ..() - check_fire() - -/mob/living/simple_animal/hostile/asteroid/brimdemon/proc/check_fire() - if(key || QDELETED(target) || get_dist(src, target) > BRIMBEAM_RANGE || !(get_dir(src, target) in GLOB.cardinals)) - return - face_atom(target) - OpenFire() - -/// Fires a brimbeam, getting a line of turfs between it and the direction to the target and creating a brimbeam effect on every one of them. -/mob/living/simple_animal/hostile/asteroid/brimdemon/proc/fire_laser() - if(stat == DEAD) - return - visible_message(span_danger("[src] fires a brimbeam!")) - balloon_alert(src, "brimbeam fired") - playsound(src, 'sound/creatures/brimdemon.ogg', 150, FALSE, 0, 3) - cut_overlay("brimdemon_telegraph_dir") - var/turf/target_turf = get_ranged_target_turf(src, dir, BRIMBEAM_RANGE) - var/turf/origin_turf = get_turf(src) - var/list/affected_turfs = get_line(origin_turf, target_turf) - origin_turf - for(var/turf/affected_turf in affected_turfs) - var/blocked = FALSE - if(affected_turf.opacity) - blocked = TRUE - for(var/obj/potential_block in affected_turf.contents) - if(potential_block.opacity) - blocked = TRUE - break - if(blocked) - break - var/atom/new_brimbeam = new /obj/effect/brimbeam(affected_turf) - new_brimbeam.dir = dir - beamparts += new_brimbeam - for(var/mob/living/hit_mob in affected_turf.contents) - hit_mob.adjustFireLoss(25) - to_chat(hit_mob, span_userdanger("You're hit by [src]'s brimbeam!")) - if(length(beamparts)) - var/atom/last_brimbeam = beamparts[length(beamparts)] - last_brimbeam.icon_state = "brimbeam_end" - var/atom/first_brimbeam = beamparts[1] - first_brimbeam.icon_state = "brimbeam_start" - addtimer(CALLBACK(src, PROC_REF(end_laser)), 2 SECONDS) - -/// Deletes all the brimbeam parts and sets variables back to their initial ones. -/mob/living/simple_animal/hostile/asteroid/brimdemon/proc/end_laser() - if(stat != DEAD) - icon_state = initial(icon_state) - move_resist = initial(move_resist) - set_dir_on_move = initial(set_dir_on_move) - firing = FALSE - for(var/obj/effect/brimbeam/beam in beamparts) - animate(beam, time = 0.5 SECONDS, alpha = 0) - QDEL_IN(beam, 0.5 SECONDS) - beamparts -= beam - -/obj/effect/brimbeam - name = "brimbeam" - icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi' - icon_state = "brimbeam_mid" - layer = ABOVE_MOB_LAYER - plane = ABOVE_GAME_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - light_color = LIGHT_COLOR_BLOOD_MAGIC - light_power = 3 - light_range = 2 - -/obj/effect/brimbeam/Initialize(mapload) - . = ..() - START_PROCESSING(SSfastprocess, src) - -/obj/effect/brimbeam/Destroy() - STOP_PROCESSING(SSfastprocess, src) - return ..() - -/obj/effect/brimbeam/process() - for(var/mob/living/hit_mob in get_turf(src)) - damage(hit_mob) - -/obj/effect/brimbeam/proc/damage(mob/living/hit_mob) - hit_mob.adjustFireLoss(5) - to_chat(hit_mob, span_danger("You're damaged by [src]!")) - -/obj/item/crusher_trophy/brimdemon_fang - name = "brimdemon's fang" - icon_state = "brimdemon_fang" - desc = "A fang from a brimdemon's corpse." - denied_type = /obj/item/crusher_trophy/brimdemon_fang - var/static/list/comic_phrases = list("BOOM", "BANG", "KABLOW", "KAPOW", "OUCH", "BAM", "KAPOW", "WHAM", "POW", "KABOOM") - -/obj/item/crusher_trophy/brimdemon_fang/effect_desc() - return "mark detonation creates visual and audiosensory effects on the target" - -/obj/item/crusher_trophy/brimdemon_fang/on_mark_detonation(mob/living/target, mob/living/user) - target.balloon_alert_to_viewers("[pick(comic_phrases)]!") - playsound(target, 'sound/lavaland/brimdemon_crush.ogg', 100) - -/obj/effect/decal/cleanable/brimdust - name = "brimdust" - desc = "Dust from a brimdemon. It is considered valuable for its' botanical abilities." - icon_state = "brimdust" - icon = 'icons/obj/mining.dmi' - layer = FLOOR_CLEAN_LAYER - mergeable_decal = FALSE - -/obj/effect/decal/cleanable/brimdust/Initialize(mapload) - . = ..() - reagents.add_reagent(/datum/reagent/brimdust, 15) - -/obj/item/ore_sensor - name = "ore sensor" - desc = "Using demonic frequencies, this ear-mounted tool detects ores in the nearby terrain." - icon_state = "oresensor" - icon = 'icons/obj/mining.dmi' - slot_flags = ITEM_SLOT_EARS - var/range = 5 - var/cooldown = 4 SECONDS //between the standard and the advanced ore scanner in strength - COOLDOWN_DECLARE(ore_sensing_cooldown) - -/obj/item/ore_sensor/equipped(mob/user, slot, initial) - . = ..() - if(slot & ITEM_SLOT_EARS) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - -/obj/item/ore_sensor/dropped(mob/user, silent) - . = ..() - STOP_PROCESSING(SSobj, src) - -/obj/item/ore_sensor/process(seconds_per_tick) - if(!COOLDOWN_FINISHED(src, ore_sensing_cooldown)) - return - COOLDOWN_START(src, ore_sensing_cooldown, cooldown) - mineral_scan_pulse(get_turf(src), range) - -#undef BRIMBEAM_RANGE diff --git a/code/modules/spells/spell_types/pointed/_pointed.dm b/code/modules/spells/spell_types/pointed/_pointed.dm index 3d1c5b91b81..04c3ed47944 100644 --- a/code/modules/spells/spell_types/pointed/_pointed.dm +++ b/code/modules/spells/spell_types/pointed/_pointed.dm @@ -64,17 +64,17 @@ build_all_button_icons() return TRUE -/datum/action/cooldown/spell/pointed/InterceptClickOn(mob/living/caller, params, atom/click_target) +/datum/action/cooldown/spell/pointed/InterceptClickOn(mob/living/caller, params, atom/target) var/atom/aim_assist_target - if(aim_assist && isturf(click_target)) + if(aim_assist && isturf(target)) // Find any human in the list. We aren't picky, it's aim assist after all - aim_assist_target = locate(/mob/living/carbon/human) in click_target + aim_assist_target = locate(/mob/living/carbon/human) in target if(!aim_assist_target) // If we didn't find a human, we settle for any living at all - aim_assist_target = locate(/mob/living) in click_target + aim_assist_target = locate(/mob/living) in target - return ..(caller, params, aim_assist_target || click_target) + return ..(caller, params, aim_assist_target || target) /datum/action/cooldown/spell/pointed/is_valid_target(atom/cast_on) if(cast_on == owner) diff --git a/code/modules/spells/spell_types/pointed/swap.dm b/code/modules/spells/spell_types/pointed/swap.dm index ddeb08b4b8a..6b4a5e45e14 100644 --- a/code/modules/spells/spell_types/pointed/swap.dm +++ b/code/modules/spells/spell_types/pointed/swap.dm @@ -35,27 +35,27 @@ return FALSE return TRUE -/datum/action/cooldown/spell/pointed/swap/InterceptClickOn(mob/living/caller, params, atom/click_target) +/datum/action/cooldown/spell/pointed/swap/InterceptClickOn(mob/living/caller, params, atom/target) if(LAZYACCESS(params2list(params), RIGHT_CLICK)) if(!IsAvailable(feedback = TRUE)) return FALSE if(!target) return FALSE - if(!isliving(click_target) || isturf(click_target)) + if(!isliving(target) || isturf(target)) // Find any living being in the list. We aren't picky, it's aim assist after all - click_target = locate(/mob/living) in click_target - if(!click_target) + target = locate(/mob/living) in target + if(!target) to_chat(owner, span_warning("You can only select living beings as secondary target!")) return FALSE - if(click_target == owner) + if(target == owner) if(!isnull(second_target)) to_chat(owner, span_notice("You cancel your secondary swap target!")) second_target = null else to_chat(owner, span_warning("You have no secondary swap target!")) return FALSE - second_target = click_target - to_chat(owner, span_notice("You select [click_target.name] as a secondary swap target!")) + second_target = target + to_chat(owner, span_notice("You select [target.name] as a secondary swap target!")) return FALSE return ..() diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 571cecff3c0..70bf4abcf22 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -51,7 +51,6 @@ /mob/living/simple_animal/hostile/alien/queen/large, /mob/living/simple_animal/hostile/alien/sentinel, /mob/living/simple_animal/hostile/asteroid, - /mob/living/simple_animal/hostile/asteroid/brimdemon, /mob/living/simple_animal/hostile/asteroid/curseblob, /mob/living/simple_animal/hostile/asteroid/elite, /mob/living/simple_animal/hostile/asteroid/elite/broodmother, diff --git a/tgstation.dme b/tgstation.dme index 84f1df04d46..28a61d7603b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -811,6 +811,7 @@ #include "code\datums\ai\basic_mobs\basic_ai_behaviors\travel_towards.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\ventcrawling.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\write_on_paper.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\attack_adjacent_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\attack_obstacle_in_path.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\climb_tree.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\find_food.dm" @@ -818,6 +819,7 @@ #include "code\datums\ai\basic_mobs\basic_subtrees\find_parent.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\flee_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\maintain_distance.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\move_to_cardinal.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\opportunistic_ventcrawler.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\ranged_skirmish.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm" @@ -4296,6 +4298,10 @@ #include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_instrument.dm" #include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_loot.dm" #include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_vileworm.dm" +#include "code\modules\mob\living\basic\lavaland\brimdemon\brimbeam.dm" +#include "code\modules\mob\living\basic\lavaland\brimdemon\brimdemon.dm" +#include "code\modules\mob\living\basic\lavaland\brimdemon\brimdemon_ai.dm" +#include "code\modules\mob\living\basic\lavaland\brimdemon\brimdemon_loot.dm" #include "code\modules\mob\living\basic\lavaland\goldgrub\goldgrub.dm" #include "code\modules\mob\living\basic\lavaland\goldgrub\goldgrub_abilities.dm" #include "code\modules\mob\living\basic\lavaland\goldgrub\goldgrub_ai.dm" @@ -4630,7 +4636,6 @@ #include "code\modules\mob\living\simple_animal\hostile\megafauna\hierophant.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\legion.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\wendigo.dm" -#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\brimdemon.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\curse_blob.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\gutlunch.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\hivelord.dm" diff --git a/tools/UpdatePaths/Scripts/78424_simple_to_basic_brimdemon.txt b/tools/UpdatePaths/Scripts/78424_simple_to_basic_brimdemon.txt new file mode 100644 index 00000000000..9895ad7f5f4 --- /dev/null +++ b/tools/UpdatePaths/Scripts/78424_simple_to_basic_brimdemon.txt @@ -0,0 +1 @@ +/mob/living/simple_animal/hostile/asteroid/brimdemon : /mob/living/basic/mining/brimdemon{@OLD}