mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-17 11:05:16 +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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user