diff --git a/code/datums/action.dm b/code/datums/action.dm index 53d4359d77f1e..6c06d9248a794 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -15,7 +15,19 @@ var/button_icon_state = "default" //And this is the state for the action icon var/mob/owner - var/has_cooldown_timer = FALSE + /// The time at which the cooldown timer will end + VAR_PRIVATE/cooldown_timer_end = 0 + /// Overlay currently applied to this action + VAR_PRIVATE/mutable_appearance/timer_overlay + + /// Timer icon file + VAR_PROTECTED/timer_icon = 'icons/effects/cooldown.dmi' + /// Icon state for the timer icon + VAR_PROTECTED/timer_icon_state_active = "second" + + /// Set this to disable auto-processing on cooldown for things that add custom behaviours like spells. + /// Avoid this if possible. + var/override_cooldown_behaviour = FALSE /datum/action/New(Target) link_to(Target) @@ -66,9 +78,6 @@ M.client.screen += button button.locked = M.client.prefs.read_player_preference(/datum/preference/toggle/buttons_locked) || button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE //even if it's not defaultly locked we should remember we locked it before button.moved = button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE - var/obj/effect/proc_holder/spell/spell_proc_holder = button.linked_action.target - if(istype(spell_proc_holder) && spell_proc_holder.text_overlay) - M.client.images += spell_proc_holder.text_overlay M.update_action_buttons() else Remove(owner) @@ -101,6 +110,8 @@ /datum/action/proc/IsAvailable() if(!owner) return FALSE + if (cooldown_timer_end && world.time < cooldown_timer_end) + return FALSE if((check_flags & AB_CHECK_HANDS_BLOCKED) && HAS_TRAIT(owner, TRAIT_HANDS_BLOCKED)) return FALSE if((check_flags & AB_CHECK_INCAPACITATED) && HAS_TRAIT(owner, TRAIT_INCAPACITATED)) @@ -113,6 +124,9 @@ return FALSE return TRUE +/datum/action/process(delta_time) + UpdateButtonIcon(TRUE, FALSE) + /datum/action/proc/UpdateButtonIcon(status_only = FALSE, force = FALSE) if(!button) return FALSE @@ -132,8 +146,13 @@ if(button.icon_state != background_icon_state) button.icon_state = background_icon_state ApplyIcon(button, force) + if(cooldown_timer_end) + if (cooldown_timer_end >= world.time) + update_cooldown_icon() + else + finish_cooldown() if(!IsAvailable()) - button.color = has_cooldown_timer ? rgb(219, 219, 219, 255) : transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0) + button.color = cooldown_timer_end ? rgb(219, 219, 219, 255) : transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0) else button.color = rgb(255,255,255,255) return TRUE @@ -143,11 +162,53 @@ current_button.cut_overlays() current_button.add_overlay(mutable_appearance(icon_icon, button_icon_state)) current_button.button_icon_state = button_icon_state + update_cooldown_icon(TRUE) /datum/action/proc/OnUpdatedIcon() SIGNAL_HANDLER UpdateButtonIcon() +//===Timer animation=== + +/datum/action/proc/set_cooldown(duration) + if(!button) + return + + cooldown_timer_end = world.time + duration + if (!timer_overlay) + timer_overlay = mutable_appearance(timer_icon, timer_icon_state_active) + timer_overlay.alpha = 180 + timer_overlay.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + timer_overlay.maptext_width = 64 + timer_overlay.maptext_height = 64 + timer_overlay.maptext_x = -8 + timer_overlay.maptext_y = -6 + + UpdateButtonIcon(TRUE, FALSE) + + if (!override_cooldown_behaviour) + START_PROCESSING(SSprocessing, src) + +/datum/action/proc/update_cooldown_icon(force = FALSE) + if(!button || !timer_overlay) + return + var/new_maptext = "
[FLOOR((cooldown_timer_end - world.time)/10, 1)]
" + if (new_maptext != timer_overlay.maptext || force) + button.cut_overlay(timer_overlay) + timer_overlay.maptext = new_maptext + button.add_overlay(timer_overlay) + +/datum/action/proc/finish_cooldown() + if(!button || !cooldown_timer_end) + return + button.cut_overlay(timer_overlay) + QDEL_NULL(timer_overlay) + cooldown_timer_end = null + UpdateButtonIcon(TRUE, FALSE) + + if (!override_cooldown_behaviour) + STOP_PROCESSING(SSprocessing, src) + //Presets for item actions /datum/action/item_action check_flags = AB_CHECK_HANDS_BLOCKED|AB_CHECK_INCAPACITATED|AB_CHECK_CONSCIOUS @@ -189,6 +250,7 @@ I.plane = FLOAT_PLANE //^ what that guy said current_button.cut_overlays() current_button.add_overlay(I) + update_cooldown_icon(TRUE) I.layer = old_layer I.plane = old_plane current_button.appearance_cache = I.appearance @@ -587,6 +649,7 @@ /datum/action/spell_action check_flags = NONE background_icon_state = "bg_spell" + override_cooldown_behaviour = TRUE /datum/action/spell_action/New(Target) ..() @@ -659,51 +722,6 @@ /datum/action/innate/proc/Deactivate() return -//Preset for an action with a cooldown - -/datum/action/cooldown - check_flags = NONE - transparent_when_unavailable = FALSE - var/cooldown_time = 0 - var/next_use_time = 0 - -/datum/action/cooldown/New() - ..() - button.maptext = "" - button.maptext_x = 8 - button.maptext_y = 0 - button.maptext_width = 24 - button.maptext_height = 12 - -/datum/action/cooldown/IsAvailable() - return next_use_time <= world.time - -/datum/action/cooldown/proc/StartCooldown() - next_use_time = world.time + cooldown_time - button.maptext = MAPTEXT("[round(cooldown_time/10, 0.1)]") - UpdateButtonIcon() - START_PROCESSING(SSfastprocess, src) - -/datum/action/cooldown/process() - if(!owner) - button.maptext = "" - return PROCESS_KILL - var/timeleft = max(next_use_time - world.time, 0) - if(timeleft == 0) - button.maptext = "" - UpdateButtonIcon() - return PROCESS_KILL - else - button.maptext = MAPTEXT("[round(timeleft/10, 0.1)]") - -/datum/action/cooldown/Grant(mob/M) - ..() - if(owner) - UpdateButtonIcon() - if(next_use_time > world.time) - START_PROCESSING(SSfastprocess, src) - - //Stickmemes /datum/action/item_action/stickmen name = "Summon Stick Minions" @@ -816,6 +834,7 @@ target.plane = FLOAT_PLANE //^ what that guy said current_button.cut_overlays() current_button.add_overlay(target) + update_cooldown_icon(TRUE) target.layer = old_layer target.plane = old_plane current_button.appearance_cache = target.appearance diff --git a/code/modules/mob/living/carbon/human/species_types/psyphoza.dm b/code/modules/mob/living/carbon/human/species_types/psyphoza.dm index 5c60a5e976f13..82557ea6ffcea 100644 --- a/code/modules/mob/living/carbon/human/species_types/psyphoza.dm +++ b/code/modules/mob/living/carbon/human/species_types/psyphoza.dm @@ -190,17 +190,9 @@ if(!(locate(/datum/action/change_psychic_auto) in owner.actions)) auto_action = new(src) auto_action.Grant(M) - ///Start auto timer - addtimer(CALLBACK(src, PROC_REF(auto_sense)), auto_cooldown) - -/datum/action/item_action/organ_action/psychic_highlight/IsAvailable() - if(has_cooldown_timer) - return FALSE - return ..() /datum/action/item_action/organ_action/psychic_highlight/Trigger() - . = ..() - if(has_cooldown_timer || !owner || !check_head()) + if(!..() || !owner || !check_head()) return //Reveal larger area of sense dim_overlay() @@ -209,14 +201,8 @@ if(BS) for(var/mob/living/L in urange(9, owner, 1)) BS.highlight_object(L, "mob", L.dir) - has_cooldown_timer = TRUE - UpdateButtonIcon() - addtimer(CALLBACK(src, PROC_REF(finish_cooldown)), cooldown + sense_time) - -/datum/action/item_action/organ_action/psychic_highlight/UpdateButtonIcon(status_only = FALSE, force = FALSE) - . = ..() - if(!IsAvailable()) - button.color = transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0) //Overwrite this line from the original to support my fucked up use + set_cooldown(cooldown + sense_time) + return TRUE /datum/action/item_action/organ_action/psychic_highlight/proc/remove() owner?.clear_fullscreen("psychic_highlight") @@ -231,14 +217,11 @@ if(!QDELETED(auto_action)) qdel(auto_action) -/datum/action/item_action/organ_action/psychic_highlight/proc/auto_sense() +/datum/action/item_action/organ_action/psychic_highlight/finish_cooldown() + . = ..() + // Auto-retrigger if(auto_sense) Trigger() - addtimer(CALLBACK(src, PROC_REF(auto_sense)), auto_cooldown) - -/datum/action/item_action/organ_action/psychic_highlight/proc/finish_cooldown() - has_cooldown_timer = FALSE - UpdateButtonIcon() //Allows user to see images through walls - mostly for if this action is added to something without xray /datum/action/item_action/organ_action/psychic_highlight/proc/toggle_eyes_fowards() @@ -464,6 +447,7 @@ /datum/action/change_psychic_auto/Trigger() . = ..() psychic_action?.auto_sense = !psychic_action?.auto_sense + psychic_action?.Trigger() UpdateButtonIcon() /datum/action/change_psychic_auto/IsAvailable() diff --git a/code/modules/mob/living/simple_animal/hostile/goose.dm b/code/modules/mob/living/simple_animal/hostile/goose.dm index c44136d588143..0b35345df6a23 100644 --- a/code/modules/mob/living/simple_animal/hostile/goose.dm +++ b/code/modules/mob/living/simple_animal/hostile/goose.dm @@ -57,7 +57,7 @@ var/vomiting = FALSE var/vomitCoefficient = 1 var/vomitTimeBonus = 0 - var/datum/action/cooldown/vomit/goosevomit + var/datum/action/vomit/goosevomit /mob/living/simple_animal/hostile/retaliate/goose/vomit/Initialize(mapload) . = ..() @@ -178,14 +178,13 @@ if (tasty) feed(tasty) -/datum/action/cooldown/vomit +/datum/action/vomit name = "Vomit" check_flags = AB_CHECK_CONSCIOUS button_icon_state = "vomit" icon_icon = 'icons/mob/animal.dmi' - cooldown_time = 250 -/datum/action/cooldown/vomit/Trigger() +/datum/action/vomit/Trigger() if(!..()) return FALSE if(!istype(owner, /mob/living/simple_animal/hostile/retaliate/goose/vomit)) @@ -195,6 +194,7 @@ vomit.vomit_prestart(vomit.vomitTimeBonus + 25) vomit.vomitCoefficient = 1 vomit.vomitTimeBonus = 0 + set_cooldown(25 SECONDS) return TRUE #undef GOOSE_SATIATED diff --git a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm index eba7425a5995a..702656c3e4469 100644 --- a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm +++ b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm @@ -72,7 +72,7 @@ /// Whether space dragon is swallowing a body currently var/is_swallowing = FALSE /// The cooldown ability to use wing gust - var/datum/action/cooldown/gust_attack/gust + var/datum/action/gust_attack/gust /// The ability to make your sprite smaller var/datum/action/small_sprite/space_dragon/small_sprite /// The color of the space dragon. @@ -456,15 +456,14 @@ var/link = FOLLOW_LINK(S, src) to_chat(S, "[link] [rendered]") -/datum/action/cooldown/gust_attack +/datum/action/gust_attack name = "Gust Attack" desc = "Use your wings to knock back foes with gusts of air, pushing them away and stunning them. Using this too often will leave you vulnerable for longer periods of time." background_icon_state = "bg_default" icon_icon = 'icons/hud/actions/actions_space_dragon.dmi' button_icon_state = "gust_attack" - cooldown_time = 5 SECONDS // the ability takes up around 2-3 seconds -/datum/action/cooldown/gust_attack/Trigger() +/datum/action/gust_attack/Trigger() if(!..() || !istype(owner, /mob/living/simple_animal/hostile/space_dragon)) return FALSE var/mob/living/simple_animal/hostile/space_dragon/S = owner @@ -474,7 +473,7 @@ S.icon_state = "spacedragon_gust" S.update_dragon_overlay() S.useGust(TRUE) - StartCooldown() + set_cooldown(5 SECONDS) return TRUE #undef DARKNESS_THRESHOLD diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm index e1e7b7c15f154..ea2ce4bb7e084 100644 --- a/code/modules/ninja/energy_katana.dm +++ b/code/modules/ninja/energy_katana.dm @@ -39,6 +39,16 @@ /obj/item/energy_katana/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() + if (ishuman(user)) + var/mob/living/carbon/human/human_user = user + if(istype(human_user.wear_suit, /obj/item/clothing/suit/space/space_ninja)) + var/obj/item/clothing/suit/space/space_ninja/ninja_suit = human_user.wear_suit + if (ninja_suit.stealth) + ninja_suit.cancel_stealth() + else + return + else + return if(dash_toggled) jaunt.Teleport(user, target) if(proximity_flag && (isobj(target) || issilicon(target))) diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm index 1c3fbd814701d..d32264e4df67e 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm @@ -6,6 +6,7 @@ Contents: */ +#define STEALTH_COOLDOWN 30 SECONDS /obj/item/clothing/suit/space/space_ninja/proc/toggle_stealth() var/mob/living/carbon/human/U = affecting @@ -14,6 +15,10 @@ Contents: if(stealth) cancel_stealth() else + var/datum/action/item_action/ninja_stealth/stealth_action = locate() in actions + if (!stealth_action.IsAvailable()) + U.balloon_alert(U, "Stealth not ready.") + return if(cell.charge <= 0) to_chat(U, "You don't have enough power to enable Stealth!") return @@ -32,12 +37,15 @@ Contents: animate(U, alpha = 255, time = 15) U.visible_message("[U.name] appears from thin air!", \ "You are now visible.") + var/datum/action/item_action/ninja_stealth/stealth_action = locate() in actions + stealth_action.set_cooldown(STEALTH_COOLDOWN) return 1 return 0 - /obj/item/clothing/suit/space/space_ninja/proc/stealth() if(!s_busy) toggle_stealth() else to_chat(affecting, "Stealth does not appear to work!") + +#undef STEALTH_COOLDOWN diff --git a/code/modules/spells/__DEFINES/spell.dm b/code/modules/spells/__DEFINES/spell.dm index 0764f970e3f89..3242e35b64c73 100644 --- a/code/modules/spells/__DEFINES/spell.dm +++ b/code/modules/spells/__DEFINES/spell.dm @@ -139,12 +139,6 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th var/overlay_icon_state = "spell" var/overlay_lifespan = 0 - var/mutable_appearance/timer_overlay - var/mutable_appearance/text_overlay - var/timer_overlay_active = FALSE - var/timer_icon = 'icons/effects/cooldown.dmi' - var/timer_icon_state_active = "second" - var/sparks_spread = 0 var/sparks_amt = 0 //cropped at 10 var/smoke_spread = 0 //1 - harmless, 2 - harmful @@ -299,7 +293,6 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th charge_counter = charge_max /obj/effect/proc_holder/spell/Destroy() - end_timer_animation() qdel(action) return ..() @@ -316,22 +309,23 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th /obj/effect/proc_holder/spell/proc/start_recharge() recharging = TRUE - begin_timer_animation() + action.set_cooldown(charge_max) + START_PROCESSING(SSfastprocess, src) /obj/effect/proc_holder/spell/process(delta_time) if(recharging && charge_type == "recharge" && (charge_counter < charge_max)) charge_counter += delta_time * 10 - update_timer_animation() + action.set_cooldown(charge_max - charge_counter) if(charge_counter >= charge_max) - end_timer_animation() - action.UpdateButtonIcon() + action.finish_cooldown() charge_counter = charge_max recharging = FALSE + return PROCESS_KILL else - end_timer_animation() - action.UpdateButtonIcon() + action.finish_cooldown() charge_counter = charge_max recharging = FALSE + return PROCESS_KILL /obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = TRUE, mob/user = usr) //if recharge is started is important for the trigger spells if(!cast_check()) @@ -402,9 +396,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th charge_counter++ if("holdervar") adjust_var(user, holder_var_type, -holder_var_amount) - end_timer_animation() - if(action) - action.UpdateButtonIcon() + action.finish_cooldown() /obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types if (!istype(target)) @@ -612,60 +604,6 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th return FALSE return TRUE -//===Timer animation=== - -/obj/effect/proc_holder/spell/update_icon() - . = ..() - if(timer_overlay_active && !recharging) - end_timer_animation() - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/begin_timer_animation() - if(!(action?.button) || timer_overlay_active) - return - - timer_overlay_active = TRUE - timer_overlay = mutable_appearance(timer_icon, timer_icon_state_active) - timer_overlay.alpha = 180 - - if(!text_overlay) - text_overlay = image(loc = action.button) - text_overlay.maptext_width = 64 - text_overlay.maptext_height = 64 - text_overlay.maptext_x = -8 - text_overlay.maptext_y = -6 - text_overlay.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - - if(action.owner?.client) - action.owner.client.images += text_overlay - - action.button.add_overlay(timer_overlay) - action.has_cooldown_timer = TRUE - update_timer_animation() - - START_PROCESSING(SSfastprocess, src) - -/obj/effect/proc_holder/spell/proc/update_timer_animation() - //Update map text (todo) - if(!(action?.button)) - return - text_overlay.maptext = "
[FLOOR((charge_max-charge_counter)/10, 1)]
" - -/obj/effect/proc_holder/spell/proc/end_timer_animation() - if(!(action?.button) || !timer_overlay_active) - return - timer_overlay_active = FALSE - if(action.owner?.client) - action.owner.client.images -= text_overlay - action.button.cut_overlays(timer_overlay) - timer_overlay = null - qdel(text_overlay) - text_overlay = null - action.has_cooldown_timer = FALSE - - STOP_PROCESSING(SSfastprocess, src) - //===================== /obj/effect/proc_holder/spell/self //Targets only the caster. Good for buffs and heals, but probably not wise for fireballs (although they usually fireball themselves anyway, honke)