mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-31 00:58:26 +01:00
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 🆑 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 /🆑
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ..()
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
. = ..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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 ..()
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
. = ..()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+3
-6
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
+6
-1
@@ -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"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/mob/living/simple_animal/hostile/asteroid/brimdemon : /mob/living/basic/mining/brimdemon{@OLD}
|
||||
Reference in New Issue
Block a user