mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-19 02:54:41 +01:00
Port mob cooldown actions and AI behavior. (#29924)
* Port mob cooldown actions and AI behavior. * fix lint * fix missing override args * Update code/_onclick/click.dm Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> * Update code/_onclick/click.dm Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> * Update code/datums/actions/action.dm Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> * Update code/__DEFINES/ai/blackboard_defines.dm Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> * remove unused defines --------- Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
653e9e8c72
commit
ef41677463
@@ -47,6 +47,8 @@
|
||||
#define BB_BASIC_MOB_RETALIATE_LIST "BB_BASIC_MOB_SHITLIST"
|
||||
///Blackboard key for a whitelist typecache of "things we can target while trying to move"
|
||||
#define BB_OBSTACLE_TARGETING_WHITELIST "BB_targeting_whitelist"
|
||||
/// some behaviors that check current_target also set this on deep crit mobs
|
||||
#define BB_BASIC_MOB_EXECUTION_TARGET "BB_basic_execution_target"
|
||||
|
||||
//Hunting BB keys
|
||||
|
||||
|
||||
@@ -263,3 +263,13 @@
|
||||
|
||||
///from the ranged_attacks component for basic mobs: (mob/living/basic/firer, atom/target, modifiers)
|
||||
#define COMSIG_BASICMOB_POST_ATTACK_RANGED "basicmob_post_attack_ranged"
|
||||
|
||||
/// From base of /datum/action/cooldown/proc/PreActivate(), sent to the action owner: (datum/action/cooldown/activated)
|
||||
#define COMSIG_MOB_ABILITY_STARTED "mob_ability_base_started"
|
||||
/// Return to block the ability from starting / activating
|
||||
#define COMPONENT_BLOCK_ABILITY_START (1<<0)
|
||||
/// From base of /datum/action/cooldown/proc/PreActivate(), sent to the action owner: (datum/action/cooldown/finished)
|
||||
#define COMSIG_MOB_ABILITY_FINISHED "mob_ability_base_finished"
|
||||
|
||||
/// From base of /datum/action/cooldown/proc/set_statpanel_format(): (list/stat_panel_data)
|
||||
#define COMSIG_ACTION_SET_STATPANEL "ability_set_statpanel"
|
||||
|
||||
@@ -494,6 +494,7 @@
|
||||
/// Prepares a text to be used for maptext. Use this so it doesn't look hideous.
|
||||
#define MAPTEXT(text) {"<span class='maptext'>[##text]</span>"}
|
||||
#define MAPTEXT_CENTER(text) {"<span class='maptext' style='text-align: center'>[##text]</span>"}
|
||||
#define MAPTEXT_SMALL(text) {"<span style='font-family: \"Small Fonts\"; font-size: 12pt; line-height: 0.75; -dm-text-outline: 1px black'>[##text]</span>"}
|
||||
|
||||
//Fullscreen overlay resolution in tiles.
|
||||
#define FULLSCREEN_OVERLAY_RESOLUTION_X 15
|
||||
|
||||
+14
-2
@@ -51,8 +51,7 @@
|
||||
if(QDELETED(A))
|
||||
return
|
||||
|
||||
if(client?.click_intercept)
|
||||
client.click_intercept.InterceptClickOn(src, params, A)
|
||||
if(check_click_intercept(params,A))
|
||||
return
|
||||
|
||||
if(next_click > world.time)
|
||||
@@ -516,5 +515,18 @@
|
||||
click_turf.Click(location, control, list2params(modifiers))
|
||||
. = 1
|
||||
|
||||
/mob/proc/check_click_intercept(params,A)
|
||||
// Client level intercept
|
||||
if(client?.click_intercept)
|
||||
if(call(client.click_intercept, "InterceptClickOn")(src, params, A))
|
||||
return TRUE
|
||||
|
||||
// Mob level intercept
|
||||
if(click_interceptor)
|
||||
if(call(click_interceptor, "InterceptClickOn")(src, params, A))
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
#undef MAX_SAFE_BYOND_ICON_SCALE_TILES
|
||||
#undef MAX_SAFE_BYOND_ICON_SCALE_PX
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
var/action_disabled = FALSE
|
||||
/// The appearance used as an overlay for when the action is unavailable
|
||||
var/mutable_appearance/unavailable_effect
|
||||
/// If False, the owner of this action does not get a hud and cannot activate it on their own
|
||||
var/owner_has_control = TRUE
|
||||
|
||||
/datum/action/New(target)
|
||||
link_to(target)
|
||||
@@ -114,7 +116,7 @@
|
||||
Trigger()
|
||||
return FALSE
|
||||
|
||||
/datum/action/proc/IsAvailable()// returns 1 if all checks pass
|
||||
/datum/action/proc/IsAvailable(show_message = TRUE) // returns 1 if all checks pass
|
||||
if(!owner)
|
||||
return FALSE
|
||||
if((check_flags & AB_CHECK_HANDS_BLOCKED) && HAS_TRAIT(owner, TRAIT_HANDS_BLOCKED))
|
||||
@@ -194,6 +196,8 @@
|
||||
/datum/action/proc/update_button_status(atom/movable/screen/movable/action_button/button, force = FALSE)
|
||||
button.overlays -= unavailable_effect
|
||||
button.maptext = ""
|
||||
if(IsAvailable(show_message = FALSE))
|
||||
button.color = rgb(255, 255, 255, 255)
|
||||
if(should_draw_cooldown())
|
||||
apply_unavailable_effect(button)
|
||||
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
#define COOLDOWN_NO_DISPLAY_TIME (180 SECONDS)
|
||||
|
||||
/// Preset for an action that has a cooldown.
|
||||
/datum/action/cooldown
|
||||
transparent_when_unavailable = FALSE
|
||||
|
||||
/// The actual next time this ability can be used
|
||||
var/next_use_time = 0
|
||||
/// The stat panel this action shows up in the stat panel in. If null, will not show up.
|
||||
var/panel
|
||||
/// The default cooldown applied when StartCooldown() is called
|
||||
var/cooldown_time = 0
|
||||
/// The default melee cooldown applied after the ability ends. If set to null, copies cooldown_time.
|
||||
var/melee_cooldown_time = 0
|
||||
/// The actual next time the owner of this action can melee
|
||||
var/next_melee_use_time = 0
|
||||
/// Whether or not you want the cooldown for the ability to display in text form
|
||||
var/text_cooldown = TRUE
|
||||
/// Significant figures to round cooldown to
|
||||
var/cooldown_rounding = 0.1
|
||||
/// Shares cooldowns with other abiliies, bitflag
|
||||
var/shared_cooldown = NONE
|
||||
/// List of prerequisite actions that are used in this sequenced ability, you cannot put other sequenced abilities in this
|
||||
var/list/sequence_actions
|
||||
/// List of prerequisite actions that have been initialized
|
||||
var/list/initialized_actions
|
||||
|
||||
// These are only used for click_to_activate actions
|
||||
/// Setting for intercepting clicks before activating the ability
|
||||
var/click_to_activate = FALSE
|
||||
/// The cooldown added onto the user's next click.
|
||||
var/click_cd_override = CLICK_CD_CLICK_ABILITY
|
||||
/// If TRUE, we will unset after using our click intercept.
|
||||
var/unset_after_click = TRUE
|
||||
/// What icon to replace our mouse cursor with when active. Optional
|
||||
var/ranged_mousepointer
|
||||
/// The base icon_state of this action's background
|
||||
var/base_background_icon_state
|
||||
/// The icon state the background uses when active
|
||||
var/active_background_icon_state
|
||||
/// The base icon_state of the overlay we apply
|
||||
var/base_overlay_icon_state
|
||||
/// The active icon_state of the overlay we apply
|
||||
var/active_overlay_icon_state
|
||||
/// The base icon state of the spell's button icon, used for editing the icon "off"
|
||||
var/base_icon_state
|
||||
/// The active icon state of the spell's button icon, used for editing the icon "on"
|
||||
var/active_icon_state
|
||||
|
||||
/datum/action/cooldown/New(Target, original = TRUE)
|
||||
. = ..()
|
||||
if(active_background_icon_state)
|
||||
base_background_icon_state ||= background_icon_state
|
||||
if(active_overlay_icon_state)
|
||||
base_overlay_icon_state ||= overlay_icon_state
|
||||
if(active_icon_state)
|
||||
base_icon_state ||= button_icon_state
|
||||
|
||||
if(isnull(melee_cooldown_time))
|
||||
melee_cooldown_time = cooldown_time
|
||||
|
||||
if(original)
|
||||
create_sequence_actions()
|
||||
|
||||
/datum/action/cooldown/create_button()
|
||||
var/atom/movable/screen/movable/action_button/button = ..()
|
||||
button.maptext = ""
|
||||
button.maptext_x = 4
|
||||
button.maptext_y = 2
|
||||
button.maptext_width = 32
|
||||
button.maptext_height = 16
|
||||
return button
|
||||
|
||||
/datum/action/cooldown/update_button_status(atom/movable/screen/movable/action_button/button, force = FALSE)
|
||||
. = ..()
|
||||
var/time_left = max(next_use_time - world.time, 0)
|
||||
if(!text_cooldown || !owner || time_left == 0 || time_left >= COOLDOWN_NO_DISPLAY_TIME)
|
||||
button.maptext = ""
|
||||
else
|
||||
if(cooldown_rounding > 0)
|
||||
button.maptext = MAPTEXT_SMALL("[round(time_left/10, cooldown_rounding)]")
|
||||
else
|
||||
button.maptext = MAPTEXT_SMALL("[round(time_left/10)]")
|
||||
|
||||
if(!IsAvailable() || !is_action_active(button))
|
||||
return
|
||||
// If we don't change the icon state, or don't apply a special overlay,
|
||||
if(active_background_icon_state || active_icon_state || active_overlay_icon_state)
|
||||
return
|
||||
// ...we need to show it's active somehow. So, make it greeeen
|
||||
button.color = COLOR_GREEN
|
||||
|
||||
/datum/action/cooldown/apply_button_background(atom/movable/screen/movable/action_button/current_button, force)
|
||||
if(active_background_icon_state)
|
||||
background_icon_state = is_action_active(current_button) ? active_background_icon_state : base_background_icon_state
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/apply_button_icon(atom/movable/screen/movable/action_button/current_button, force)
|
||||
if(active_icon_state)
|
||||
button_icon_state = is_action_active(current_button) ? active_icon_state : base_icon_state
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/apply_button_overlay(atom/movable/screen/movable/action_button/current_button, force)
|
||||
if(active_overlay_icon_state)
|
||||
overlay_icon_state = is_action_active(current_button) ? active_overlay_icon_state : base_overlay_icon_state
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/is_action_active(atom/movable/screen/movable/action_button/current_button)
|
||||
return click_to_activate && current_button.our_hud?.mymob?.click_interceptor == src
|
||||
|
||||
/datum/action/cooldown/Destroy()
|
||||
QDEL_LIST_CONTENTS(initialized_actions)
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/Grant(mob/granted_to)
|
||||
. = ..()
|
||||
if(!owner)
|
||||
return
|
||||
build_all_button_icons()
|
||||
if(next_use_time > world.time)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
RegisterSignal(granted_to, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(handle_melee_attack))
|
||||
for(var/datum/action/cooldown/ability as anything in initialized_actions)
|
||||
ability.Grant(granted_to)
|
||||
|
||||
/datum/action/cooldown/Remove(mob/removed_from)
|
||||
UnregisterSignal(removed_from, COMSIG_HOSTILE_PRE_ATTACKINGTARGET)
|
||||
if(click_to_activate && removed_from.click_interceptor == src)
|
||||
unset_click_ability(removed_from, refund_cooldown = FALSE)
|
||||
for(var/datum/action/cooldown/ability as anything in initialized_actions)
|
||||
ability.Remove(removed_from)
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/IsAvailable(show_message = TRUE)
|
||||
return ..() && (next_use_time <= world.time)
|
||||
|
||||
/// Initializes any sequence actions
|
||||
/datum/action/cooldown/proc/create_sequence_actions()
|
||||
if(!LAZYLEN(sequence_actions))
|
||||
return
|
||||
// remove existing actions if any
|
||||
QDEL_LIST_CONTENTS(initialized_actions)
|
||||
initialized_actions = list()
|
||||
for(var/type_path in sequence_actions)
|
||||
var/datum/action/cooldown/ability = new type_path(target, original = FALSE)
|
||||
// prevents clients from using the individual abilities in sequences (this stops it from being added to mob actions when granted as well)
|
||||
ability.owner_has_control = FALSE
|
||||
// [ability] = delay
|
||||
initialized_actions[ability] = sequence_actions[type_path]
|
||||
|
||||
/// Starts a cooldown time to be shared with similar abilities
|
||||
/// Will use default cooldown time if an override is not specified
|
||||
/datum/action/cooldown/proc/StartCooldown(override_cooldown_time, override_melee_cooldown_time)
|
||||
// "Shared cooldowns" covers actions which are not the same type,
|
||||
// but have the same cooldown group and are on the same mob
|
||||
if(shared_cooldown != NONE)
|
||||
StartCooldownOthers(override_cooldown_time)
|
||||
|
||||
StartCooldownSelf(override_cooldown_time)
|
||||
|
||||
if(isnum(override_melee_cooldown_time))
|
||||
next_melee_use_time = world.time + override_melee_cooldown_time
|
||||
else
|
||||
next_melee_use_time = world.time + melee_cooldown_time
|
||||
|
||||
/// Starts a cooldown time for this ability only
|
||||
/// Will use default cooldown time if an override is not specified
|
||||
/datum/action/cooldown/proc/StartCooldownSelf(override_cooldown_time)
|
||||
if(isnum(override_cooldown_time))
|
||||
next_use_time = world.time + override_cooldown_time
|
||||
else
|
||||
next_use_time = world.time + cooldown_time
|
||||
// Don't start a cooldown if we have a cooldown time of 0 seconds
|
||||
if(next_use_time == world.time)
|
||||
return
|
||||
build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/// Starts a cooldown time for other abilities that share a cooldown with this. Has some niche usage with more complicated attack ai!
|
||||
/// Will use default cooldown time if an override is not specified
|
||||
/datum/action/cooldown/proc/StartCooldownOthers(override_cooldown_time)
|
||||
if(!length(owner?.actions))
|
||||
return // Possible if they have an action they don't control
|
||||
for(var/datum/action/cooldown/shared_ability in owner.actions - src)
|
||||
if(!(shared_cooldown & shared_ability.shared_cooldown))
|
||||
continue
|
||||
if(isnum(override_cooldown_time))
|
||||
shared_ability.StartCooldownSelf(override_cooldown_time)
|
||||
else
|
||||
shared_ability.StartCooldownSelf(cooldown_time)
|
||||
|
||||
/// Resets the cooldown of this ability
|
||||
/datum/action/cooldown/proc/ResetCooldown()
|
||||
next_use_time = world.time
|
||||
build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
|
||||
/// Re-enables this cooldown action
|
||||
/datum/action/cooldown/proc/enable()
|
||||
action_disabled = FALSE
|
||||
build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
|
||||
/// Disables this cooldown action
|
||||
/datum/action/cooldown/proc/disable()
|
||||
action_disabled = TRUE
|
||||
build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
|
||||
/// Re-enables all cooldown actions
|
||||
/datum/action/cooldown/proc/enable_cooldown_actions()
|
||||
for(var/datum/action/cooldown/cd_action in owner.actions)
|
||||
cd_action.enable()
|
||||
|
||||
/// Disables all cooldown actions
|
||||
/datum/action/cooldown/proc/disable_cooldown_actions()
|
||||
for(var/datum/action/cooldown/cd_action in owner.actions)
|
||||
cd_action.disable()
|
||||
|
||||
/datum/action/cooldown/Trigger(trigger_flags, atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!owner)
|
||||
return FALSE
|
||||
|
||||
var/mob/user = usr || owner
|
||||
|
||||
// If our cooldown action is a click_to_activate action:
|
||||
// The actual action is activated on whatever the user clicks on -
|
||||
// the target is what the action is being used on
|
||||
// In trigger, we handle setting the click intercept
|
||||
if(click_to_activate)
|
||||
if(target)
|
||||
// For automatic / mob handling
|
||||
return InterceptClickOn(user, null, target)
|
||||
|
||||
var/datum/action/cooldown/already_set = user.click_interceptor
|
||||
if(already_set == src)
|
||||
// if we clicked ourself and we're already set, unset and return
|
||||
return unset_click_ability(user, refund_cooldown = TRUE)
|
||||
|
||||
else if(istype(already_set))
|
||||
// if we have an active set already, unset it before we set our's
|
||||
already_set.unset_click_ability(user, refund_cooldown = TRUE)
|
||||
|
||||
return set_click_ability(user)
|
||||
|
||||
// If our cooldown action is not a click_to_activate action:
|
||||
// We can just continue on and use the action
|
||||
// the target is the user of the action (often, the owner)
|
||||
return PreActivate(user)
|
||||
|
||||
/// Intercepts client owner clicks to activate the ability
|
||||
/datum/action/cooldown/proc/InterceptClickOn(mob/living/clicker, params, atom/target)
|
||||
if(!IsAvailable(show_message = TRUE))
|
||||
return FALSE
|
||||
if(!target)
|
||||
return FALSE
|
||||
// The actual action begins here
|
||||
if(!PreActivate(target))
|
||||
return FALSE
|
||||
|
||||
// And if we reach here, the action was complete successfully
|
||||
if(unset_after_click)
|
||||
unset_click_ability(clicker, refund_cooldown = FALSE)
|
||||
clicker.next_click = world.time + click_cd_override
|
||||
|
||||
return TRUE
|
||||
|
||||
/// For signal calling
|
||||
/datum/action/cooldown/proc/PreActivate(atom/target)
|
||||
if(SEND_SIGNAL(owner, COMSIG_MOB_ABILITY_STARTED, src, target) & COMPONENT_BLOCK_ABILITY_START)
|
||||
return
|
||||
// Note, that PreActivate handles no cooldowns at all by default.
|
||||
// Be sure to call StartCooldown() in Activate() where necessary.
|
||||
. = Activate(target)
|
||||
// There is a possibility our action (or owner) is qdeleted in Activate().
|
||||
if(!QDELETED(src) && !QDELETED(owner))
|
||||
SEND_SIGNAL(owner, COMSIG_MOB_ABILITY_FINISHED, src)
|
||||
|
||||
/// To be implemented by subtypes (if not generic)
|
||||
/datum/action/cooldown/proc/Activate(atom/target)
|
||||
var/total_delay = 0
|
||||
for(var/datum/action/cooldown/ability as anything in initialized_actions)
|
||||
if(LAZYLEN(ability.initialized_actions) > 0)
|
||||
ability.initialized_actions = list()
|
||||
addtimer(CALLBACK(ability, PROC_REF(Activate), target), total_delay)
|
||||
total_delay += initialized_actions[ability]
|
||||
StartCooldown()
|
||||
|
||||
/// Cancels melee attacks if they are on cooldown.
|
||||
/datum/action/cooldown/proc/handle_melee_attack(mob/source, mob/target)
|
||||
SIGNAL_HANDLER
|
||||
if(next_melee_use_time > world.time)
|
||||
return COMPONENT_HOSTILE_NO_ATTACK
|
||||
|
||||
/datum/action/cooldown/process()
|
||||
if(!owner || (next_use_time - world.time) <= 0)
|
||||
build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
return
|
||||
|
||||
build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
|
||||
/**
|
||||
* Set our action as the click override on the passed mob.
|
||||
*/
|
||||
/datum/action/cooldown/proc/set_click_ability(mob/on_who)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
on_who.click_interceptor = src
|
||||
// TODO: Support custom mouse cursors for click abilities
|
||||
build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Unset our action as the click override of the passed mob.
|
||||
*
|
||||
* if refund_cooldown is TRUE, we are being unset by the user clicking the action off
|
||||
* if refund_cooldown is FALSE, we are being forcefully unset, likely by someone actually using the action
|
||||
*/
|
||||
/datum/action/cooldown/proc/unset_click_ability(mob/on_who, refund_cooldown = TRUE)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
on_who.click_interceptor = null
|
||||
// TODO: Support unsetting custom mouse cursors for click abilities
|
||||
build_all_button_icons(UPDATE_BUTTON_STATUS)
|
||||
return TRUE
|
||||
|
||||
/// Formats the action to be returned to the stat panel.
|
||||
/datum/action/cooldown/proc/set_statpanel_format()
|
||||
if(!panel)
|
||||
return null
|
||||
|
||||
var/time_remaining = max(next_use_time - world.time, 0)
|
||||
var/time_remaining_in_seconds = round(time_remaining / 10, 0.1)
|
||||
var/cooldown_time_in_seconds = round(cooldown_time / 10, 0.1)
|
||||
|
||||
var/list/stat_panel_data = list()
|
||||
|
||||
// Pass on what panel we should be displayed in.
|
||||
stat_panel_data[PANEL_DISPLAY_PANEL] = panel
|
||||
// Also pass on the name of the spell, with some spacing
|
||||
stat_panel_data[PANEL_DISPLAY_NAME] = " - [name]"
|
||||
|
||||
// No cooldown time at all, just show the ability
|
||||
if(cooldown_time_in_seconds <= 0)
|
||||
stat_panel_data[PANEL_DISPLAY_STATUS] = ""
|
||||
|
||||
// It's a toggle-active ability, show if it's active
|
||||
else if(click_to_activate && owner.click_interceptor == src)
|
||||
stat_panel_data[PANEL_DISPLAY_STATUS] = "ACTIVE"
|
||||
|
||||
// It's on cooldown, show the cooldown
|
||||
else if(time_remaining_in_seconds > 0)
|
||||
stat_panel_data[PANEL_DISPLAY_STATUS] = "CD - [time_remaining_in_seconds]s / [cooldown_time_in_seconds]s"
|
||||
|
||||
// It's not on cooldown, show that it is ready
|
||||
else
|
||||
stat_panel_data[PANEL_DISPLAY_STATUS] = "READY"
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_ACTION_SET_STATPANEL, stat_panel_data)
|
||||
|
||||
return stat_panel_data
|
||||
|
||||
#undef COOLDOWN_NO_DISPLAY_TIME
|
||||
@@ -115,7 +115,7 @@
|
||||
desc = "Recall yourself, and anyone nearby, to an attuned hierophant beacon at any time.<br>If the beacon is still attached, will detach it."
|
||||
button_icon_state = "vortex_recall"
|
||||
|
||||
/datum/action/item_action/vortex_recall/IsAvailable()
|
||||
/datum/action/item_action/vortex_recall/IsAvailable(show_message = TRUE)
|
||||
if(istype(target, /obj/item/hierophant_club))
|
||||
var/obj/item/hierophant_club/H = target
|
||||
if(H.teleporting)
|
||||
@@ -221,7 +221,7 @@
|
||||
/datum/action/item_action/jetpack_stabilization
|
||||
name = "Toggle Jetpack Stabilization"
|
||||
|
||||
/datum/action/item_action/jetpack_stabilization/IsAvailable()
|
||||
/datum/action/item_action/jetpack_stabilization/IsAvailable(show_message = TRUE)
|
||||
var/obj/item/tank/jetpack/J = target
|
||||
if(!istype(J) || !J.on)
|
||||
return FALSE
|
||||
@@ -319,7 +319,7 @@
|
||||
/datum/action/item_action/organ_action
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
|
||||
/datum/action/item_action/organ_action/IsAvailable()
|
||||
/datum/action/item_action/organ_action/IsAvailable(show_message = TRUE)
|
||||
var/obj/item/organ/internal/I = target
|
||||
if(!I.owner)
|
||||
return FALSE
|
||||
@@ -360,7 +360,7 @@
|
||||
/datum/action/item_action/accessory
|
||||
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
|
||||
|
||||
/datum/action/item_action/accessory/IsAvailable()
|
||||
/datum/action/item_action/accessory/IsAvailable(show_message = TRUE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/datum/action/cooldown/mob_cooldown
|
||||
name = "Standard Mob Cooldown Ability"
|
||||
button_icon_state = "sniper_zoom"
|
||||
desc = "Click this ability to attack."
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
cooldown_time = 5 SECONDS
|
||||
click_to_activate = TRUE
|
||||
shared_cooldown = MOB_SHARED_COOLDOWN_1
|
||||
@@ -38,13 +38,13 @@
|
||||
spell.AltClick(usr)
|
||||
return TRUE
|
||||
|
||||
/datum/action/spell_action/IsAvailable()
|
||||
/datum/action/spell_action/IsAvailable(show_message = TRUE)
|
||||
if(!target)
|
||||
return FALSE
|
||||
var/datum/spell/spell = target
|
||||
|
||||
if(owner)
|
||||
return spell.can_cast(owner, show_message = TRUE)
|
||||
return spell.can_cast(owner, show_message)
|
||||
return FALSE
|
||||
|
||||
/datum/action/spell_action/apply_unavailable_effect(atom/movable/screen/movable/action_button/button)
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* # Targeted Mob Ability
|
||||
* Attempts to use a mob's cooldown ability on a target
|
||||
*/
|
||||
/datum/ai_behavior/targeted_mob_ability
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key)
|
||||
var/datum/action/cooldown/ability = controller.blackboard[ability_key]
|
||||
var/mob/living/target = controller.blackboard[target_key]
|
||||
if(QDELETED(ability) || QDELETED(target))
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
var/mob/pawn = controller.pawn
|
||||
pawn.face_atom(target)
|
||||
var/result = ability.Trigger(target = target)
|
||||
if(result)
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
|
||||
/**
|
||||
* # Try Mob Ability and plan execute
|
||||
* Attempts to use a mob's cooldown ability on a target and then move the target into a special target blackboard datum
|
||||
* Doesn't need another subtype to clear BB_BASIC_MOB_EXECUTION_TARGET because it will be the target key for the normal action
|
||||
*/
|
||||
/datum/ai_behavior/targeted_mob_ability/and_plan_execute
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/and_plan_execute/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
|
||||
controller.set_blackboard_key(BB_BASIC_MOB_EXECUTION_TARGET, controller.blackboard[target_key])
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* # Try Mob Ability and clear target
|
||||
* Attempts to use a mob's cooldown ability on a target and releases the target when the action completes
|
||||
*/
|
||||
/datum/ai_behavior/targeted_mob_ability/and_clear_target
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/and_clear_target/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
|
||||
. = ..()
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
/**
|
||||
* Attempts to move into the provided range and then use a mob's cooldown ability on a target
|
||||
*/
|
||||
/datum/ai_behavior/targeted_mob_ability/min_range
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
|
||||
required_distance = 6
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/min_range/setup(datum/ai_controller/controller, ability_key, target_key)
|
||||
. = ..()
|
||||
var/atom/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/min_range/short
|
||||
required_distance = 3
|
||||
@@ -0,0 +1,34 @@
|
||||
/// Attempts to use a mob ability on a target
|
||||
/datum/ai_planning_subtree/targeted_mob_ability
|
||||
/// Blackboard key for the ability
|
||||
var/ability_key = BB_TARGETED_ACTION
|
||||
/// Blackboard key for where the target ref is stored
|
||||
var/target_key = BB_BASIC_MOB_CURRENT_TARGET
|
||||
/// Behaviour to perform using ability
|
||||
var/use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability
|
||||
/// If true we terminate planning after trying to use the ability.
|
||||
var/finish_planning = TRUE
|
||||
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(!ability_key)
|
||||
CRASH("You forgot to tell this mob where to find its ability")
|
||||
|
||||
if(!controller.blackboard_key_exists(target_key))
|
||||
return
|
||||
|
||||
var/datum/action/cooldown/using_action = controller.blackboard[ability_key]
|
||||
if(!using_action?.IsAvailable())
|
||||
return
|
||||
if(!additional_ability_checks(controller, using_action))
|
||||
return
|
||||
|
||||
controller.queue_behavior(use_ability_behaviour, ability_key, target_key)
|
||||
if(finish_planning)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
/// Any additional checks before we queue the behaviour
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/proc/additional_ability_checks(datum/ai_controller/controller, datum/action/cooldown/using_action)
|
||||
return TRUE
|
||||
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/continue_planning
|
||||
finish_planning = FALSE
|
||||
@@ -128,7 +128,7 @@
|
||||
hand_magic = null
|
||||
..()
|
||||
|
||||
/datum/action/innate/cult/blood_spell/IsAvailable()
|
||||
/datum/action/innate/cult/blood_spell/IsAvailable(show_message = TRUE)
|
||||
if(!IS_CULTIST(owner) || owner.incapacitated() || !charges)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS
|
||||
buttontooltipstyle = "cult"
|
||||
|
||||
/datum/action/innate/cult/IsAvailable()
|
||||
/datum/action/innate/cult/IsAvailable(show_message = TRUE)
|
||||
if(!IS_CULTIST(owner))
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -66,7 +66,7 @@
|
||||
name = "Spiritual Communion"
|
||||
desc = "Conveys a message from the spirit realm that all cultists can hear."
|
||||
|
||||
/datum/action/innate/cult/comm/spirit/IsAvailable()
|
||||
/datum/action/innate/cult/comm/spirit/IsAvailable(show_message = TRUE)
|
||||
return TRUE
|
||||
|
||||
/datum/action/innate/cult/comm/spirit/cultist_commune(mob/living/user, message)
|
||||
@@ -95,7 +95,7 @@
|
||||
button_icon_state = GET_CULT_DATA(tome_icon, "tome")
|
||||
..()
|
||||
|
||||
/datum/action/innate/cult/check_progress/IsAvailable()
|
||||
/datum/action/innate/cult/check_progress/IsAvailable(show_message = TRUE)
|
||||
return IS_CULTIST(owner) || isobserver(owner)
|
||||
|
||||
/datum/action/innate/cult/check_progress/Activate()
|
||||
|
||||
@@ -167,9 +167,6 @@
|
||||
button_icon_state = "demon_comms"
|
||||
background_icon_state = "bg_demon"
|
||||
|
||||
/datum/action/innate/demon_whisper/IsAvailable()
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/demon_whisper/proc/choose_targets(mob/user = usr)//yes i am copying from telepathy..hush...
|
||||
var/list/validtargets = list()
|
||||
for(var/mob/living/M in view(user.client.maxview(), get_turf(user)))
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
button_icon_state = "reset"
|
||||
var/cooldown_timer
|
||||
|
||||
/datum/action/guardian/reset_guardian/IsAvailable()
|
||||
/datum/action/guardian/reset_guardian/IsAvailable(show_message = TRUE)
|
||||
if(cooldown_timer)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
on_cooldown = FALSE
|
||||
build_all_button_icons()
|
||||
|
||||
/datum/action/item_action/agent_box/IsAvailable()
|
||||
/datum/action/item_action/agent_box/IsAvailable(show_message = TRUE)
|
||||
if(..() && !on_cooldown)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -888,7 +888,7 @@
|
||||
name = "Remove a card - Remove a single card from the hand."
|
||||
button_icon_state = "remove_card"
|
||||
|
||||
/datum/action/item_action/remove_card/IsAvailable()
|
||||
/datum/action/item_action/remove_card/IsAvailable(show_message = TRUE)
|
||||
var/obj/item/cardhand/C = target
|
||||
if(length(C.cards) <= 1)
|
||||
return FALSE
|
||||
|
||||
@@ -510,7 +510,7 @@
|
||||
var/last_teleport = 0
|
||||
var/tele_range = 6
|
||||
|
||||
/datum/action/innate/unstable_teleport/IsAvailable()
|
||||
/datum/action/innate/unstable_teleport/IsAvailable(show_message = TRUE)
|
||||
if(..())
|
||||
if(world.time > last_teleport + cooldown && !activated)
|
||||
return 1
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
background_icon_state = "bg_alien"
|
||||
var/needs_growth = NO_GROWTH_NEEDED
|
||||
|
||||
/datum/action/innate/slime/IsAvailable()
|
||||
/datum/action/innate/slime/IsAvailable(show_message = TRUE)
|
||||
if(..())
|
||||
var/mob/living/simple_animal/slime/S = owner
|
||||
if(needs_growth == GROWTH_NEEDED)
|
||||
|
||||
@@ -250,6 +250,9 @@
|
||||
/// Does this mob speak OOC?
|
||||
/// Controls whether they can say some symbols.
|
||||
var/speaks_ooc = FALSE
|
||||
/// Allows a datum to intercept all click calls this mob is the source of.
|
||||
/// This is *not* necessarily an instance of [/datum/click_intercept].
|
||||
var/datum/click_interceptor
|
||||
|
||||
/// For storing what do_after's something has, key = string, value = amount of interactions of that type happening.
|
||||
var/list/do_afters
|
||||
|
||||
@@ -103,7 +103,7 @@ GLOBAL_DATUM_INIT(multispin_words, /regex, regex("like a record baby"))
|
||||
..()
|
||||
cords = target
|
||||
|
||||
/datum/action/item_action/organ_action/colossus/IsAvailable()
|
||||
/datum/action/item_action/organ_action/colossus/IsAvailable(show_message = TRUE)
|
||||
if(world.time < cords.next_command)
|
||||
return FALSE
|
||||
if(!owner)
|
||||
|
||||
@@ -446,10 +446,12 @@
|
||||
#include "code\datums\vision_override.dm"
|
||||
#include "code\datums\vv_core_topics.dm"
|
||||
#include "code\datums\actions\action.dm"
|
||||
#include "code\datums\actions\cooldown_action.dm"
|
||||
#include "code\datums\actions\generic_action.dm"
|
||||
#include "code\datums\actions\innate_action.dm"
|
||||
#include "code\datums\actions\item_action.dm"
|
||||
#include "code\datums\actions\spell_action.dm"
|
||||
#include "code\datums\actions\mobs\mobcooldown.dm"
|
||||
#include "code\datums\ai\ai_behavior.dm"
|
||||
#include "code\datums\ai\ai_controller.dm"
|
||||
#include "code\datums\ai\ai_planning_subtree.dm"
|
||||
@@ -462,6 +464,7 @@
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\nearest_targeting.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\run_away_from_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\stop_and_stare.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\targeted_mob_abilities.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\swirl_around.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\targeting.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm"
|
||||
@@ -481,6 +484,7 @@
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\swirl_around_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\stare_at_thing.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\target_retaliate.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\targeted_mob_ability.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\tip_reaction.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\use_mob_ability.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\ventcrawl_find_target.dm"
|
||||
|
||||
Reference in New Issue
Block a user