mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-25 00:22:39 +00:00
* Lightgeist AI (#77287) ## About The Pull Request The lightgeist AI controller was marked as "these aren't intended to exist outside of player control" but this is actually untrue. Lightgiests can appear under AI control either from a rare vent clog event or in the "patch of eden" lavaland ruin. As they heal things by "attacking" them, I made it so that they will target wounded creatures to "attack" them, though they will only heal them quite slowly, significantly less efficiently or effectively than a medibot (for example). While making this change I also added a couple more parameters to their "healing hands" component, now they can no longer repair beepsky or heal cyborg limbs. It's fleshies only. Lightgeists will attempt to heal _anything_ they can see which is both injured and has healable damage, which is notable because while this makes the "Patch of Eden" ruin a nice place of respite for a wounded miner you should be careful to let them finish up before you leave. If they follow you out, they will attempt to heal any of the fauna that you are attacking. Worse still, most Lavaland mobs are not signatories of the Geneva convention and have no compunctions against killing field medics. The majority of listed file changes in this PR is that I made some attempt at splitting our massive list of blackboard keys across several files, in order to cause myself a headache based on which of my (or other people's) open basic mob AI PRs gets merged first. Also I fixed a bug where the goliath attack forecast would runtime if it killed a mob which qdels itself on death (guess how I found that out). ## Why It's Good For The Game Adds a bit more character to a lavaland area and rare event. ## Changelog 🆑 add: Lightgeists under AI control will selflessly heal any wounded creature that they see. balance: Lightgeists can no longer repair non-organic tissue. /🆑 * Lightgeist AI --------- Co-authored-by: Jacquerel <hnevard@gmail.com>
83 lines
3.0 KiB
Plaintext
83 lines
3.0 KiB
Plaintext
/**
|
|
* Delays outgoing attacks which are directed at mobs to give players time to get out of the way
|
|
*/
|
|
/datum/component/basic_mob_attack_telegraph
|
|
/// Time to wait before attack can complete
|
|
var/telegraph_duration
|
|
/// Overlay which we display over targets
|
|
var/mutable_appearance/target_overlay
|
|
/// Our current target, if we have one
|
|
var/mob/living/current_target
|
|
/// Callback executed when we start aiming at something
|
|
var/datum/callback/on_began_forecast
|
|
|
|
/datum/component/basic_mob_attack_telegraph/Initialize(
|
|
telegraph_icon = 'icons/mob/telegraphing/telegraph.dmi',
|
|
telegraph_state = ATTACK_EFFECT_BITE,
|
|
telegraph_duration = 0.3 SECONDS,
|
|
datum/callback/on_began_forecast,
|
|
)
|
|
. = ..()
|
|
if (!isbasicmob(parent))
|
|
return ELEMENT_INCOMPATIBLE
|
|
|
|
target_overlay = mutable_appearance(telegraph_icon, telegraph_state)
|
|
src.telegraph_duration = telegraph_duration
|
|
src.on_began_forecast = on_began_forecast
|
|
|
|
/datum/component/basic_mob_attack_telegraph/RegisterWithParent()
|
|
. = ..()
|
|
RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_attack))
|
|
|
|
/datum/component/basic_mob_attack_telegraph/UnregisterFromParent()
|
|
if (current_target)
|
|
forget_target(current_target)
|
|
QDEL_NULL(target_overlay)
|
|
REMOVE_TRAIT(parent, TRAIT_BASIC_ATTACK_FORECAST, REF(src))
|
|
UnregisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET)
|
|
return ..()
|
|
|
|
/// When we attempt to attack, check if it is allowed
|
|
/datum/component/basic_mob_attack_telegraph/proc/on_attack(mob/living/basic/source, atom/target)
|
|
SIGNAL_HANDLER
|
|
if (!isliving(target))
|
|
return
|
|
if (HAS_TRAIT_FROM(source, TRAIT_BASIC_ATTACK_FORECAST, REF(src)))
|
|
REMOVE_TRAIT(source, TRAIT_BASIC_ATTACK_FORECAST, REF(src))
|
|
return
|
|
|
|
if (!DOING_INTERACTION(source, INTERACTION_BASIC_ATTACK_FORCEAST))
|
|
INVOKE_ASYNC(src, PROC_REF(delayed_attack), source, target)
|
|
return COMPONENT_HOSTILE_NO_ATTACK
|
|
|
|
/// Perform an attack after a delay
|
|
/datum/component/basic_mob_attack_telegraph/proc/delayed_attack(mob/living/basic/source, mob/living/target)
|
|
current_target = target
|
|
target.add_overlay(target_overlay)
|
|
RegisterSignal(target, COMSIG_QDELETING, PROC_REF(forget_target))
|
|
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(target_moved))
|
|
|
|
on_began_forecast?.Invoke(target)
|
|
if (!do_after(source, delay = telegraph_duration, target = source, interaction_key = INTERACTION_BASIC_ATTACK_FORCEAST))
|
|
forget_target(target)
|
|
return
|
|
if (isnull(target)) // They got out of the way :(
|
|
return
|
|
ADD_TRAIT(source, TRAIT_BASIC_ATTACK_FORECAST, REF(src))
|
|
forget_target(target)
|
|
source.melee_attack(target)
|
|
|
|
/// The guy we're trying to attack moved, is he still in range?
|
|
/datum/component/basic_mob_attack_telegraph/proc/target_moved(mob/living/target)
|
|
SIGNAL_HANDLER
|
|
if (in_range(parent, target))
|
|
return
|
|
forget_target(target)
|
|
|
|
/// The guy we're trying to attack isn't a valid target any more
|
|
/datum/component/basic_mob_attack_telegraph/proc/forget_target(atom/target)
|
|
SIGNAL_HANDLER
|
|
current_target = null
|
|
target.cut_overlay(target_overlay)
|
|
UnregisterSignal(target, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED))
|