mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-22 03:27:05 +01:00
* Initial commit - FLOCKMIND - Probably has like a billion things to fix * Do after conversions * Config * Moved the files, icon fixes * Tick everything, language work, event, spawn landmark, role prefs, beginning mob port * Spans and some other fixes. Also the tickening * More tickening * More fixes. Lots of fixes. * More Fixes * A whole lot more. Also flock TGUI. * Fixes fixes fixes fixes fixes * FIXES * More fixes - PR ready, still needs a fuckton of testing * Fixes * fix incomplete upstream merge * fix FlockPanel + sort button name * TGUI review * Fixes tealprint list * Fixes * More fixes * Incapacitator Fix * Filenames * Linters * Interceptor range buff * Reagent counts * Linters * Fabricator vendor fix * Keybinds and HUD - Flockdrones, Fixes Vendor Conversion, Cube Materials * Reworks reagent, adds flock grilles, fixes compute node overlays * Intent-based flockdrone parts * Intent based drone parts * Radial control panel for controlling drones manually, phasing through windows/grilles * Movement fixes * Radio talk power, stare fix * Flock health HUD * Fixes flock lights, linters * Unit tests * Adds countdown to relay * Relay improvements * Small fix * Logic Schmogic * Relay overlay and looping sound effect * Ignore air when converting turfs * Cage fixes and improvements * Improved flock bolt * Turret conversions * Flock bolts taze simple or basic mobs * Sentience type * Fixe * Linter * tgui review stage 2 * Concentrated Repair Burst * Improves radio detection * Removes extra space * Adds healing visual effect * Cube tech levels * Ghooost * Excess * Flock doors, chairs, lattices. Centralizes conversion code. Crafting with Gnesis * Update code/modules/antagonists/flockmind/ai_behaviors/flock_wander.dm Co-authored-by: Kapu1178 <75460809+Kapu1178@users.noreply.github.com> Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> * Fixes the fix * Astar movement detection * Fix, extraneous code, language stuff * Language fixes and wander fix * Fixes * Another fix * Lints * Another linter * Language improvement * More language improvements * Time requirement and appearing in orbit menu as an antag * Cube glow * TGUI * Minicache * Linters * Grammar * Material ID fix * Lid fix * Reagent turf reaction * Reagent fix * Butcher results * Conversion rates * Flock stare fix * Fixes stare behavior * Staring * Flock mob blood * Flock mobs gibs and blood. Also some runtime fixes * Flock mobs now resist out of grabs, buckles, lockers, and more * Fixes flock orbit, fixes a runtime I think, * Target mechs, damage mechs, other bug fixes * Cage fix * Cage resist change * Some mind changes, gatecrash buff * Drones now shoot mechs, stare improvement * Cut down on spam a little * Nest fix * No more resist spam * Fixed drone death control * Resist statement * Makes the relay alarm scarier * Fixes dead flock camera mobs having no ghost sprite, something with ghosting * Enhanced flockphasing * Improved flockmob pathing * Added required turf restriction to relay * Increased needed bandwidth for relay construction * Nerfed drone substrate rate * Added new status tab items for relay progress * Another relay cost adjustment * Improves drone AI responsiveness * Computer frames now become flock computers * Improves target finding for conversion, building, and replicating * Reduced flock event pop requirements * Adjusts flock protection on structures. Adjusts overlays. * Relay unlock tweak * Fixwes flock being able to gib mech'd AIs with one button * Map conflict * Flock can no longer be outed by merely existing * Fied bug causing drones to shoot themselves * Prevents mobs from attacking while in a cage * Converter tool can now open closets and crates. * Adds descriptions to flockdrone tools. * More informatic blurbs * Adds xenobiology organs * Organ lint * TGUI merge * bundle and mm --------- Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Co-authored-by: Toastical <20125180+Toastical@users.noreply.github.com> Co-authored-by: Kapu1178 <75460809+Kapu1178@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
378 lines
14 KiB
Plaintext
378 lines
14 KiB
Plaintext
#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
|
|
|
|
if(!is_valid_target(target))
|
|
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
|
|
|
|
/**
|
|
* Check if the target we're casting on is a valid target.
|
|
* For no-target (self cast) actions, the target being checked (cast_on) is the caster.
|
|
* For click_to_activate actions, the target being checked is the clicked atom.
|
|
*
|
|
* Return TRUE if cast_on is valid, FALSE otherwise
|
|
*/
|
|
/datum/action/cooldown/proc/is_valid_target(atom/cast_on)
|
|
return TRUE
|
|
|
|
#undef COOLDOWN_NO_DISPLAY_TIME
|