Datumizes spells (#24242)

* datumized spells

* finished

* last changes

* conflict

* Update code/datums/spells/alien_spells/transfer_plasma.dm

* conflicts

* shitty runtime fix until i get to this tomorrow

* Update code/datums/spell.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spell_handler/alien_spell_handler.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/alien_spells/regurgitate.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/alien_spells/regurgitate.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/bloodcrawl.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/bloodcrawl.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/modules/awaymissions/mission_code/ruins/wizardcrash.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/modules/research/xenobiology/xenobiology.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/modules/mob/living/carbon/superheroes.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/conjure.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/ethereal_jaunt.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/emplosion.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/turf_teleport.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/wizard_spells.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/wizard_spells.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/game/dna/mutations/mutation_powers.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/wizard_spells.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/datums/spells/wizard_spells.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/game/dna/mutations/mutation_powers.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* Update code/game/gamemodes/miniantags/revenant/revenant_abilities.dm

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>

* guess who just rework the entire malf ai actionsf ai

* gc better

---------

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
This commit is contained in:
GDN
2024-04-01 07:42:21 +00:00
committed by GitHub
co-authored by Burzah
parent d52535a041
commit eee8878024
149 changed files with 1358 additions and 1536 deletions
+6 -6
View File
@@ -265,7 +265,7 @@
name = "Hallucinations"
desc = "Gives hallucinations to a target at range. A silent and invisible spell."
button_icon_state = "horror"
var/obj/effect/proc_holder/horror/PH
var/datum/spell/horror/PH
charges = 4
/datum/action/innate/cult/blood_spell/horror/New()
@@ -274,7 +274,7 @@
..()
/datum/action/innate/cult/blood_spell/horror/Destroy()
var/obj/effect/proc_holder/horror/destroy = PH
var/datum/spell/horror/destroy = PH
. = ..()
if(!QDELETED(destroy))
QDEL_NULL(destroy)
@@ -283,24 +283,24 @@
PH.toggle(owner) //the important bit
return TRUE
/obj/effect/proc_holder/horror
/datum/spell/horror
active = FALSE
ranged_mousepointer = 'icons/effects/cult_target.dmi'
var/datum/action/innate/cult/blood_spell/attached_action
/obj/effect/proc_holder/horror/Destroy()
/datum/spell/horror/Destroy()
var/datum/action/innate/cult/blood_spell/AA = attached_action
. = ..()
if(!QDELETED(AA))
QDEL_NULL(AA)
/obj/effect/proc_holder/horror/proc/toggle(mob/user)
/datum/spell/horror/proc/toggle(mob/user)
if(active)
remove_ranged_ability(user, "<span class='cult'>You dispel the magic...</span>")
else
add_ranged_ability(user, "<span class='cult'>You prepare to horrify a target...</span>")
/obj/effect/proc_holder/horror/InterceptClickOn(mob/living/user, params, atom/target)
/datum/spell/horror/InterceptClickOn(mob/living/user, params, atom/target)
if(..())
return
if(ranged_ability_user.incapacitated() || !IS_CULTIST(user))
+240 -292
View File
@@ -4,117 +4,91 @@
// crit percent
#define MALF_AI_ROLL_CRIT_CHANCE 5
//The malf AI action subtype. All malf actions are subtypes of this.
/datum/action/innate/ai
name = "AI Action"
//The malf AI spell subtype. All malf actions are subtypes of this.
/datum/spell/ai_spell
name = "AI Spell"
desc = "You aren't entirely sure what this does, but it's very beepy and boopy."
background_icon_state = "bg_tech_blue"
var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time
action_background_icon_state = "bg_tech_blue"
clothes_req = FALSE
base_cooldown = 0
var/uses //If we have multiple uses of the same power
var/auto_use_uses = TRUE //If we automatically use up uses on each activation
var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules
/datum/action/innate/ai/Grant(mob/living/L)
. = ..()
if(!isAI(owner))
WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!")
qdel(src)
else
owner_AI = owner
/datum/spell/ai_spell/create_new_targeting()
return new /datum/spell_targeting/self
/datum/action/innate/ai/IsAvailable()
/datum/spell/ai_spell/can_cast(mob/living/silicon/ai/user, charge_check, show_message)
. = ..()
if(owner_AI && owner_AI.malf_cooldown > world.time)
if(!.)
return
if(!istype(user))
stack_trace("A non ai ([user]) tried to cast an AI spell.")
user.RemoveSpell(src)
return FALSE
/datum/action/innate/ai/Trigger(left_click)
/datum/spell/ai_spell/after_cast(list/targets, mob/user)
. = ..()
if(auto_use_uses)
adjust_uses(-1)
if(cooldown_period)
owner_AI.malf_cooldown = world.time + cooldown_period
adjust_uses(-1, user)
/datum/action/innate/ai/proc/adjust_uses(amt, silent)
/datum/spell/ai_spell/proc/adjust_uses(amt, mob/living/silicon/ai/owner, silent)
uses += amt
if(!silent && uses)
to_chat(owner, "<span class='notice'>[name] now has <b>[uses]</b> use[uses > 1 ? "s" : ""] remaining.</span>")
if(!uses)
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "<span class='warning'>[name] has run out of uses!</span>")
qdel(src)
owner.RemoveSpell(src)
if(QDELETED(src) || uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
return
desc = "[initial(desc)] It has [uses] use\s remaining."
UpdateButtons()
//Framework for ranged abilities that can have different effects by left-clicking stuff.
/datum/action/innate/ai/ranged
/datum/spell/ai_spell/ranged
name = "Ranged AI Action"
auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses
var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code
var/linked_ability_type //The path of our linked ability
selection_activated_message = "<span class='notice'>Hello World!</span>"
selection_deactivated_message = "<span class='danger'>Goodbye Cruel World!</span>"
/datum/action/innate/ai/ranged/New()
if(!linked_ability_type)
WARNING("Ranged AI action [name] attempted to spawn without a linked ability!")
qdel(src) //uh oh!
return
linked_ability = new linked_ability_type()
linked_ability.attached_action = src
..()
/datum/action/innate/ai/ranged/adjust_uses(amt, silent)
/datum/spell/ai_spell/ranged/adjust_uses(amt, mob/living/silicon/ai/owner, silent)
uses += amt
if(!silent && uses)
to_chat(owner, "<span class='notice'>[name] now has <b>[uses]</b> use[uses > 1 ? "s" : ""] remaining.</span>")
if(!uses)
if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
to_chat(owner, "<span class='warning'>[name] has run out of uses!</span>")
Remove(owner)
QDEL_IN(src, 100) //let any active timers on us finish up
owner.mob_spell_list -= src
QDEL_IN(src, 10 SECONDS) //let any active timers on us finish up
/datum/action/innate/ai/ranged/Destroy()
QDEL_NULL(linked_ability)
return ..()
/datum/spell/ai_spell/ranged/create_new_targeting()
var/datum/spell_targeting/clicked_atom/external/C = new()
C.range = INFINITY
return C
/datum/action/innate/ai/ranged/Activate()
linked_ability.toggle(owner)
return TRUE
//The actual ranged proc holder.
/obj/effect/proc_holder/ranged_ai
var/enable_text = "<span class='notice'>Hello World!</span>" //Appears when the user activates the ability
var/disable_text = "<span class='danger'>Goodbye Cruel World!</span>" //Context clues!
var/datum/action/innate/ai/ranged/attached_action
/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user)
if(active)
remove_ranged_ability(user, disable_text)
else
add_ranged_ability(user, enable_text)
/datum/action/innate/ai/choose_modules
/datum/spell/ai_spell/choose_modules
name = "Choose Modules"
desc = "Spend your processing time to gain a variety of different abilities."
button_icon_state = "choose_module"
action_icon_state = "choose_module"
auto_use_uses = FALSE // This is an infinite ability.
create_attack_logs = FALSE
/datum/action/innate/ai/choose_modules/Trigger(left_click)
/datum/spell/ai_spell/choose_modules/cast(list/targets, mob/living/silicon/ai/user)
. = ..()
owner_AI.malf_picker.use(owner_AI)
user.malf_picker.use(user)
/datum/action/innate/ai/return_to_core
/datum/spell/ai_spell/return_to_core
name = "Return to Main Core"
desc = "Leave the APC you are shunted to, and return to your core."
icon_icon = 'icons/obj/power.dmi'
button_icon_state = "apcemag"
action_icon = 'icons/obj/power.dmi'
action_icon_state = "apcemag"
auto_use_uses = FALSE // Here just to prevent the "You have X uses remaining" from popping up.
/datum/action/innate/ai/return_to_core/Trigger(left_click)
/datum/spell/ai_spell/return_to_core/cast(list/targets, mob/living/silicon/ai/user)
. = ..()
var/obj/machinery/power/apc/apc = owner_AI.loc
var/obj/machinery/power/apc/apc = user.loc
if(!istype(apc)) // This shouldn't happen but here for safety.
to_chat(src, "<span class='notice'>You are already in your Main Core.</span>")
to_chat(user, "<span class='notice'>You are already in your Main Core.</span>")
return
apc.malfvacate()
qdel(src)
@@ -127,9 +101,9 @@
/datum/module_picker/New()
possible_modules = list()
for(var/type in typesof(/datum/AI_Module))
for(var/type in subtypesof(/datum/AI_Module))
var/datum/AI_Module/AM = new type
if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade)
if(AM.power_type || AM.upgrade)
possible_modules += AM
/datum/module_picker/proc/use(mob/user)
@@ -167,7 +141,7 @@
temp = "You cannot afford this module."
break
var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions
var/datum/spell/ai_spell/action = locate(AM.power_type) in A.mob_spell_list
// Give the power and take away the money.
if(AM.upgrade) //upgrade and upgrade() are separate, be careful!
@@ -178,8 +152,8 @@
else
if(AM.power_type)
if(!action) //Unlocking for the first time
var/datum/action/AC = new AM.power_type
AC.Grant(A)
var/datum/spell/ai_spell/AC = new AM.power_type
A.AddSpell(AC)
A.current_modules += new AM.type
temp = AM.description
if(AM.one_purchase)
@@ -207,7 +181,7 @@
var/description = ""
var/cost = 5
var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false.
var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade.
var/power_type = /datum/spell/ai_spell //If the module gives an active ability, use this. Mutually exclusive with upgrade.
var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type.
var/unlock_text = "<span class='notice'>Hello World!</span>" //Text shown when an ability is unlocked
var/unlock_sound //Sound played when an ability is unlocked
@@ -223,36 +197,39 @@
description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed."
cost = 130
one_purchase = TRUE
power_type = /datum/action/innate/ai/nuke_station
power_type = /datum/spell/ai_spell/nuke_station
unlock_text = "<span class='notice'>You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time.</span>"
unlock_sound = 'sound/items/timer.ogg'
/datum/action/innate/ai/nuke_station
/datum/spell/ai_spell/nuke_station
name = "Doomsday Device"
desc = "Activates the doomsday device. This is not reversible."
button_icon_state = "doomsday_device"
action_icon_state = "doomsday_device"
auto_use_uses = FALSE
var/in_use
/datum/action/innate/ai/nuke_station/Activate()
var/turf/T = get_turf(owner)
/datum/spell/ai_spell/nuke_station/cast(list/targets, mob/living/silicon/ai/user)
var/turf/T = get_turf(user)
if(!istype(T) || !is_station_level(T.z))
to_chat(owner, "<span class='warning'>You cannot activate the doomsday device while off-station!</span>")
to_chat(user, "<span class='warning'>You cannot activate the doomsday device while off-station!</span>")
return
if(tgui_alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", list("confirm = TRUE;", "confirm = FALSE;")) != "confirm = TRUE;")
if(tgui_alert(user, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", list("confirm = TRUE;", "confirm = FALSE;")) != "confirm = TRUE;")
return
if(active)
if(!istype(user) || QDELETED(user))
return
if(in_use)
return //prevent the AI from activating an already active doomsday
active = TRUE
set_us_up_the_bomb()
in_use = TRUE
set_us_up_the_bomb(user)
/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb()
to_chat(owner_AI, "<span class='notice'>Nuclear device armed.</span>")
/datum/spell/ai_spell/nuke_station/proc/set_us_up_the_bomb(mob/living/silicon/ai/user)
to_chat(user, "<span class='notice'>Nuclear device armed.</span>")
GLOB.major_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", 'sound/AI/aimalf.ogg')
SSsecurity_level.set_level(SEC_LEVEL_DELTA)
owner_AI.nuking = TRUE
var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI)
owner_AI.doomsday_device = DOOM
owner_AI.doomsday_device.start()
user.nuking = TRUE
var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(user)
user.doomsday_device = DOOM
user.doomsday_device.start()
for(var/obj/item/pinpointer/point in GLOB.pinpointer_list)
for(var/mob/living/silicon/ai/A in GLOB.ai_list)
if((A.stat != DEAD) && A.nuking)
@@ -352,17 +329,17 @@
opening all doors on the station."
cost = 30
one_purchase = TRUE
power_type = /datum/action/innate/ai/lockdown
power_type = /datum/spell/ai_spell/lockdown
unlock_text = "<span class='notice'>You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time.</span>"
/datum/action/innate/ai/lockdown
/datum/spell/ai_spell/lockdown
name = "Lockdown"
desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves."
button_icon_state = "lockdown"
action_icon_state = "lockdown"
uses = 1
/datum/action/innate/ai/lockdown/Activate()
to_chat(owner, "<span class='warning'>Lockdown Initiated. Network reset in 90 seconds.</span>")
/datum/spell/ai_spell/lockdown/cast(list/targets, mob/user)
to_chat(user, "<span class='warning'>Lockdown Initiated. Network reset in 90 seconds.</span>")
new /datum/event/door_runtime()
//Destroy RCDs: Detonates all non-cyborg RCDs on the station.
@@ -372,17 +349,17 @@
description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station."
cost = 25
one_purchase = TRUE
power_type = /datum/action/innate/ai/destroy_rcds
power_type = /datum/spell/ai_spell/destroy_rcds
unlock_text = "<span class='notice'>After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs.</span>"
/datum/action/innate/ai/destroy_rcds
/datum/spell/ai_spell/destroy_rcds
name = "Destroy RCDs"
desc = "Detonate all non-cyborg RCDs on the station."
button_icon_state = "detonate_rcds"
action_icon_state = "detonate_rcds"
uses = 1
cooldown_period = 10 SECONDS
base_cooldown = 10 SECONDS
/datum/action/innate/ai/destroy_rcds/Activate()
/datum/spell/ai_spell/destroy_rcds/cast(list/targets, mob/user)
for(var/obj/item/rcd/RCD in GLOB.rcd_list)
if(istype(RCD, /obj/item/rcd/borg)) //Ensures that cyborg RCDs are spared.
continue
@@ -390,8 +367,8 @@
if(is_level_reachable(RCD_turf.z))
RCD.detonate_pulse()
to_chat(owner, "<span class='danger'>RCD detonation pulse emitted.</span>")
owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, FALSE, use_reverb = FALSE)
to_chat(user, "<span class='danger'>RCD detonation pulse emitted.</span>")
user.playsound_local(user, 'sound/machines/twobeep.ogg', 50, FALSE, use_reverb = FALSE)
//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right?
/datum/AI_Module/mecha_domination
@@ -415,22 +392,22 @@
Anyone can check the fire alarm's interface and may be tipped off by its status."
one_purchase = TRUE
cost = 25
power_type = /datum/action/innate/ai/break_fire_alarms
power_type = /datum/spell/ai_spell/break_fire_alarms
unlock_text = "<span class='notice'>You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will.</span>"
/datum/action/innate/ai/break_fire_alarms
/datum/spell/ai_spell/break_fire_alarms
name = "Override Thermal Sensors"
desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless."
button_icon_state = "break_fire_alarms"
action_icon_state = "break_fire_alarms"
uses = 1
/datum/action/innate/ai/break_fire_alarms/Activate()
/datum/spell/ai_spell/break_fire_alarms/cast(list/targets, mob/user)
for(var/obj/machinery/firealarm/F in GLOB.machines)
if(!is_station_level(F.z))
continue
F.emagged = TRUE
to_chat(owner, "<span class='notice'>All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.</span>")
owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, FALSE, use_reverb = FALSE)
to_chat(user, "<span class='notice'>All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.</span>")
user.playsound_local(user, 'sound/machines/terminal_off.ogg', 50, FALSE, use_reverb = FALSE)
//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms.
/datum/AI_Module/break_air_alarms
@@ -440,23 +417,22 @@
Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality."
one_purchase = TRUE
cost = 50
power_type = /datum/action/innate/ai/break_air_alarms
power_type = /datum/spell/ai_spell/break_air_alarms
unlock_text = "<span class='notice'>You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard.</span>"
/datum/action/innate/ai/break_air_alarms
/datum/spell/ai_spell/break_air_alarms
name = "Override Air Alarm Safeties"
desc = "Enables the Flood setting on all air alarms."
button_icon_state = "break_air_alarms"
action_icon_state = "break_air_alarms"
uses = 1
/datum/action/innate/ai/break_air_alarms/Activate()
/datum/spell/ai_spell/break_air_alarms/cast(list/targets, mob/user)
for(var/obj/machinery/alarm/AA in GLOB.machines)
if(!is_station_level(AA.z))
continue
AA.emagged = TRUE
to_chat(owner, "<span class='notice'>All air alarm safeties on the station have been overridden. Air alarms may now use the Flood environmental mode.")
owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, FALSE, use_reverb = FALSE)
to_chat(user, "<span class='notice'>All air alarm safeties on the station have been overridden. Air alarms may now use the Flood environmental mode.")
user.playsound_local(user, 'sound/machines/terminal_off.ogg', 50, FALSE, use_reverb = FALSE)
//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase.
/datum/AI_Module/overload_machine
@@ -464,95 +440,78 @@
mod_pick_name = "overload"
description = "Overheats an electrical machine, causing a moderately-sized explosion and destroying it. Four uses per purchase."
cost = 20
power_type = /datum/action/innate/ai/ranged/overload_machine
power_type = /datum/spell/ai_spell/ranged/overload_machine
unlock_text = "<span class='notice'>You enable the ability for the station's APCs to direct intense energy into machinery.</span>"
/datum/action/innate/ai/ranged/overload_machine
/datum/spell/ai_spell/ranged/overload_machine
name = "Overload Machine"
desc = "Overheats a machine, causing a moderately-sized explosion after a short time."
button_icon_state = "overload_machine"
action_icon_state = "overload_machine"
uses = 4
linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine
active = FALSE
ranged_mousepointer = 'icons/effects/cult_target.dmi'
selection_activated_message = "<span class='notice'>You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel.</span>"
selection_deactivated_message = "<span class='notice'>You release your hold on the powernet.</span>"
/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M)
/datum/spell/ai_spell/ranged/overload_machine/cast(list/targets, mob/user)
var/obj/machinery/target = targets[1]
if(!istype(target))
to_chat(user, "<span class='warning'>You can only overload machines!</span>")
return
if(target.flags_2 & NO_MALF_EFFECT_2)
to_chat(user, "<span class='warning'>That machine can't be overloaded!</span>")
return
user.playsound_local(user, "sparks", 50, FALSE, use_reverb = FALSE)
adjust_uses(-1, user)
target.audible_message("<span class='italics'>You hear a loud electrical buzzing sound coming from [target]!</span>")
addtimer(CALLBACK(src, PROC_REF(detonate_machine), target), 5 SECONDS) //kaboom!
to_chat(user, "<span class='warning'>Overloading machine circuitry...</span>")
return TRUE
/datum/spell/ai_spell/ranged/overload_machine/proc/detonate_machine(obj/machinery/M)
if(M && !QDELETED(M))
explosion(get_turf(M), 0, 3, 5, 0)
if(M) //to check if the explosion killed it before we try to delete it
qdel(M)
/obj/effect/proc_holder/ranged_ai/overload_machine
active = FALSE
ranged_mousepointer = 'icons/effects/cult_target.dmi'
enable_text = "<span class='notice'>You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel.</span>"
disable_text = "<span class='notice'>You release your hold on the powernet.</span>"
/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target)
if(..())
return
if(ranged_ability_user.incapacitated())
remove_ranged_ability()
return
if(!istype(target))
to_chat(ranged_ability_user, "<span class='warning'>You can only overload machines!</span>")
return
if(target.flags_2 & NO_MALF_EFFECT_2)
to_chat(ranged_ability_user, "<span class='warning'>That machine can't be overloaded!</span>")
return
ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, FALSE, use_reverb = FALSE)
attached_action.adjust_uses(-1)
target.audible_message("<span class='italics'>You hear a loud electrical buzzing sound coming from [target]!</span>")
addtimer(CALLBACK(attached_action, TYPE_PROC_REF(/datum/action/innate/ai/ranged/overload_machine, detonate_machine), target), 50) //kaboom!
remove_ranged_ability(ranged_ability_user, "<span class='warning'>Overloading machine circuitry...</span>")
return TRUE
//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself.
/datum/AI_Module/override_machine
module_name = "Machine Override"
mod_pick_name = "override"
description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses."
cost = 30
power_type = /datum/action/innate/ai/ranged/override_machine
power_type = /datum/spell/ai_spell/ranged/override_machine
unlock_text = "<span class='notice'>You procure a virus from the Space Dark Web and distribute it to the station's machines.</span>"
/datum/action/innate/ai/ranged/override_machine
/datum/spell/ai_spell/ranged/override_machine
name = "Override Machine"
desc = "Animates a targeted machine, causing it to attack anyone nearby."
button_icon_state = "override_machine"
action_icon_state = "override_machine"
uses = 4
linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine
/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M)
if(M && !QDELETED(M))
new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1)
/obj/effect/proc_holder/ranged_ai/override_machine
active = FALSE
ranged_mousepointer = 'icons/effects/override_machine_target.dmi'
enable_text = "<span class='notice'>You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel.</span>"
disable_text = "<span class='notice'>You release your hold on the powernet.</span>"
selection_activated_message = "<span class='notice'>You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel.</span>"
selection_deactivated_message = "<span class='notice'>You release your hold on the powernet.</span>"
/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target)
if(..())
return
if(ranged_ability_user.incapacitated())
remove_ranged_ability()
return
/datum/spell/ai_spell/ranged/override_machine/cast(list/targets, mob/user)
var/obj/machinery/target = targets[1]
if(!istype(target))
to_chat(ranged_ability_user, "<span class='warning'>You can only animate machines!</span>")
to_chat(user, "<span class='warning'>You can only animate machines!</span>")
return
if(target.flags_2 & NO_MALF_EFFECT_2)
to_chat(ranged_ability_user, "<span class='warning'>That machine can't be overridden!</span>")
to_chat(user, "<span class='warning'>That machine can't be overridden!</span>")
return
ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, FALSE, use_reverb = FALSE)
attached_action.adjust_uses(-1)
user.playsound_local(user, 'sound/misc/interference.ogg', 50, FALSE, use_reverb = FALSE)
adjust_uses(-1, user)
target.audible_message("<span class='userdanger'>You hear a loud electrical buzzing sound coming from [target]!</span>")
addtimer(CALLBACK(attached_action, TYPE_PROC_REF(/datum/action/innate/ai/ranged/override_machine, animate_machine), target), 50) //kabeep!
remove_ranged_ability(ranged_ability_user, "<span class='danger'>Sending override signal...</span>")
addtimer(CALLBACK(src, PROC_REF(animate_machine), target), 5 SECONDS) //kabeep!
to_chat(user, "<span class='danger'>Sending override signal...</span>")
return TRUE
/datum/spell/ai_spell/ranged/override_machine/proc/animate_machine(obj/machinery/M, mob/user)
if(M && !QDELETED(M))
new /mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, user, 1)
//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting.
/datum/AI_Module/place_cyborg_transformer
@@ -561,46 +520,47 @@
description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside."
cost = 100
one_purchase = TRUE
power_type = /datum/action/innate/ai/place_transformer
power_type = /datum/spell/ai_spell/place_transformer
unlock_text = "<span class='notice'>You prepare a robotics factory for deployment.</span>"
unlock_sound = 'sound/machines/ping.ogg'
/datum/action/innate/ai/place_transformer
/datum/spell/ai_spell/place_transformer
name = "Place Robotics Factory"
desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!"
button_icon_state = "robotic_factory"
action_icon_state = "robotic_factory"
uses = 1
auto_use_uses = FALSE //So we can attempt multiple times
var/list/turfOverlays
var/in_use = FALSE
/datum/action/innate/ai/place_transformer/New()
/datum/spell/ai_spell/place_transformer/New()
..()
for(var/i in 1 to 3)
var/image/I = image("icon"='icons/turf/overlays.dmi')
LAZYADD(turfOverlays, I)
/datum/action/innate/ai/place_transformer/Activate()
if(!owner_AI.can_place_transformer(src))
/datum/spell/ai_spell/place_transformer/cast(list/targets, mob/living/silicon/ai/user)
if(!user.can_place_transformer(src))
return
active = TRUE
if(tgui_alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", list("Yes", "No")) != "Yes")
in_use = TRUE
if(tgui_alert(user, "Are you sure you want to place the machine here?", "Are you sure?", list("Yes", "No")) != "Yes")
active = FALSE
return
if(!owner_AI.can_place_transformer(src))
if(!user.can_place_transformer(src))
active = FALSE
return
var/turf/T = get_turf(owner_AI.eyeobj)
new /obj/machinery/transformer(T, owner_AI)
var/turf/T = get_turf(user.eyeobj)
new /obj/machinery/transformer(T, user)
playsound(T, 'sound/effects/phasein.ogg', 100, 1)
owner_AI.can_shunt = FALSE
to_chat(owner, "<span class='warning'>You are no longer able to shunt your core to APCs.</span>")
adjust_uses(-1)
user.can_shunt = FALSE
to_chat(user, "<span class='warning'>You are no longer able to shunt your core to APCs.</span>")
adjust_uses(-1, user)
/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T)
if(C && I.loc == T)
C.images -= I
/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action)
/mob/living/silicon/ai/proc/can_place_transformer(datum/spell/ai_spell/place_transformer/action)
if(!eyeobj || !isturf(loc) || incapacitated() || !action)
return
var/turf/middle = get_turf(eyeobj)
@@ -634,46 +594,46 @@
mod_pick_name = "turretdeployer"
description = "Build a turret anywhere that lethally targets organic life in sight."
cost = 30
power_type = /datum/action/innate/ai/place_turret
power_type = /datum/spell/ai_spell/place_turret
unlock_text = "<span class='notice'>You prepare an energy turret for deployment.</span>"
unlock_sound = 'sound/items/rped.ogg'
/datum/action/innate/ai/place_turret
/datum/spell/ai_spell/place_turret
name = "Deploy Turret"
desc = "Build a turret anywhere that lethally targets organic life in sight."
button_icon_state = "deploy_turret"
action_icon_state = "deploy_turret"
uses = 1
auto_use_uses = FALSE
var/image/turf_overlay
var/in_use = FALSE
/datum/action/innate/ai/place_turret/New()
/datum/spell/ai_spell/place_turret/New()
..()
turf_overlay = image('icons/turf/overlays.dmi')
/datum/action/innate/ai/place_turret/Activate()
if(active)
to_chat(owner, "<span class='notice'>Your assemblers can only construct one turret at a time.</span>")
/datum/spell/ai_spell/place_turret/cast(list/targets, mob/living/silicon/ai/user)
if(in_use)
to_chat(user, "<span class='notice'>Your assemblers can only construct one turret at a time.</span>")
return
if(!owner_AI.can_place_turret(src))
if(!user.can_place_turret(src))
return
active = TRUE
var/response = alert(owner, "Are you sure you want to place a turret here? Deployment will take a few seconds to complete, in which the turret will be vulnerable.", "Are you sure?", "No", "Yes")
if(!response || response == "No")
active = FALSE
in_use = TRUE
if(tgui_alert(user, "Are you sure you want to place a turret here? Deployment will take a few seconds to complete, in which the turret will be vulnerable.", "Are you sure?", list("No", "Yes")) != "Yes")
in_use = FALSE
return
if(!owner_AI.can_place_turret(src))
active = FALSE
if(!user.can_place_turret(src))
in_use = FALSE
return
deploy_turret()
active = FALSE
deploy_turret(user)
in_use = FALSE
/datum/action/innate/ai/place_turret/proc/deploy_turret()
var/turf/T = get_turf(owner_AI.eyeobj)
/datum/spell/ai_spell/place_turret/proc/deploy_turret(mob/living/silicon/ai/user)
var/turf/T = get_turf(user.eyeobj)
//Handles the turret construction and configuration
playsound(T, 'sound/items/rped.ogg', 100, TRUE) //Plays a sound both at the location of the construction to alert players and to the user as feedback
owner.playsound_local(owner, 'sound/items/rped.ogg', 50, FALSE, use_reverb = FALSE)
to_chat(owner, "<span class='notice'>You order your electronics to assemble a turret. This will take a few seconds.</span>")
user.playsound_local(user, 'sound/items/rped.ogg', 50, FALSE, use_reverb = FALSE)
to_chat(user, "<span class='notice'>You order your electronics to assemble a turret. This will take a few seconds.</span>")
var/obj/effect/temp_visual/rcd_effect/spawning_effect = new(T)
QDEL_IN(spawning_effect, 5 SECONDS)
@@ -687,21 +647,21 @@
turret.invisibility = 100
//If turrets are already upgraded, beef it up
if(owner_AI.turrets_upgraded)
if(user.turrets_upgraded)
turret.health += 30
turret.eprojectile = /obj/item/projectile/beam/laser/ai_turret/heavylaser //Big gun
turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg'
if(do_after_once(owner, 5 SECONDS, target = T, allow_moving = TRUE)) //Once this is done, turret is armed and dangerous
if(do_after_once(user, 5 SECONDS, target = T, allow_moving = TRUE)) //Once this is done, turret is armed and dangerous
turret.raised = initial(turret.raised)
turret.invisibility = initial(turret.invisibility)
turret.disabled = initial(turret.disabled)
new /obj/effect/temp_visual/rcd_effect/end(T)
playsound(T, 'sound/items/deconstruct.ogg', 100, TRUE)
to_chat(owner, "<span class='notice'>Turret deployed.</span>")
adjust_uses(-1)
to_chat(user, "<span class='notice'>Turret deployed.</span>")
adjust_uses(-1, user)
/mob/living/silicon/ai/proc/can_place_turret(datum/action/innate/ai/place_turret/action)
/mob/living/silicon/ai/proc/can_place_turret(datum/spell/ai_spell/place_turret/action)
if(!eyeobj || !isturf(eyeobj.loc) || incapacitated() || !action)
return
@@ -735,26 +695,26 @@
mod_pick_name = "blackout"
description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses."
cost = 15
power_type = /datum/action/innate/ai/blackout
power_type = /datum/spell/ai_spell/blackout
unlock_text = "<span class='notice'>You hook into the powernet and route bonus power towards the station's lighting.</span>"
/datum/action/innate/ai/blackout
/datum/spell/ai_spell/blackout
name = "Blackout"
desc = "Overloads random lights across the station."
button_icon_state = "blackout"
action_icon_state = "blackout"
uses = 3
auto_use_uses = FALSE
/datum/action/innate/ai/blackout/Activate()
/datum/spell/ai_spell/blackout/cast(list/targets, mob/user)
for(var/thing in GLOB.apcs)
var/obj/machinery/power/apc/apc = thing
if(prob(30 * apc.overload))
INVOKE_ASYNC(apc, TYPE_PROC_REF(/obj/machinery/power/apc, overload_lighting))
else
apc.overload++
to_chat(owner, "<span class='notice'>Overcurrent applied to the powernet.</span>")
owner.playsound_local(owner, "sparks", 50, FALSE, use_reverb = FALSE)
adjust_uses(-1)
to_chat(user, "<span class='notice'>Overcurrent applied to the powernet.</span>")
user.playsound_local(user, "sparks", 50, FALSE, use_reverb = FALSE)
adjust_uses(-1, user)
//Reactivate Camera Network: Reactivates up to 30 cameras across the station.
/datum/AI_Module/reactivate_cameras
@@ -762,19 +722,18 @@
mod_pick_name = "recam"
description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras."
cost = 10
power_type = /datum/action/innate/ai/reactivate_cameras
power_type = /datum/spell/ai_spell/reactivate_cameras
unlock_text = "<span class='notice'>You deploy nanomachines to the cameranet.</span>"
/datum/action/innate/ai/reactivate_cameras
/datum/spell/ai_spell/reactivate_cameras
name = "Reactivate Cameras"
desc = "Reactivates disabled cameras across the station; remaining uses can be used later."
button_icon_state = "reactivate_cameras"
action_icon_state = "reactivate_cameras"
uses = 10
auto_use_uses = FALSE
cooldown_period = 3 SECONDS
base_cooldown = 3 SECONDS
/datum/action/innate/ai/reactivate_cameras/Activate()
var/mob/living/silicon/ai/user = usr
/datum/spell/ai_spell/reactivate_cameras/cast(list/targets, mob/living/silicon/ai/user)
var/repaired_cameras = 0
if(!istype(user))
return
@@ -782,14 +741,14 @@
if(!uses)
break
if(!camera_to_repair.status || camera_to_repair.view_range != initial(camera_to_repair.view_range))
camera_to_repair.toggle_cam(owner_AI, 0)
camera_to_repair.toggle_cam(user, 0)
camera_to_repair.view_range = initial(camera_to_repair.view_range)
camera_to_repair.wires.cut_wires.Cut()
repaired_cameras++
uses--
to_chat(owner, "<span class='notice'>Diagnostic complete! Cameras reactivated: <b>[repaired_cameras]</b>. Reactivations remaining: <b>[uses]</b>.</span>")
owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, FALSE, use_reverb = FALSE)
adjust_uses(0, TRUE)
to_chat(user, "<span class='notice'>Diagnostic complete! Cameras reactivated: <b>[repaired_cameras]</b>. Reactivations remaining: <b>[uses]</b>.</span>")
user.playsound_local(user, 'sound/items/wirecutter.ogg', 50, FALSE, use_reverb = FALSE)
adjust_uses(0, user, TRUE)
//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision.
/datum/AI_Module/upgrade_cameras
@@ -880,124 +839,113 @@
mod_pick_name = "repair_borg"
description = "Causes an electrical surge in the targeted cyborg, rebooting and repairing most of its subsystems. Requires two uses on a cyborg with broken armor."
cost = 20
power_type = /datum/action/innate/ai/ranged/repair_cyborg
power_type = /datum/spell/ai_spell/ranged/repair_cyborg
unlock_text = "<span class='notice'>TLB exception on load: Error pointing to address 0000001H, Proceed with execution anywa- SURGE protocols installed, welcome to open APC!</span>"
unlock_sound = 'sound/items/rped.ogg'
/datum/action/innate/ai/ranged/repair_cyborg
/datum/spell/ai_spell/ranged/repair_cyborg
name = "Repair Cyborg"
desc = "Shocks a cyborg back to 'life' after a short delay."
button_icon_state = "overload_machine"
action_icon_state = "overload_machine"
uses = 2
linked_ability_type = /obj/effect/proc_holder/ranged_ai/repair_cyborg
ranged_mousepointer = 'icons/effects/overload_machine_target.dmi'
selection_activated_message = "<span class='notice'>Call to address 0FFFFFFF in APC logic thread, awaiting user response.</span>"
selection_deactivated_message = "<span class='notice'>APC logic thread restarting...</span>"
var/is_active = FALSE
/datum/spell/ai_spell/ranged/repair_cyborg/create_new_targeting()
var/datum/spell_targeting/click/T = new
T.allowed_type = /mob/living/silicon/robot
T.try_auto_target = FALSE
return T
/datum/action/innate/ai/ranged/repair_cyborg/proc/fix_borg(mob/living/silicon/robot/to_repair)
/datum/spell/ai_spell/ranged/repair_cyborg/cast(list/targets, mob/user)
var/mob/living/silicon/robot/robot_target = targets[1]
if(!istype(robot_target))
to_chat(user, "<span class='warning'>You can only repair robots with this ability!</span>")
return
if(is_active)
to_chat(user, "<span class='warning'>You can only repair one robot at a time!</span>")
return
is_active = TRUE
user.playsound_local(user, "sparks", 50, FALSE, use_reverb = FALSE)
adjust_uses(-1, user)
robot_target.audible_message("<span class='italics'>You hear a loud electrical buzzing sound coming from [robot_target]!</span>")
if(!do_mob(user, robot_target, 10 SECONDS))
is_active = FALSE
return
is_active = FALSE
fix_borg(robot_target)
to_chat(user, "<span class='warning'>[robot_target] successfully rebooted.</span>")
return TRUE
/datum/spell/ai_spell/ranged/repair_cyborg/proc/fix_borg(mob/living/silicon/robot/to_repair)
for(var/datum/robot_component/component in to_repair.components)
component.brute_damage = 0
component.electronics_damage = 0
component.component_disabled = FALSE
to_repair.revive()
/obj/effect/proc_holder/ranged_ai/repair_cyborg
active = FALSE
ranged_mousepointer = 'icons/effects/overload_machine_target.dmi'
enable_text = "<span class='notice'>Call to address 0FFFFFFF in APC logic thread, awaiting user response.</span>"
disable_text = "<span class='notice'>APC logic thread restarting...</span>"
var/is_active = FALSE
/obj/effect/proc_holder/ranged_ai/repair_cyborg/InterceptClickOn(mob/living/caller, params, mob/living/silicon/robot/robot_target)
if(..())
return
if(ranged_ability_user.incapacitated())
remove_ranged_ability()
return
if(!istype(robot_target))
to_chat(ranged_ability_user, "<span class='warning'>You can only repair robots with this ability!</span>")
return
if(is_active)
to_chat(ranged_ability_user, "<span class='warning'>You can only repair one robot at a time!</span>")
return
is_active = TRUE
ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, FALSE, use_reverb = FALSE)
var/datum/action/innate/ai/ranged/repair_cyborg/actual_action = attached_action
actual_action.adjust_uses(-1)
robot_target.audible_message("<span class='italics'>You hear a loud electrical buzzing sound coming from [robot_target]!</span>")
if(!do_mob(caller, robot_target, 10 SECONDS))
is_active = FALSE
return
is_active = FALSE
actual_action.fix_borg(robot_target)
remove_ranged_ability(ranged_ability_user, "<span class='warning'>[robot_target] successfully rebooted.</span>")
return TRUE
/datum/AI_Module/core_tilt
module_name = "Rolling Servos"
mod_pick_name = "watchforrollingcores"
description = "Allows you to slowly roll your core around, crushing anything in your path with your bulk."
cost = 10
one_purchase = FALSE
power_type = /datum/action/innate/ai/ranged/core_tilt
one_purchase = TRUE
power_type = /datum/spell/ai_spell/ranged/core_tilt
unlock_sound = 'sound/effects/bang.ogg'
unlock_text = "<span class='notice'>You gain the ability to roll over and crush anything in your way.</span>"
/datum/action/innate/ai/ranged/core_tilt
/datum/spell/ai_spell/ranged/core_tilt
name = "Roll Over"
button_icon_state = "roll_over"
action_icon_state = "roll_over"
desc = "Allows you to roll over in the direction of your choosing, crushing anything in your way."
auto_use_uses = FALSE
linked_ability_type = /obj/effect/proc_holder/ranged_ai/roll_over
/obj/effect/proc_holder/ranged_ai/roll_over
active = FALSE
ranged_mousepointer = 'icons/effects/cult_target.dmi'
enable_text = "<span class='notice'>Your inner servos shift as you prepare to roll around. Click adjacent tiles to roll into them!</span>"
disable_text = "<span class='notice'>You disengage your rolling protocols.</span>"
selection_activated_message = "<span class='notice'>Your inner servos shift as you prepare to roll around. Click adjacent tiles to roll into them!</span>"
selection_deactivated_message = "<span class='notice'>You disengage your rolling protocols.</span>"
COOLDOWN_DECLARE(time_til_next_tilt)
/// How long does it take us to roll?
var/roll_over_time = MALF_AI_ROLL_TIME
/// How long does it take for the ability to cool down, on top of [roll_over_time]?
var/roll_over_cooldown = MALF_AI_ROLL_COOLDOWN
/obj/effect/proc_holder/ranged_ai/roll_over/InterceptClickOn(mob/living/caller, params, atom/target_atom)
if(..())
/datum/spell/ai_spell/ranged/core_tilt/cast(list/targets, mob/living/silicon/ai/user)
var/atom/target_atom = targets[1]
if(!istype(user))
return
if(!isAI(ranged_ability_user))
return
if(ranged_ability_user.incapacitated() || !isturf(ranged_ability_user.loc))
remove_ranged_ability()
if(!isturf(user.loc))
user.RemoveSpell(src)
return
if(!COOLDOWN_FINISHED(src, time_til_next_tilt))
to_chat(ranged_ability_user, "<span class='warning'>Your rolling capacitors are still powering back up!</span>")
to_chat(user, "<span class='warning'>Your rolling capacitors are still powering back up!</span>")
return
var/turf/target = get_turf(target_atom)
if(isnull(target))
return
if(target == get_turf(ranged_ability_user))
to_chat(ranged_ability_user, "<span class='warning'>You can't roll over on yourself!</span>")
if(target == get_turf(user))
to_chat(user, "<span class='warning'>You can't roll over on yourself!</span>")
return
var/picked_dir = get_dir(caller, target)
var/picked_dir = get_dir(user, target)
if(!picked_dir)
return FALSE
// we can move during the timer so we cant just pass the ref
var/turf/temp_target = get_step(ranged_ability_user, picked_dir)
var/turf/temp_target = get_step(user, picked_dir)
new /obj/effect/temp_visual/single_user/ai_telegraph(temp_target, ranged_ability_user)
ranged_ability_user.visible_message("<span class='danger'>[ranged_ability_user] seems to be winding up!</span>")
addtimer(CALLBACK(src, PROC_REF(do_roll_over), caller, picked_dir), MALF_AI_ROLL_TIME)
new /obj/effect/temp_visual/single_user/ai_telegraph(temp_target, user)
user.visible_message("<span class='danger'>[user] seems to be winding up!</span>")
addtimer(CALLBACK(src, PROC_REF(do_roll_over), user, picked_dir), MALF_AI_ROLL_TIME)
to_chat(ranged_ability_user, "<span class='warning'>Overloading machine circuitry...</span>")
to_chat(user, "<span class='warning'>Overloading machine circuitry...</span>")
COOLDOWN_START(src, time_til_next_tilt, roll_over_cooldown)
return TRUE
/obj/effect/proc_holder/ranged_ai/roll_over/proc/do_roll_over(mob/living/silicon/ai/ai_caller, picked_dir)
/datum/spell/ai_spell/ranged/core_tilt/proc/do_roll_over(mob/living/silicon/ai/ai_caller, picked_dir)
var/turf/target = get_step(ai_caller, picked_dir) // in case we moved we pass the dir not the target turf
if(isnull(target) || ai_caller.incapacitated() || !isturf(ai_caller.loc))
@@ -1009,7 +957,7 @@
ai_caller.fall_and_crush(target, MALF_AI_ROLL_DAMAGE, prob(MALF_AI_ROLL_CRIT_CHANCE), 2, null, paralyze_time, crush_dir = picked_dir, angle = get_rotation_from_dir(picked_dir))
ai_caller.allow_teleporter = FALSE
/obj/effect/proc_holder/ranged_ai/roll_over/proc/get_rotation_from_dir(dir)
/datum/spell/ai_spell/ranged/core_tilt/proc/get_rotation_from_dir(dir)
switch(dir)
if(NORTH, NORTHWEST, WEST, SOUTHWEST)
return 270 // try our best to not return 180 since it works badly with animate
@@ -147,8 +147,8 @@
/mob/living/simple_animal/demon/shadow/Initialize(mapload)
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/fireball/shadow_grapple)
var/obj/effect/proc_holder/spell/bloodcrawl/shadow_crawl/S = new
AddSpell(new /datum/spell/fireball/shadow_grapple)
var/datum/spell/bloodcrawl/shadow_crawl/S = new
AddSpell(S)
whisper_action.button_icon_state = "shadow_whisper"
whisper_action.background_icon_state = "shadow_demon_bg"
@@ -176,7 +176,7 @@
return lum_count
/obj/effect/proc_holder/spell/fireball/shadow_grapple
/datum/spell/fireball/shadow_grapple
name = "Shadow Grapple"
desc = "Fire one of your hands, if it hits a person it pulls them in. If you hit a structure you get pulled to the structure. Any light source hit with this will be disabled in a two tile radius."
base_cooldown = 10 SECONDS
@@ -193,7 +193,7 @@
invocation_type = "none"
invocation = null
/obj/effect/proc_holder/spell/fireball/shadow_grapple/update_icon_state()
/datum/spell/fireball/shadow_grapple/update_spell_icon()
return
/obj/item/projectile/magic/shadow_hand
@@ -239,9 +239,9 @@
/obj/item/organ/internal/heart/demon/shadow/insert(mob/living/carbon/M, special = 0)
. = ..()
if(M.mind)
M.mind.AddSpell(new /obj/effect/proc_holder/spell/fireball/shadow_grapple)
M.mind.AddSpell(new /datum/spell/fireball/shadow_grapple)
/obj/item/organ/internal/heart/demon/shadow/remove(mob/living/carbon/M, special = 0)
. = ..()
if(M.mind)
M.mind.RemoveSpell(/obj/effect/proc_holder/spell/fireball/shadow_grapple)
M.mind.RemoveSpell(/datum/spell/fireball/shadow_grapple)
@@ -32,7 +32,7 @@
..()
remove_from_all_data_huds()
ADD_TRAIT(src, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat")
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
var/datum/spell/bloodcrawl/bloodspell = new
AddSpell(bloodspell)
if(istype(loc, /obj/effect/dummy/slaughter))
bloodspell.phased = TRUE
@@ -102,7 +102,7 @@
to emerge from it. You are fast, powerful, and almost invincible. By dragging a dead or unconscious body into a blood pool with you, you will consume it after a time and fully regain \
your health. You may use the ability 'Sense Victims' in your Cultist tab to locate a random, living heretic.</span></b>"
/obj/effect/proc_holder/spell/sense_victims
/datum/spell/sense_victims
name = "Sense Victims"
desc = "Sense the location of heretics"
base_cooldown = 0
@@ -113,13 +113,13 @@
action_background_icon_state = "bg_cult"
panel = "Demon"
/obj/effect/proc_holder/spell/sense_victims/create_new_targeting()
/datum/spell/sense_victims/create_new_targeting()
return new /datum/spell_targeting/alive_mob_list
/obj/effect/proc_holder/spell/sense_victims/valid_target(mob/living/target, user)
/datum/spell/sense_victims/valid_target(mob/living/target, user)
return target.stat == CONSCIOUS && target.key && !IS_CULTIST(target) // Only conscious, non cultist players
/obj/effect/proc_holder/spell/sense_victims/cast(list/targets, mob/user)
/datum/spell/sense_victims/cast(list/targets, mob/user)
var/mob/living/victim = targets[1]
to_chat(victim, "<span class='userdanger'>You feel an awful sense of being watched...</span>")
victim.Stun(6 SECONDS) //HUE
@@ -153,7 +153,7 @@
S.mind.special_role = "Harbinger of the Slaughter"
to_chat(S, playstyle_string)
S.mind.add_antag_datum(/datum/antagonist/cultist)
var/obj/effect/proc_holder/spell/sense_victims/SV = new
var/datum/spell/sense_victims/SV = new
AddSpell(SV)
S.mind.add_mind_objective(/datum/objective/cult_slaughter)
@@ -257,14 +257,14 @@
/obj/item/organ/internal/heart/demon/slaughter/insert(mob/living/carbon/M, special = 0)
. = ..()
if(M.mind)
M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null))
M.mind.AddSpell(new /datum/spell/bloodcrawl(null))
/obj/item/organ/internal/heart/demon/slaughter/remove(mob/living/carbon/M, special = 0)
. = ..()
if(M.mind)
REMOVE_TRAIT(M, TRAIT_BLOODCRAWL, "bloodcrawl")
REMOVE_TRAIT(M, TRAIT_BLOODCRAWL_EAT, "bloodcrawl_eat")
M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl)
M.mind.RemoveSpell(/datum/spell/bloodcrawl)
/mob/living/simple_animal/demon/slaughter/laughter
// The laughter demon! It's everyone's best friend! It just wants to hug
@@ -102,7 +102,7 @@
dust_if_respawnable(new_stand)
qdel(src)
/obj/effect/proc_holder/spell/summon_guardian_beacon
/datum/spell/summon_guardian_beacon
name = "Place Teleportation Beacon"
desc = "Mark a floor as your beacon point, allowing you to warp targets to it. Your beacon requires an anchor, will not work on space tiles."
clothes_req = FALSE
@@ -111,10 +111,10 @@
action_background_icon_state = "reset"
action_icon = 'icons/mob/guardian.dmi'
/obj/effect/proc_holder/spell/summon_guardian_beacon/create_new_targeting()
/datum/spell/summon_guardian_beacon/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/summon_guardian_beacon/cast(list/targets, mob/living/user = usr)
/datum/spell/summon_guardian_beacon/cast(list/targets, mob/living/user = usr)
var/target = targets[1]
var/mob/living/simple_animal/hostile/guardian/healer/guardian_user = user
var/turf/beacon_loc = get_turf(target)
@@ -125,7 +125,7 @@
return TRUE
/obj/effect/proc_holder/spell/surveillance_snare
/datum/spell/surveillance_snare
name = "Set Surveillance Snare"
desc = "Places an invisible Surveillance Snare on the ground, if someone walks over it you'll be alerted. Max of 6 snares active at a time"
clothes_req = FALSE
@@ -134,10 +134,10 @@
action_background_icon_state = "reset"
action_icon = 'icons/mob/guardian.dmi'
/obj/effect/proc_holder/spell/surveillance_snare/create_new_targeting()
/datum/spell/surveillance_snare/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/surveillance_snare/cast(list/targets, mob/living/user = usr)
/datum/spell/surveillance_snare/cast(list/targets, mob/living/user = usr)
var/target = targets[1]
var/mob/living/simple_animal/hostile/guardian/ranged/guardian_user = user
if(length(guardian_user.snares) < 6)
@@ -156,7 +156,7 @@
to_chat(user, "<span class='notice'>Snare disarmed.</span>")
revert_cast()
/obj/effect/proc_holder/spell/choose_battlecry
/datum/spell/choose_battlecry
name = "Change battlecry"
desc = "Changes your battlecry."
clothes_req = FALSE
@@ -165,10 +165,10 @@
action_background_icon_state = "communicate"
action_icon = 'icons/mob/guardian.dmi'
/obj/effect/proc_holder/spell/choose_battlecry/create_new_targeting()
/datum/spell/choose_battlecry/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/choose_battlecry/cast(list/targets, mob/living/user = usr)
/datum/spell/choose_battlecry/cast(list/targets, mob/living/user = usr)
var/mob/living/simple_animal/hostile/guardian/punch/guardian_user = user
var/input = tgui_input_text(guardian_user, "What do you want your battlecry to be? Max length of 6 characters.", "Change Battlecry", guardian_user.battlecry, 6)
if(!input)
@@ -36,7 +36,7 @@
/mob/living/simple_animal/hostile/guardian/healer/Initialize(mapload, mob/living/host)
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/summon_guardian_beacon(null))
AddSpell(new /datum/spell/summon_guardian_beacon(null))
/mob/living/simple_animal/hostile/guardian/healer/Destroy()
QDEL_NULL(beacon)
@@ -27,7 +27,7 @@
/mob/living/simple_animal/hostile/guardian/ranged/Initialize(mapload, mob/living/host)
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/surveillance_snare(null))
AddSpell(new /datum/spell/surveillance_snare(null))
/mob/living/simple_animal/hostile/guardian/ranged/ToggleMode()
if(loc == summoner)
@@ -12,7 +12,7 @@
/mob/living/simple_animal/hostile/guardian/punch/Initialize(mapload, mob/living/host)
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/choose_battlecry(null))
AddSpell(new /datum/spell/choose_battlecry(null))
/mob/living/simple_animal/hostile/guardian/punch/AttackingTarget()
. = ..()
@@ -45,11 +45,11 @@
/// How much weaken a successful ambush attack applies
var/ambush_weaken = 6 SECONDS
/// The spell the morph uses to morph
var/obj/effect/proc_holder/spell/mimic/morph/mimic_spell
var/datum/spell/mimic/morph/mimic_spell
/// The ambush action used by the morph
var/obj/effect/proc_holder/spell/morph_spell/ambush/ambush_spell
var/datum/spell/morph_spell/ambush/ambush_spell
/// The spell the morph uses to pass through airlocks
var/obj/effect/proc_holder/spell/morph_spell/pass_airlock/pass_airlock_spell
var/datum/spell/morph_spell/pass_airlock/pass_airlock_spell
/// How much the morph has gathered in terms of food. Used to reproduce and such
var/gathered_food = 20 // Start with a bit to use abilities
@@ -60,8 +60,8 @@
AddSpell(mimic_spell)
ambush_spell = new
AddSpell(ambush_spell)
AddSpell(new /obj/effect/proc_holder/spell/morph_spell/reproduce)
AddSpell(new /obj/effect/proc_holder/spell/morph_spell/open_vent)
AddSpell(new /datum/spell/morph_spell/reproduce)
AddSpell(new /datum/spell/morph_spell/open_vent)
pass_airlock_spell = new
AddSpell(pass_airlock_spell)
@@ -83,8 +83,8 @@
/mob/living/simple_animal/hostile/morph/wizard/Initialize(mapload)
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/smoke)
AddSpell(new /obj/effect/proc_holder/spell/forcewall)
AddSpell(new /datum/spell/smoke)
AddSpell(new /datum/spell/forcewall)
/mob/living/simple_animal/hostile/morph/proc/try_eat(atom/movable/A)
@@ -1,16 +1,16 @@
#define MORPH_AMBUSH_PERFECTION_TIME 15 SECONDS
/obj/effect/proc_holder/spell/morph_spell/ambush
/datum/spell/morph_spell/ambush
name = "Prepare Ambush"
desc = "Prepare an ambush. Dealing significantly more damage on the first hit and you will weaken the target. Only works while morphed. If the target tries to use you with their hands then you will do even more damage. \
Keeping still for another 15 seconds will perfect your disguise."
action_icon_state = "morph_ambush"
base_cooldown = 8 SECONDS
/obj/effect/proc_holder/spell/morph_spell/ambush/create_new_targeting()
/datum/spell/morph_spell/ambush/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/morph_spell/ambush/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
/datum/spell/morph_spell/ambush/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
. = ..()
if(!.)
return
@@ -23,7 +23,7 @@
to_chat(user, "<span class='warning'>You are already prepared!</span>")
return FALSE
/obj/effect/proc_holder/spell/morph_spell/ambush/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
/datum/spell/morph_spell/ambush/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
to_chat(user, "<span class='sinister'>You start preparing an ambush.</span>")
if(!do_after(user, 6 SECONDS, FALSE, user, TRUE, list(CALLBACK(src, PROC_REF(prepare_check), user)), FALSE))
if(!user.morphed)
@@ -33,7 +33,7 @@
return
user.prepare_ambush()
/obj/effect/proc_holder/spell/morph_spell/ambush/proc/prepare_check(mob/living/simple_animal/hostile/morph/user)
/datum/spell/morph_spell/ambush/proc/prepare_check(mob/living/simple_animal/hostile/morph/user)
return !user.morphed
/datum/status_effect/morph_ambush
@@ -1,15 +1,15 @@
/obj/effect/proc_holder/spell/morph_spell
/datum/spell/morph_spell
action_background_icon_state = "bg_morph"
clothes_req = FALSE
/// How much food it costs the morph to use this
var/hunger_cost = 0
/obj/effect/proc_holder/spell/morph_spell/Initialize(mapload)
. = ..()
/datum/spell/morph_spell/New()
..()
if(hunger_cost)
name = "[name] ([hunger_cost])"
/obj/effect/proc_holder/spell/morph_spell/create_new_handler()
/datum/spell/morph_spell/create_new_handler()
var/datum/spell_handler/morph/H = new
H.hunger_cost = hunger_cost
return H
@@ -1,17 +1,17 @@
/obj/effect/proc_holder/spell/morph_spell/open_vent
/datum/spell/morph_spell/open_vent
name = "Open Vents"
desc = "Spit out acidic puke on nearby vents or scrubbers. Will take a little while for the acid to take effect. Not usable from inside a vent."
action_icon_state = "acid_vent"
base_cooldown = 10 SECONDS
hunger_cost = 10
/obj/effect/proc_holder/spell/morph_spell/open_vent/create_new_targeting()
/datum/spell/morph_spell/open_vent/create_new_targeting()
var/datum/spell_targeting/aoe/T = new
T.range = 1
T.allowed_type = /obj/machinery/atmospherics/unary
return T
/obj/effect/proc_holder/spell/morph_spell/open_vent/valid_target(target, user)
/datum/spell/morph_spell/open_vent/valid_target(target, user)
if(istype(target, /obj/machinery/atmospherics/unary/vent_scrubber))
var/obj/machinery/atmospherics/unary/vent_scrubber/S = target
return S.welded
@@ -20,7 +20,7 @@
return V.welded
return FALSE
/obj/effect/proc_holder/spell/morph_spell/open_vent/cast(list/targets, mob/user)
/datum/spell/morph_spell/open_vent/cast(list/targets, mob/user)
if(!length(targets))
to_chat(user, "<span class='warning'>No nearby welded vents found!</span>")
revert_cast(user)
@@ -36,7 +36,7 @@
addtimer(CALLBACK(src, PROC_REF(unweld_vent), U), 2 SECONDS)
playsound(U, 'sound/items/welder.ogg', 100, TRUE)
/obj/effect/proc_holder/spell/morph_spell/open_vent/proc/unweld_vent(obj/machinery/atmospherics/unary/U)
/datum/spell/morph_spell/open_vent/proc/unweld_vent(obj/machinery/atmospherics/unary/U)
if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
var/obj/machinery/atmospherics/unary/vent_scrubber/S = U
S.welded = FALSE
@@ -1,5 +1,5 @@
// TODO refactor when spell code is component based instead of OO based
/obj/effect/proc_holder/spell/morph_spell/pass_airlock
/datum/spell/morph_spell/pass_airlock
name = "Pass Airlock"
desc = "Reform yourself so you can fit through a non bolted airlock. Takes a while to do and can only be used in a non disguised form."
action_background_icon_state = "bg_morph"
@@ -8,7 +8,7 @@
base_cooldown = 10 SECONDS
selection_activated_message = "<span class='sinister'>Click on an airlock to try pass it.</span>"
/obj/effect/proc_holder/spell/morph_spell/pass_airlock/create_new_targeting()
/datum/spell/morph_spell/pass_airlock/create_new_targeting()
var/datum/spell_targeting/click/T = new
T.range = 1
T.allowed_type = /obj/machinery/door/airlock
@@ -16,7 +16,7 @@
return T
/obj/effect/proc_holder/spell/morph_spell/pass_airlock/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
/datum/spell/morph_spell/pass_airlock/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
. = ..()
if(!.)
return
@@ -26,7 +26,7 @@
to_chat(user, "<span class='warning'>You can only pass through airlocks in your true form!</span>")
return FALSE
/obj/effect/proc_holder/spell/morph_spell/pass_airlock/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
/datum/spell/morph_spell/pass_airlock/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
var/obj/machinery/door/airlock/A = targets[1]
if(A.locked)
to_chat(user, "<span class='warning'>[A] is bolted shut! You're unable to create a crack to pass through!</span>")
@@ -49,5 +49,5 @@
user.forceMove(A.loc) // Move into the turf of the airlock
/obj/effect/proc_holder/spell/morph_spell/pass_airlock/proc/pass_check(mob/living/simple_animal/hostile/morph/user, obj/machinery/door/airlock/A)
/datum/spell/morph_spell/pass_airlock/proc/pass_check(mob/living/simple_animal/hostile/morph/user, obj/machinery/door/airlock/A)
return user.morphed || A.locked
@@ -1,4 +1,4 @@
/obj/effect/proc_holder/spell/morph_spell/reproduce
/datum/spell/morph_spell/reproduce
name = "Reproduce"
desc = "Split yourself in half making a new morph. Can only be used while on a floor. Makes you temporarily unable to vent crawl."
hunger_cost = 150 // 5 humans
@@ -6,10 +6,10 @@
action_icon_state = "morph_reproduce"
create_attack_logs = FALSE
/obj/effect/proc_holder/spell/morph_spell/reproduce/create_new_targeting()
/datum/spell/morph_spell/reproduce/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/morph_spell/reproduce/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
/datum/spell/morph_spell/reproduce/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
. = ..()
if(!.)
return
@@ -18,7 +18,7 @@
to_chat(user, "<span class='warning'>You can only split while on flooring!</span>")
return FALSE
/obj/effect/proc_holder/spell/morph_spell/reproduce/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
/datum/spell/morph_spell/reproduce/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
to_chat(user, "<span class='sinister'>You prepare to split in two, making you unable to vent crawl!</span>")
user.ventcrawler = VENTCRAWLER_NONE // Temporarily disable it
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a morph?", ROLE_MORPH, TRUE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/hostile/morph)
@@ -254,16 +254,16 @@
SSticker.mode.traitors |= mind
/mob/living/simple_animal/demon/pulse_demon/proc/give_spells()
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/cycle_camera)
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/toggle/do_drain(do_drain))
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/toggle/can_exit_cable(can_exit_cable))
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/cablehop)
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/emagtamper)
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/emp)
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/overload)
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/remotehijack)
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/remotedrain)
AddSpell(new /obj/effect/proc_holder/spell/pulse_demon/open_upgrades)
AddSpell(new /datum/spell/pulse_demon/cycle_camera)
AddSpell(new /datum/spell/pulse_demon/toggle/do_drain(do_drain))
AddSpell(new /datum/spell/pulse_demon/toggle/can_exit_cable(can_exit_cable))
AddSpell(new /datum/spell/pulse_demon/cablehop)
AddSpell(new /datum/spell/pulse_demon/emagtamper)
AddSpell(new /datum/spell/pulse_demon/emp)
AddSpell(new /datum/spell/pulse_demon/overload)
AddSpell(new /datum/spell/pulse_demon/remotehijack)
AddSpell(new /datum/spell/pulse_demon/remotedrain)
AddSpell(new /datum/spell/pulse_demon/open_upgrades)
/mob/living/simple_animal/demon/pulse_demon/get_status_tab_items()
var/list/status_tab_data = ..()
@@ -299,7 +299,7 @@
forceMove(T)
Move(T)
if(!current_cable && !current_power)
var/obj/effect/proc_holder/spell/pulse_demon/toggle/can_exit_cable/S = locate() in mob_spell_list
var/datum/spell/pulse_demon/toggle/can_exit_cable/S = locate() in mob_spell_list
if(!S.locked && !can_exit_cable)
can_exit_cable = TRUE
S.do_toggle(can_exit_cable)
@@ -318,7 +318,7 @@
if((!prev && !controlling_area) || (prev && controlling_area))
return // only update icons when we get or no longer have ANY area
for(var/obj/effect/proc_holder/spell/pulse_demon/S in mob_spell_list)
for(var/datum/spell/pulse_demon/S in mob_spell_list)
if(!S.action || S.locked)
continue
if(S.requires_area)
@@ -429,7 +429,7 @@
charge_drained += realdelta
update_glow()
for(var/obj/effect/proc_holder/spell/pulse_demon/S in mob_spell_list)
for(var/datum/spell/pulse_demon/S in mob_spell_list)
if(!S.action || S.locked || !S.cast_cost)
continue
var/dist = S.cast_cost - orig
@@ -8,7 +8,7 @@
#define PD_UPGRADE_HEALTH_COST "Efficiency"
#define PD_UPGRADE_MAX_CHARGE "Capacity"
/obj/effect/proc_holder/spell/pulse_demon
/datum/spell/pulse_demon
panel = "Pulse Demon"
school = "pulse demon"
clothes_req = FALSE
@@ -21,11 +21,11 @@
base_cooldown = 20 SECONDS
level_max = 4
/obj/effect/proc_holder/spell/pulse_demon/New()
/datum/spell/pulse_demon/New()
. = ..()
update_info()
/obj/effect/proc_holder/spell/pulse_demon/proc/update_info()
/datum/spell/pulse_demon/proc/update_info()
if(locked)
name = "[initial(name)] (Locked) ([format_si_suffix(unlock_cost)]W)"
desc = "[initial(desc)] It costs [format_si_suffix(unlock_cost)]W to unlock."
@@ -36,7 +36,7 @@
action.desc = desc
action.UpdateButtons()
/obj/effect/proc_holder/spell/pulse_demon/can_cast(mob/living/simple_animal/demon/pulse_demon/user, charge_check, show_message)
/datum/spell/pulse_demon/can_cast(mob/living/simple_animal/demon/pulse_demon/user, charge_check, show_message)
if(!..())
return FALSE
if(!istype(user))
@@ -57,7 +57,7 @@
return FALSE
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/cast(list/targets, mob/living/simple_animal/demon/pulse_demon/user)
/datum/spell/pulse_demon/cast(list/targets, mob/living/simple_animal/demon/pulse_demon/user)
if(!istype(user) || locked || user.charge < cast_cost || !length(targets))
return FALSE
if(requires_area && !user.controlling_area)
@@ -72,14 +72,14 @@
revert_cast(user)
return FALSE
/obj/effect/proc_holder/spell/pulse_demon/create_new_targeting()
/datum/spell/pulse_demon/create_new_targeting()
return new /datum/spell_targeting/clicked_atom
/obj/effect/proc_holder/spell/pulse_demon/proc/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/proc/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
return FALSE
// handles purchasing and upgrading abilities
/obj/effect/proc_holder/spell/pulse_demon/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
/datum/spell/pulse_demon/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
if(!istype(user))
return
@@ -115,11 +115,11 @@
else
to_chat(user, "<span class='warning'>You cannot afford to upgrade this ability! It costs [format_si_suffix(upgrade_cost)]W to upgrade.</span>")
/obj/effect/proc_holder/spell/pulse_demon/proc/do_upgrade(mob/living/simple_animal/demon/pulse_demon/user)
/datum/spell/pulse_demon/proc/do_upgrade(mob/living/simple_animal/demon/pulse_demon/user)
cooldown_handler.recharge_duration = round(base_cooldown / (1.5 ** spell_level))
to_chat(user, "<span class='notice'>You have upgraded [initial(name)] to level [spell_level + 1], it now takes [cooldown_handler.recharge_duration / 10] seconds to recharge.</span>")
/obj/effect/proc_holder/spell/pulse_demon/cablehop
/datum/spell/pulse_demon/cablehop
name = "Cable Hop"
desc = "Jump to another cable in view."
action_icon_state = "pd_cablehop"
@@ -127,7 +127,7 @@
cast_cost = 5 KW
upgrade_cost = 75 KW
/obj/effect/proc_holder/spell/pulse_demon/cablehop/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/cablehop/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
var/turf/O = get_turf(user)
var/turf/T = get_turf(target)
var/obj/structure/cable/C = locate(/obj/structure/cable) in T
@@ -145,7 +145,7 @@
user.Move(T)
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/emagtamper
/datum/spell/pulse_demon/emagtamper
name = "Electromagnetic Tamper"
desc = "Unlocks hidden programming in machines. Must be inside a hijacked APC to use."
action_icon_state = "pd_emag"
@@ -154,12 +154,12 @@
upgrade_cost = 200 KW
requires_area = TRUE
/obj/effect/proc_holder/spell/pulse_demon/emagtamper/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/emagtamper/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
to_chat(user, "<span class='warning'>You attempt to tamper with [target]!</span>")
target.emag_act(user)
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/emp
/datum/spell/pulse_demon/emp
name = "Electromagnetic Pulse"
desc = "Creates an EMP where you click. Be careful not to use it on yourself!"
action_icon_state = "pd_emp"
@@ -168,12 +168,12 @@
upgrade_cost = 200 KW
requires_area = TRUE
/obj/effect/proc_holder/spell/pulse_demon/emp/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/emp/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
to_chat(user, "<span class='warning'>You attempt to EMP [target]!</span>")
empulse(get_turf(target), 1, 1)
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/overload
/datum/spell/pulse_demon/overload
name = "Overload Machine"
desc = "Overloads a machine, causing it to explode."
action_icon_state = "pd_overload"
@@ -182,7 +182,7 @@
upgrade_cost = 500 KW
requires_area = TRUE
/obj/effect/proc_holder/spell/pulse_demon/overload/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/overload/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
var/obj/machinery/M = target
if(!istype(M))
to_chat(user, "<span class='warning'>That is not a machine.</span>")
@@ -194,13 +194,13 @@
addtimer(CALLBACK(src, PROC_REF(detonate), M), 5 SECONDS)
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/overload/proc/detonate(obj/machinery/target)
/datum/spell/pulse_demon/overload/proc/detonate(obj/machinery/target)
if(!QDELETED(target))
explosion(get_turf(target), 0, 1, 1, 0)
if(!QDELETED(target))
qdel(target)
/obj/effect/proc_holder/spell/pulse_demon/remotehijack
/datum/spell/pulse_demon/remotehijack
name = "Remote Hijack"
desc = "Remotely hijacks an APC."
action_icon_state = "pd_remotehack"
@@ -209,7 +209,7 @@
level_max = 0
base_cooldown = 3 SECONDS // you have to wait for the regular hijack time anyway
/obj/effect/proc_holder/spell/pulse_demon/remotehijack/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/remotehijack/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
var/obj/machinery/power/apc/A = target
if(!istype(A))
to_chat(user, "<span class='warning'>That is not an APC.</span>")
@@ -218,7 +218,7 @@
to_chat(user, "<span class='warning'>You cannot hijack that APC right now!</span>")
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/remotedrain
/datum/spell/pulse_demon/remotedrain
name = "Remote Drain"
desc = "Remotely drains a power source."
action_icon_state = "pd_remotedrain"
@@ -226,7 +226,7 @@
cast_cost = 100
upgrade_cost = 100 KW
/obj/effect/proc_holder/spell/pulse_demon/remotedrain/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/remotedrain/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
if(isapc(target))
var/drained = user.drain_APC(target, PULSEDEMON_REMOTE_DRAIN_MULTIPLIER)
if(drained == PULSEDEMON_SOURCE_DRAIN_INVALID)
@@ -241,20 +241,20 @@
return FALSE
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/toggle
/datum/spell/pulse_demon/toggle
base_cooldown = 0
cast_cost = 0
create_attack_logs = FALSE
var/base_message = "see messages you shouldn't!"
/obj/effect/proc_holder/spell/pulse_demon/toggle/New(initstate = FALSE)
/datum/spell/pulse_demon/toggle/New(initstate = FALSE)
. = ..()
do_toggle(initstate, null)
/obj/effect/proc_holder/spell/pulse_demon/toggle/create_new_targeting()
/datum/spell/pulse_demon/toggle/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/pulse_demon/toggle/proc/do_toggle(varstate, mob/user)
/datum/spell/pulse_demon/toggle/proc/do_toggle(varstate, mob/user)
if(action)
action.background_icon_state = varstate ? action_background_icon_state : "[action_background_icon_state]_disabled"
action.UpdateButtons()
@@ -262,7 +262,7 @@
to_chat(user, "<span class='notice'>You will [varstate ? "now" : "no longer"] [base_message]</span>")
return varstate
/obj/effect/proc_holder/spell/pulse_demon/toggle/do_drain
/datum/spell/pulse_demon/toggle/do_drain
name = "Toggle Draining"
desc = "Toggle whether you drain charge from power sources."
base_message = "drain charge from power sources."
@@ -270,11 +270,11 @@
locked = FALSE
level_max = 0
/obj/effect/proc_holder/spell/pulse_demon/toggle/do_drain/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/toggle/do_drain/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
user.do_drain = do_toggle(!user.do_drain, user)
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/toggle/do_drain/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
/datum/spell/pulse_demon/toggle/do_drain/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
if(!istype(user))
return
@@ -288,7 +288,7 @@
user.power_drain_rate = amount
to_chat(user, "<span class='notice'>Drain speed has been set to [format_si_suffix(user.power_drain_rate)]W per second.</span>")
/obj/effect/proc_holder/spell/pulse_demon/toggle/can_exit_cable
/datum/spell/pulse_demon/toggle/can_exit_cable
name = "Toggle Self-Sustaining"
desc = "Toggle whether you can move outside of cables or power sources."
base_message = "move outside of cables."
@@ -297,18 +297,18 @@
upgrade_cost = 300 KW
level_max = 3
/obj/effect/proc_holder/spell/pulse_demon/toggle/can_exit_cable/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/toggle/can_exit_cable/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
if(user.can_exit_cable && !(user.current_cable || user.current_power))
to_chat(user, "<span class='warning'>Enter a cable or power source first!</span>")
return FALSE
user.can_exit_cable = do_toggle(!user.can_exit_cable, user)
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/toggle/can_exit_cable/do_upgrade(mob/living/simple_animal/demon/pulse_demon/user)
/datum/spell/pulse_demon/toggle/can_exit_cable/do_upgrade(mob/living/simple_animal/demon/pulse_demon/user)
user.outside_cable_speed = max(initial(user.outside_cable_speed) - spell_level, 1)
to_chat(user, "<span class='notice'>You have upgraded [initial(name)] to level [spell_level + 1], you will now move faster outside of cables.</span>")
/obj/effect/proc_holder/spell/pulse_demon/cycle_camera
/datum/spell/pulse_demon/cycle_camera
name = "Cycle Camera View"
desc = "Jump between the cameras in your APC's area. Alt-click to return to the APC."
action_icon_state = "pd_camera_view"
@@ -320,10 +320,10 @@
requires_area = TRUE
var/current_camera = 0
/obj/effect/proc_holder/spell/pulse_demon/cycle_camera/create_new_targeting()
/datum/spell/pulse_demon/cycle_camera/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/pulse_demon/cycle_camera/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
/datum/spell/pulse_demon/cycle_camera/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
if(!istype(user))
return
current_camera = 0
@@ -334,7 +334,7 @@
return
user.forceMove(user.current_power)
/obj/effect/proc_holder/spell/pulse_demon/cycle_camera/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/cycle_camera/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
if(!length(user.controlling_area.cameras))
return FALSE
@@ -352,7 +352,7 @@
user.forceMove(locateUID(user.controlling_area.cameras[current_camera + 1]))
return TRUE
/obj/effect/proc_holder/spell/pulse_demon/open_upgrades
/datum/spell/pulse_demon/open_upgrades
name = "Open Upgrade Menu"
desc = "Open the upgrades menu. Alt-click for descriptions and costs."
action_icon_state = "pd_upgrade"
@@ -380,10 +380,10 @@
PD_UPGRADE_MAX_CHARGE = "Increase the total amount of charge you can have at once."
)
/obj/effect/proc_holder/spell/pulse_demon/open_upgrades/create_new_targeting()
/datum/spell/pulse_demon/open_upgrades/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/pulse_demon/open_upgrades/proc/calc_cost(mob/living/simple_animal/demon/pulse_demon/user, upgrade)
/datum/spell/pulse_demon/open_upgrades/proc/calc_cost(mob/living/simple_animal/demon/pulse_demon/user, upgrade)
var/cost
switch(upgrade)
if(PD_UPGRADE_HIJACK_SPEED)
@@ -416,7 +416,7 @@
return -1
return round(cost)
/obj/effect/proc_holder/spell/pulse_demon/open_upgrades/proc/get_upgrades(mob/living/simple_animal/demon/pulse_demon/user)
/datum/spell/pulse_demon/open_upgrades/proc/get_upgrades(mob/living/simple_animal/demon/pulse_demon/user)
var/upgrades = list()
for(var/upgrade in upgrade_icons)
var/cost = calc_cost(user, upgrade)
@@ -425,7 +425,7 @@
upgrades["[upgrade] ([format_si_suffix(cost)]W)"] = upgrade_icons[upgrade]
return upgrades
/obj/effect/proc_holder/spell/pulse_demon/open_upgrades/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
/datum/spell/pulse_demon/open_upgrades/AltClick(mob/living/simple_animal/demon/pulse_demon/user)
if(!istype(user))
return
@@ -434,7 +434,7 @@
var/cost = calc_cost(user, upgrade)
to_chat(user, "<b>[upgrade]</b> ([cost == -1 ? "Fully Upgraded" : "[format_si_suffix(cost)]W"]) - [upgrade_descs[upgrade]]")
/obj/effect/proc_holder/spell/pulse_demon/open_upgrades/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
/datum/spell/pulse_demon/open_upgrades/try_cast_action(mob/living/simple_animal/demon/pulse_demon/user, atom/target)
var/upgrades = get_upgrades(user)
if(!length(upgrades))
to_chat(user, "<span class='warning'>You have already fully upgraded everything available!</span>")
@@ -192,13 +192,13 @@
to_chat(src, chat_box_red(messages.Join("<br>")))
/mob/living/simple_animal/revenant/proc/giveSpells()
mind.AddSpell(new /obj/effect/proc_holder/spell/night_vision/revenant(null))
mind.AddSpell(new /obj/effect/proc_holder/spell/revenant_transmit(null))
mind.AddSpell(new /obj/effect/proc_holder/spell/aoe/revenant/defile(null))
mind.AddSpell(new /obj/effect/proc_holder/spell/aoe/revenant/malfunction(null))
mind.AddSpell(new /obj/effect/proc_holder/spell/aoe/revenant/overload(null))
mind.AddSpell(new /obj/effect/proc_holder/spell/aoe/revenant/haunt_object(null))
mind.AddSpell(new /obj/effect/proc_holder/spell/aoe/revenant/hallucinations(null))
mind.AddSpell(new /datum/spell/night_vision/revenant(null))
mind.AddSpell(new /datum/spell/revenant_transmit(null))
mind.AddSpell(new /datum/spell/aoe/revenant/defile(null))
mind.AddSpell(new /datum/spell/aoe/revenant/malfunction(null))
mind.AddSpell(new /datum/spell/aoe/revenant/overload(null))
mind.AddSpell(new /datum/spell/aoe/revenant/haunt_object(null))
mind.AddSpell(new /datum/spell/aoe/revenant/hallucinations(null))
return TRUE
@@ -108,7 +108,7 @@
return
//Toggle night vision: lets the revenant toggle its night vision
/obj/effect/proc_holder/spell/night_vision/revenant
/datum/spell/night_vision/revenant
base_cooldown = 0
panel = "Revenant Abilities"
message = "<span class='revennotice'>You toggle your night vision.</span>"
@@ -116,7 +116,7 @@
action_background_icon_state = "bg_revenant"
//Transmit: the revemant's only direct way to communicate. Sends a single message silently to a single mob
/obj/effect/proc_holder/spell/revenant_transmit
/datum/spell/revenant_transmit
name = "Transmit"
desc = "Telepathically transmits a message to the target."
panel = "Revenant Abilities"
@@ -125,12 +125,12 @@
action_icon_state = "r_transmit"
action_background_icon_state = "bg_revenant"
/obj/effect/proc_holder/spell/revenant_transmit/create_new_targeting()
/datum/spell/revenant_transmit/create_new_targeting()
var/datum/spell_targeting/targeted/T = new()
T.allowed_type = /mob/living
return T
/obj/effect/proc_holder/spell/revenant_transmit/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
/datum/spell/revenant_transmit/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
for(var/mob/living/M in targets)
spawn(0)
var/msg = tgui_input_text(user, "What do you wish to tell [M]?", "Transmit")
@@ -141,7 +141,7 @@
to_chat(user, "<span class='revennotice'><b>You transmit to [M]:</b> [msg]</span>")
to_chat(M, "<span class='revennotice'><b>An alien voice resonates from all around...</b></span><i> [msg]</I>")
/obj/effect/proc_holder/spell/aoe/revenant
/datum/spell/aoe/revenant
name = "Spell"
clothes_req = FALSE
action_background_icon_state = "bg_revenant"
@@ -157,20 +157,20 @@
/// How much essence it costs to use
var/cast_amount = 50
/obj/effect/proc_holder/spell/aoe/revenant/New()
/datum/spell/aoe/revenant/New()
..()
if(locked)
name = "[initial(name)] ([unlock_amount]E)"
else
name = "[initial(name)] ([cast_amount]E)"
/obj/effect/proc_holder/spell/aoe/revenant/revert_cast(mob/user)
/datum/spell/aoe/revenant/revert_cast(mob/user)
. = ..()
to_chat(user, "<span class='revennotice'>Your ability wavers and fails!</span>")
var/mob/living/simple_animal/revenant/R = user
R?.essence += cast_amount //refund the spell and reset
/obj/effect/proc_holder/spell/aoe/revenant/can_cast(mob/living/simple_animal/revenant/user = usr, charge_check = TRUE, show_message = FALSE)
/datum/spell/aoe/revenant/can_cast(mob/living/simple_animal/revenant/user = usr, charge_check = TRUE, show_message = FALSE)
if(user.inhibited)
return FALSE
if(cooldown_handler.is_on_cooldown())
@@ -182,7 +182,7 @@
return FALSE
return TRUE
/obj/effect/proc_holder/spell/aoe/revenant/proc/attempt_cast(mob/living/simple_animal/revenant/user = usr)
/datum/spell/aoe/revenant/proc/attempt_cast(mob/living/simple_animal/revenant/user = usr)
if(locked)
if(!user.castcheck(-unlock_amount))
cooldown_handler.revert_cast()
@@ -204,7 +204,7 @@
return TRUE
//Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people.
/obj/effect/proc_holder/spell/aoe/revenant/overload
/datum/spell/aoe/revenant/overload
name = "Overload Lights"
desc = "Directs a large amount of essence into nearby electrical lights, causing lights to shock those nearby."
base_cooldown = 20 SECONDS
@@ -215,21 +215,21 @@
action_icon_state = "overload_lights"
aoe_range = 5
/obj/effect/proc_holder/spell/aoe/revenant/overload/create_new_targeting()
/datum/spell/aoe/revenant/overload/create_new_targeting()
var/datum/spell_targeting/aoe/targeting = new()
targeting.range = aoe_range
targeting.allowed_type = /obj/machinery/light
return targeting
/obj/effect/proc_holder/spell/aoe/revenant/overload/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
/datum/spell/aoe/revenant/overload/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/obj/machinery/light/L as anything in targets)
INVOKE_ASYNC(src, PROC_REF(shock_lights), L, user)
/obj/effect/proc_holder/spell/aoe/revenant/overload/proc/shock_lights(obj/machinery/light/L, mob/living/simple_animal/revenant/user)
/datum/spell/aoe/revenant/overload/proc/shock_lights(obj/machinery/light/L, mob/living/simple_animal/revenant/user)
if(!L.on)
return
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</span>")
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</b></span>")
do_sparks(4, 0, L)
new /obj/effect/temp_visual/revenant(L.loc)
sleep(2 SECONDS)
@@ -246,7 +246,7 @@
playsound(M, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
//Defile: Corrupts nearby stuff, unblesses floor tiles.
/obj/effect/proc_holder/spell/aoe/revenant/defile
/datum/spell/aoe/revenant/defile
name = "Defile"
desc = "Twists and corrupts the nearby area as well as dispelling holy auras on floors."
base_cooldown = 15 SECONDS
@@ -257,12 +257,12 @@
action_icon_state = "defile"
aoe_range = 4
/obj/effect/proc_holder/spell/aoe/revenant/defile/create_new_targeting()
/datum/spell/aoe/revenant/defile/create_new_targeting()
var/datum/spell_targeting/aoe/turf/targeting = new()
targeting.range = aoe_range
return targeting
/obj/effect/proc_holder/spell/aoe/revenant/defile/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
/datum/spell/aoe/revenant/defile/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(!attempt_cast(user))
return
for(var/turf/T in targets)
@@ -271,7 +271,7 @@
A.defile()
//Malfunction: Makes bad stuff happen to robots and machines.
/obj/effect/proc_holder/spell/aoe/revenant/malfunction
/datum/spell/aoe/revenant/malfunction
name = "Malfunction"
desc = "Corrupts and damages nearby machines and mechanical objects."
base_cooldown = 200
@@ -280,18 +280,18 @@
action_icon_state = "malfunction"
aoe_range = 2
/obj/effect/proc_holder/spell/aoe/revenant/malfunction/create_new_targeting()
/datum/spell/aoe/revenant/malfunction/create_new_targeting()
var/datum/spell_targeting/aoe/turf/targeting = new()
targeting.range = aoe_range
return targeting
//A note to future coders: do not replace this with an EMP because it will wreck malf AIs and gang dominators and everyone will hate you.
/obj/effect/proc_holder/spell/aoe/revenant/malfunction/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
/datum/spell/aoe/revenant/malfunction/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(attempt_cast(user))
for(var/turf/T in targets)
INVOKE_ASYNC(src, PROC_REF(effect), user, T)
/obj/effect/proc_holder/spell/aoe/revenant/malfunction/proc/effect(mob/living/simple_animal/revenant/user, turf/T)
/datum/spell/aoe/revenant/malfunction/proc/effect(mob/living/simple_animal/revenant/user, turf/T)
T.rev_malfunction(TRUE)
for(var/atom/A in T.contents)
A.rev_malfunction(TRUE)
@@ -299,7 +299,7 @@
/**
* Makes objects be haunted and then throws them at conscious people to do damage, spooky!
*/
/obj/effect/proc_holder/spell/aoe/revenant/haunt_object
/datum/spell/aoe/revenant/haunt_object
name = "Haunt Objects"
desc = "Empower nearby objects to you with ghostly energy, causing them to attack nearby mortals. \
Items closer to you are more likely to be haunted."
@@ -315,13 +315,13 @@
/// A list of all attack timers started by this spell being cast
var/list/attack_timers = list()
/obj/effect/proc_holder/spell/aoe/revenant/haunt_object/create_new_targeting()
/datum/spell/aoe/revenant/haunt_object/create_new_targeting()
var/datum/spell_targeting/aoe/targeting = new()
targeting.range = aoe_range
targeting.allowed_type = /obj/item
return targeting
/obj/effect/proc_holder/spell/aoe/revenant/haunt_object/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
/datum/spell/aoe/revenant/haunt_object/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(!attempt_cast(user))
return
@@ -357,7 +357,7 @@
addtimer(CALLBACK(src, PROC_REF(stop_timers)), 65 SECONDS, TIMER_UNIQUE)
/// Handles making an object haunted and setting it up to attack
/obj/effect/proc_holder/spell/aoe/revenant/haunt_object/proc/make_spooky(obj/item/item_to_possess, mob/living/simple_animal/revenant/user)
/datum/spell/aoe/revenant/haunt_object/proc/make_spooky(obj/item/item_to_possess, mob/living/simple_animal/revenant/user)
new /obj/effect/temp_visual/revenant(get_turf(item_to_possess)) // Thematic spooky visuals
var/mob/living/simple_animal/possessed_object/possessed_object = new(item_to_possess) // Begin haunting object
item_to_possess.throwforce = min(item_to_possess.throwforce + 5, 15) // Damage it should do? throwforce+5 or 15, whichever is lower
@@ -372,7 +372,7 @@
addtimer(CALLBACK(possessed_object, TYPE_PROC_REF(/mob/living/simple_animal/possessed_object, death)), 70 SECONDS, TIMER_UNIQUE) // De-haunt the object
/// Handles finding a valid target and throwing us at it
/obj/effect/proc_holder/spell/aoe/revenant/haunt_object/proc/attack(mob/living/simple_animal/possessed_object/possessed_object, mob/living/simple_animal/revenant/user)
/datum/spell/aoe/revenant/haunt_object/proc/attack(mob/living/simple_animal/possessed_object/possessed_object, mob/living/simple_animal/revenant/user)
var/list/potential_victims = list()
for(var/turf/turf_to_search in spiral_range_turfs(aoe_range, get_turf(possessed_object)))
for(var/mob/living/carbon/potential_victim in turf_to_search)
@@ -393,20 +393,20 @@
possessed_object.throw_at(victim, aoe_range, 2, user, dodgeable = FALSE)
/// Sets the glow on the haunted object, scales up based on throwforce
/obj/effect/proc_holder/spell/aoe/revenant/haunt_object/proc/set_outline(mob/living/simple_animal/possessed_object/possessed_object)
/datum/spell/aoe/revenant/haunt_object/proc/set_outline(mob/living/simple_animal/possessed_object/possessed_object)
possessed_object.remove_filter("haunt_glow")
var/outline_size = min((possessed_object.possessed_item.throwforce / 15) * 3, 3)
possessed_object.add_filter("haunt_glow", 2, list("type" = "outline", "color" = "#7A4FA9", "size" = outline_size)) // Give it spooky purple outline
/// Stop all attack timers cast by the previous spell use
/obj/effect/proc_holder/spell/aoe/revenant/haunt_object/proc/stop_timers()
/datum/spell/aoe/revenant/haunt_object/proc/stop_timers()
for(var/I in attack_timers)
deltimer(I)
/**
* Gives everyone in a 7 tile radius 2 minutes of hallucinations
*/
/obj/effect/proc_holder/spell/aoe/revenant/hallucinations
/datum/spell/aoe/revenant/hallucinations
name = "Hallucination Aura"
desc = "Toy with the living nearby, giving them glimpses of things that could be or once were."
action_icon_state = "hallucinations"
@@ -416,13 +416,13 @@
stun = 1 SECONDS
reveal = 3 SECONDS
/obj/effect/proc_holder/spell/aoe/revenant/hallucinations/create_new_targeting()
/datum/spell/aoe/revenant/hallucinations/create_new_targeting()
var/datum/spell_targeting/aoe/targeting = new()
targeting.range = aoe_range
targeting.allowed_type = /mob/living/carbon
return targeting
/obj/effect/proc_holder/spell/aoe/revenant/hallucinations/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
/datum/spell/aoe/revenant/hallucinations/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
if(!attempt_cast(user))
return
+1 -1
View File
@@ -3,7 +3,7 @@
desc = "High Five?"
var/catchphrase = "High Five!"
var/on_use_sound = null
var/obj/effect/proc_holder/spell/touch/attached_spell
var/datum/spell/touch/attached_spell
icon = 'icons/obj/weapons/magical_weapons.dmi'
icon_state = "disintegrate"
item_state = null
+3 -3
View File
@@ -400,9 +400,9 @@
if(SS.purified)
make_holy()
// Replace regular soulstone summoning with purified soulstones
if(is_type_in_list(/obj/effect/proc_holder/spell/aoe/conjure/build/soulstone, mob_spell_list))
RemoveSpell(/obj/effect/proc_holder/spell/aoe/conjure/build/soulstone)
AddSpell(new /obj/effect/proc_holder/spell/aoe/conjure/build/soulstone/holy)
if(is_type_in_list(/datum/spell/aoe/conjure/build/soulstone, mob_spell_list))
RemoveSpell(/datum/spell/aoe/conjure/build/soulstone)
AddSpell(new /datum/spell/aoe/conjure/build/soulstone/holy)
else if(mind.has_antag_datum(/datum/antagonist/cultist)) // Re-grant cult actions, lost in the transfer
var/datum/action/innate/cult/comm/CC = new
+58 -58
View File
@@ -6,7 +6,7 @@
var/category = "Offensive"
var/cost = 2
var/refundable = TRUE
var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell
var/datum/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell
var/buy_word = "Learn"
var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook
@@ -21,8 +21,8 @@
return LearnSpell(user, book, S)
/datum/spellbook_entry/proc/LearnSpell(mob/living/carbon/human/user, obj/item/spellbook/book, obj/effect/proc_holder/spell/newspell)
for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list)
/datum/spellbook_entry/proc/LearnSpell(mob/living/carbon/human/user, obj/item/spellbook/book, datum/spell/newspell)
for(var/datum/spell/aspell in user.mind.spell_list)
if(initial(newspell.name) == initial(aspell.name)) // Not using directly in case it was learned from one spellbook then upgraded in another
if(aspell.spell_level >= aspell.level_max)
to_chat(user, "<span class='warning'>This spell cannot be improved further.</span>")
@@ -59,7 +59,7 @@
return FALSE
if(!S)
S = new spell_type()
for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list)
for(var/datum/spell/aspell in user.mind.spell_list)
if(initial(S.name) == initial(aspell.name))
return TRUE
return FALSE
@@ -72,7 +72,7 @@
if(!S) //This happens when the spell's source is from another spellbook, from loadouts, or adminery, this create a new template temporary spell
S = new spell_type()
var/spell_levels = 0
for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list)
for(var/datum/spell/aspell in user.mind.spell_list)
if(initial(S.name) == initial(aspell.name))
spell_levels = aspell.spell_level
user.mind.spell_list.Remove(aspell)
@@ -97,126 +97,126 @@
//Offensive
/datum/spellbook_entry/blind
name = "Blind"
spell_type = /obj/effect/proc_holder/spell/trigger/blind
spell_type = /datum/spell/genetic/blind
category = "Offensive"
cost = 1
/datum/spellbook_entry/lightningbolt
name = "Lightning Bolt"
spell_type = /obj/effect/proc_holder/spell/charge_up/bounce/lightning
spell_type = /datum/spell/charge_up/bounce/lightning
category = "Offensive"
cost = 1
/datum/spellbook_entry/cluwne
name = "Curse of the Cluwne"
spell_type = /obj/effect/proc_holder/spell/touch/cluwne
spell_type = /datum/spell/touch/cluwne
category = "Offensive"
/datum/spellbook_entry/banana_touch
name = "Banana Touch"
spell_type = /obj/effect/proc_holder/spell/touch/banana
spell_type = /datum/spell/touch/banana
cost = 1
/datum/spellbook_entry/mime_malaise
name = "Mime Malaise"
spell_type = /obj/effect/proc_holder/spell/touch/mime_malaise
spell_type = /datum/spell/touch/mime_malaise
cost = 1
/datum/spellbook_entry/horseman
name = "Curse of the Horseman"
spell_type = /obj/effect/proc_holder/spell/horsemask
spell_type = /datum/spell/horsemask
category = "Offensive"
/datum/spellbook_entry/disintegrate
name = "Disintegrate"
spell_type = /obj/effect/proc_holder/spell/touch/disintegrate
spell_type = /datum/spell/touch/disintegrate
category = "Offensive"
/datum/spellbook_entry/fireball
name = "Fireball"
spell_type = /obj/effect/proc_holder/spell/fireball
spell_type = /datum/spell/fireball
category = "Offensive"
/datum/spellbook_entry/summon_toolbox
name = "Homing Toolbox"
spell_type = /obj/effect/proc_holder/spell/fireball/toolbox
spell_type = /datum/spell/fireball/toolbox
category = "Offensive"
cost = 1
/datum/spellbook_entry/fleshtostone
name = "Flesh to Stone"
spell_type = /obj/effect/proc_holder/spell/touch/flesh_to_stone
spell_type = /datum/spell/touch/flesh_to_stone
category = "Offensive"
/datum/spellbook_entry/mutate
name = "Mutate"
spell_type = /obj/effect/proc_holder/spell/genetic/mutate
spell_type = /datum/spell/genetic/mutate
category = "Offensive"
/datum/spellbook_entry/rod_form
name = "Rod Form"
spell_type = /obj/effect/proc_holder/spell/rod_form
spell_type = /datum/spell/rod_form
category = "Offensive"
/datum/spellbook_entry/infinite_guns
name = "Lesser Summon Guns"
spell_type = /obj/effect/proc_holder/spell/infinite_guns
spell_type = /datum/spell/infinite_guns
category = "Offensive"
//Defensive
/datum/spellbook_entry/disabletech
name = "Disable Tech"
spell_type = /obj/effect/proc_holder/spell/emplosion/disable_tech
spell_type = /datum/spell/emplosion/disable_tech
category = "Defensive"
cost = 1
/datum/spellbook_entry/forcewall
name = "Force Wall"
spell_type = /obj/effect/proc_holder/spell/forcewall
spell_type = /datum/spell/forcewall
category = "Defensive"
cost = 1
/datum/spellbook_entry/rathens
name = "Rathen's Secret"
spell_type = /obj/effect/proc_holder/spell/rathens
spell_type = /datum/spell/rathens
category = "Defensive"
cost = 2
/datum/spellbook_entry/repulse
name = "Repulse"
spell_type = /obj/effect/proc_holder/spell/aoe/repulse
spell_type = /datum/spell/aoe/repulse
category = "Defensive"
cost = 1
/datum/spellbook_entry/smoke
name = "Smoke"
spell_type = /obj/effect/proc_holder/spell/smoke
spell_type = /datum/spell/smoke
category = "Defensive"
cost = 1
/datum/spellbook_entry/lichdom
name = "Bind Soul"
spell_type = /obj/effect/proc_holder/spell/lichdom
spell_type = /datum/spell/lichdom
category = "Defensive"
is_ragin_restricted = TRUE
/datum/spellbook_entry/magicm
name = "Magic Missile"
spell_type = /obj/effect/proc_holder/spell/projectile/magic_missile
spell_type = /datum/spell/projectile/magic_missile
category = "Defensive"
/datum/spellbook_entry/timestop
name = "Time Stop"
spell_type = /obj/effect/proc_holder/spell/aoe/conjure/timestop
spell_type = /datum/spell/aoe/conjure/timestop
category = "Defensive"
/datum/spellbook_entry/sacred_flame
name = "Sacred Flame and Fire Immunity"
spell_type = /obj/effect/proc_holder/spell/sacred_flame
spell_type = /datum/spell/sacred_flame
cost = 1
category = "Defensive"
/datum/spellbook_entry/sacred_flame/LearnSpell(mob/living/carbon/human/user, obj/item/spellbook/book, obj/effect/proc_holder/spell/newspell)
/datum/spellbook_entry/sacred_flame/LearnSpell(mob/living/carbon/human/user, obj/item/spellbook/book, datum/spell/newspell)
to_chat(user, "<span class='notice'>You feel fireproof.</span>")
ADD_TRAIT(user, TRAIT_RESISTHEAT, MAGIC_TRAIT)
ADD_TRAIT(user, TRAIT_RESISTHIGHPRESSURE, MAGIC_TRAIT)
@@ -230,11 +230,11 @@
/datum/spellbook_entry/summon_supermatter
name = "Summon Supermatter Crystal"
spell_type = /obj/effect/proc_holder/spell/aoe/conjure/summon_supermatter
spell_type = /datum/spell/aoe/conjure/summon_supermatter
cost = 3
category = "Defensive"
/datum/spellbook_entry/summon_supermatter/LearnSpell(mob/living/carbon/human/user, obj/item/spellbook/book, obj/effect/proc_holder/spell/newspell)
/datum/spellbook_entry/summon_supermatter/LearnSpell(mob/living/carbon/human/user, obj/item/spellbook/book, datum/spell/newspell)
to_chat(user, "<span class='notice'>You feel a little bit of supermatter enter your body.</span>")
ADD_TRAIT(user, TRAIT_RADIMMUNE, MAGIC_TRAIT)
ADD_TRAIT(user, SM_HALLUCINATION_IMMUNE, MAGIC_TRAIT)
@@ -249,64 +249,64 @@
//Mobility
/datum/spellbook_entry/knock
name = "Knock"
spell_type = /obj/effect/proc_holder/spell/aoe/knock
spell_type = /datum/spell/aoe/knock
category = "Mobility"
cost = 1
/datum/spellbook_entry/blink
name = "Blink"
spell_type = /obj/effect/proc_holder/spell/turf_teleport/blink
spell_type = /datum/spell/turf_teleport/blink
category = "Mobility"
/datum/spellbook_entry/jaunt
name = "Ethereal Jaunt"
spell_type = /obj/effect/proc_holder/spell/ethereal_jaunt
spell_type = /datum/spell/ethereal_jaunt
category = "Mobility"
/datum/spellbook_entry/spacetime_dist
name = "Spacetime Distortion"
spell_type = /obj/effect/proc_holder/spell/spacetime_dist
spell_type = /datum/spell/spacetime_dist
cost = 1 //Better defence than greater forcewall (maybe) but good luck hitting anyone, so 1 point.
category = "Mobility"
/datum/spellbook_entry/greaterknock
name = "Greater Knock"
spell_type = /obj/effect/proc_holder/spell/aoe/knock/greater
spell_type = /datum/spell/aoe/knock/greater
category = "Mobility"
refundable = 0 //global effect on cast
/datum/spellbook_entry/mindswap
name = "Mindswap"
spell_type = /obj/effect/proc_holder/spell/mind_transfer
spell_type = /datum/spell/mind_transfer
category = "Mobility"
/datum/spellbook_entry/teleport
name = "Teleport"
spell_type = /obj/effect/proc_holder/spell/area_teleport/teleport
spell_type = /datum/spell/area_teleport/teleport
category = "Mobility"
//Assistance
/datum/spellbook_entry/charge
name = "Charge"
spell_type = /obj/effect/proc_holder/spell/charge
spell_type = /datum/spell/charge
category = "Assistance"
cost = 1
/datum/spellbook_entry/summonitem
name = "Summon Item"
spell_type = /obj/effect/proc_holder/spell/summonitem
spell_type = /datum/spell/summonitem
category = "Assistance"
cost = 1
/datum/spellbook_entry/disguiseself
name = "Disguise Self"
spell_type = /obj/effect/proc_holder/spell/disguise_self
spell_type = /datum/spell/disguise_self
category = "Assistance"
cost = 1
/datum/spellbook_entry/noclothes
name = "Remove Clothes Requirement"
spell_type = /obj/effect/proc_holder/spell/noclothes
spell_type = /datum/spell/noclothes
category = "Assistance"
cost = 1
@@ -433,7 +433,7 @@
/datum/spellbook_entry/item/soulstones/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
. = ..()
if(.)
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe/conjure/construct(null))
user.mind.AddSpell(new /datum/spell/aoe/conjure/construct(null))
return .
/datum/spellbook_entry/item/wands
@@ -676,7 +676,7 @@
new path(B)
user.put_in_hands(B)
for(var/path in spells_path)
var/obj/effect/proc_holder/spell/S = new path()
var/datum/spell/S = new path()
LearnSpell(user, book, S)
OnBuy(user, book)
return TRUE
@@ -946,7 +946,7 @@
//Single Use Spellbooks
/obj/item/spellbook/oneuse
var/spell = /obj/effect/proc_holder/spell/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic
var/spell = /datum/spell/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic
var/spellname = "sandbox"
var/used = FALSE
name = "spellbook of "
@@ -961,8 +961,8 @@
return
/obj/item/spellbook/oneuse/attack_self(mob/user)
var/obj/effect/proc_holder/spell/S = new spell
for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list)
var/datum/spell/S = new spell
for(var/datum/spell/knownspell in user.mind.spell_list)
if(knownspell.type == S.type)
if(user.mind)
if(user.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE || user.mind.special_role == SPECIAL_ROLE_WIZARD)
@@ -990,7 +990,7 @@
return
/obj/item/spellbook/oneuse/fireball
spell = /obj/effect/proc_holder/spell/fireball
spell = /datum/spell/fireball
spellname = "fireball"
icon_state = "bookfireball"
desc = "This book feels warm to the touch."
@@ -1001,7 +1001,7 @@
qdel(src)
/obj/item/spellbook/oneuse/smoke
spell = /obj/effect/proc_holder/spell/smoke
spell = /datum/spell/smoke
spellname = "smoke"
icon_state = "booksmoke"
desc = "This book is overflowing with the dank arts."
@@ -1012,7 +1012,7 @@
user.adjust_nutrition(-200)
/obj/item/spellbook/oneuse/blind
spell = /obj/effect/proc_holder/spell/trigger/blind
spell = /datum/spell/genetic/blind
spellname = "blind"
icon_state = "bookblind"
desc = "This book looks blurry, no matter how you look at it."
@@ -1025,7 +1025,7 @@
L.EyeBlind(20 SECONDS)
/obj/item/spellbook/oneuse/mindswap
spell = /obj/effect/proc_holder/spell/mind_transfer
spell = /datum/spell/mind_transfer
spellname = "mindswap"
icon_state = "bookmindswap"
desc = "This book's cover is pristine, though its pages look ragged and torn."
@@ -1049,7 +1049,7 @@
to_chat(user, "<span class='notice'>You stare at the book some more, but there doesn't seem to be anything else to learn...</span>")
return
var/obj/effect/proc_holder/spell/mind_transfer/swapper = new
var/datum/spell/mind_transfer/swapper = new
swapper.cast(user, stored_swap)
to_chat(stored_swap, "<span class='warning'>You're suddenly somewhere else... and someone else?!</span>")
@@ -1057,7 +1057,7 @@
stored_swap = null
/obj/item/spellbook/oneuse/forcewall
spell = /obj/effect/proc_holder/spell/forcewall
spell = /datum/spell/forcewall
spellname = "forcewall"
icon_state = "bookforcewall"
desc = "This book has a dedication to mimes everywhere inside the front cover."
@@ -1070,7 +1070,7 @@
user.drop_item()
/obj/item/spellbook/oneuse/knock
spell = /obj/effect/proc_holder/spell/aoe/knock
spell = /datum/spell/aoe/knock
spellname = "knock"
icon_state = "bookknock"
desc = "This book is hard to hold closed properly."
@@ -1081,7 +1081,7 @@
user.Weaken(40 SECONDS)
/obj/item/spellbook/oneuse/horsemask
spell = /obj/effect/proc_holder/spell/horsemask
spell = /datum/spell/horsemask
spellname = "horses"
icon_state = "bookhorses"
desc = "This book is more horse than your mind has room for."
@@ -1101,7 +1101,7 @@
to_chat(user, "<span class='notice'>I say thee neigh</span>")
/obj/item/spellbook/oneuse/charge
spell = /obj/effect/proc_holder/spell/charge
spell = /datum/spell/charge
spellname = "charging"
icon_state = "bookcharge"
desc = "This book is made of 100% post-consumer wizard."
@@ -1112,7 +1112,7 @@
empulse(src, 1, 1)
/obj/item/spellbook/oneuse/summonitem
spell = /obj/effect/proc_holder/spell/summonitem
spell = /datum/spell/summonitem
spellname = "instant summons"
icon_state = "booksummons"
desc = "This book is bright and garish, very hard to miss."
@@ -1123,13 +1123,13 @@
qdel(src)
/obj/item/spellbook/oneuse/fake_gib
spell = /obj/effect/proc_holder/spell/touch/fake_disintegrate
spell = /datum/spell/touch/fake_disintegrate
spellname = "disintegrate"
icon_state = "bookfireball"
desc = "This book feels like it will rip stuff apart."
/obj/item/spellbook/oneuse/sacredflame
spell = /obj/effect/proc_holder/spell/sacred_flame
spell = /datum/spell/sacred_flame
spellname = "sacred flame"
icon_state = "booksacredflame"
desc = "Become one with the flames that burn within... and invite others to do so as well."
+2 -2
View File
@@ -122,13 +122,13 @@
/mob/proc/spellremove()
if(!mind)
return
for(var/obj/effect/proc_holder/spell/spell_to_remove in mind.spell_list)
for(var/datum/spell/spell_to_remove in mind.spell_list)
qdel(spell_to_remove)
mind.spell_list -= spell_to_remove
//To batch-remove mob spells.
/mob/proc/mobspellremove()
for(var/obj/effect/proc_holder/spell/spell_to_remove in mob_spell_list)
for(var/datum/spell/spell_to_remove in mob_spell_list)
qdel(spell_to_remove)
mob_spell_list -= spell_to_remove
+17 -17
View File
@@ -7,8 +7,8 @@
Ethereal Jaunt and Blink provide escape and mobility, while Magic Missile and Disintegrate can be used together for dangerous or key targets. <br> \
As this set lacks any form of healing or resurrection, healing items should be acquired from the station, and you should be careful to avoid being hurt in the first place. <br><br> \
</i>Provides Mutate, Ethereal Jaunt, Blink, Magic Missile, and Disintegrate.<i>"
spells_path = list(/obj/effect/proc_holder/spell/genetic/mutate, /obj/effect/proc_holder/spell/ethereal_jaunt, /obj/effect/proc_holder/spell/turf_teleport/blink, \
/obj/effect/proc_holder/spell/projectile/magic_missile, /obj/effect/proc_holder/spell/touch/disintegrate)
spells_path = list(/datum/spell/genetic/mutate, /datum/spell/ethereal_jaunt, /datum/spell/turf_teleport/blink, \
/datum/spell/projectile/magic_missile, /datum/spell/touch/disintegrate)
/datum/spellbook_entry/loadout/lich
name = "Defense Focus - Lich"
@@ -16,8 +16,8 @@
Ethereal Jaunt provides escape, Fireball and Rod Form are your offensive spells, and Disable Tech and Forcewall provides utility in disabling sec equipment or blocking their path. <br> \
Care should be taken in hiding the item you choose as your phylactery after using Bind Soul, as you cannot revive if it destroyed or too far from your body! <br><br> \
</i>Provides Bind Soul, Ethereal Jaunt, Fireball, Rod Form, Disable Tech, and Greater Forcewall.<i>"
spells_path = list(/obj/effect/proc_holder/spell/lichdom, /obj/effect/proc_holder/spell/ethereal_jaunt, /obj/effect/proc_holder/spell/fireball, \
/obj/effect/proc_holder/spell/rod_form, /obj/effect/proc_holder/spell/emplosion/disable_tech, /obj/effect/proc_holder/spell/forcewall)
spells_path = list(/datum/spell/lichdom, /datum/spell/ethereal_jaunt, /datum/spell/fireball, \
/datum/spell/rod_form, /datum/spell/emplosion/disable_tech, /datum/spell/forcewall)
is_ragin_restricted = TRUE
/datum/spellbook_entry/loadout/wands
@@ -27,8 +27,8 @@
Do not lose any of your wands to the station's crew, as they are extremely deadly even in their hands. Remember that the Revive wand can be used on yourself for a full heal! <br><br> \
</i>Provides a Belt of Wands, Charge, Ethereal Jaunt, Blink, Repulse, and Disintegrate.<i>"
items_path = list(/obj/item/storage/belt/wands/full)
spells_path = list(/obj/effect/proc_holder/spell/charge, /obj/effect/proc_holder/spell/ethereal_jaunt, /obj/effect/proc_holder/spell/turf_teleport/blink, \
/obj/effect/proc_holder/spell/aoe/repulse, /obj/effect/proc_holder/spell/touch/disintegrate)
spells_path = list(/datum/spell/charge, /datum/spell/ethereal_jaunt, /datum/spell/turf_teleport/blink, \
/datum/spell/aoe/repulse, /datum/spell/touch/disintegrate)
//Unique loadouts, which are more gimmicky. Should contain some unique spell or item that separates it from just buying standard wiz spells, and be balanced around a 10 spell point cost.
/datum/spellbook_entry/loadout/mimewiz
@@ -37,14 +37,14 @@
</i>Provides Finger Gun and Invisible Greater Wall manuals, Mime Robes, a Cane and Duct Tape, Ethereal Jaunt, Blink, Teleport, Mime Malaise, Knock, and Stop Time.<i>"
items_path = list(/obj/item/spellbook/oneuse/mime/fingergun, /obj/item/spellbook/oneuse/mime/greaterwall, /obj/item/clothing/suit/wizrobe/mime, /obj/item/clothing/head/wizard/mime, \
/obj/item/clothing/mask/gas/mime/wizard, /obj/item/clothing/shoes/sandal/marisa, /obj/item/cane, /obj/item/stack/tape_roll)
spells_path = list(/obj/effect/proc_holder/spell/ethereal_jaunt, /obj/effect/proc_holder/spell/turf_teleport/blink, /obj/effect/proc_holder/spell/area_teleport/teleport, \
/obj/effect/proc_holder/spell/touch/mime_malaise, /obj/effect/proc_holder/spell/aoe/knock, /obj/effect/proc_holder/spell/aoe/conjure/timestop)
spells_path = list(/datum/spell/ethereal_jaunt, /datum/spell/turf_teleport/blink, /datum/spell/area_teleport/teleport, \
/datum/spell/touch/mime_malaise, /datum/spell/aoe/knock, /datum/spell/aoe/conjure/timestop)
category = "Unique"
destroy_spellbook = TRUE
/datum/spellbook_entry/loadout/mimewiz/OnBuy(mob/living/carbon/human/user, obj/item/spellbook/book)
if(user.mind)
user.mind.AddSpell(new /obj/effect/proc_holder/spell/mime/speak(null))
user.mind.AddSpell(new /datum/spell/mime/speak(null))
user.mind.miming = TRUE
/datum/spellbook_entry/loadout/gunreaper
@@ -54,13 +54,13 @@
You will likely need to scavenge additional ammo or weapons aboard the station. <br><br>\
</i>Provides a .357 Revolver, 4 speedloaders of ammo, Ethereal Jaunt, Blink, Summon Item, No Clothes, and Bind Soul, with a unique outfit.<i>"
items_path = list(/obj/item/gun/projectile/revolver, /obj/item/ammo_box/a357, /obj/item/ammo_box/a357, /obj/item/ammo_box/a357, /obj/item/ammo_box/a357, /obj/item/clothing/under/syndicate)
spells_path = list(/obj/effect/proc_holder/spell/ethereal_jaunt, /obj/effect/proc_holder/spell/turf_teleport/blink, \
/obj/effect/proc_holder/spell/summonitem, /obj/effect/proc_holder/spell/noclothes, /obj/effect/proc_holder/spell/lichdom/gunslinger)
spells_path = list(/datum/spell/ethereal_jaunt, /datum/spell/turf_teleport/blink, \
/datum/spell/summonitem, /datum/spell/noclothes, /datum/spell/lichdom/gunslinger)
category = "Unique"
destroy_spellbook = TRUE
is_ragin_restricted = TRUE
/obj/effect/proc_holder/spell/lichdom/gunslinger/equip_lich(mob/living/carbon/human/H)
/datum/spell/lichdom/gunslinger/equip_lich(mob/living/carbon/human/H)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/det_suit(H), SLOT_HUD_OUTER_SUIT)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), SLOT_HUD_SHOES)
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(H), SLOT_HUD_GLOVES)
@@ -74,8 +74,8 @@
</i>Provides His Grace, an Ancient Jumpsuit, an Assistant ID, a Gas Mask and Shoes, Insulated Gloves, a full Toolbelt, Ethereal Jaunt, Force Wall, Homing Toolbox, Knock and No Clothes.<i>"
items_path = list(/obj/item/his_grace, /obj/item/clothing/under/color/grey/glorf, /obj/item/clothing/mask/gas, /obj/item/clothing/shoes/black, \
/obj/item/clothing/gloves/color/yellow, /obj/item/storage/belt/utility/full/multitool)
spells_path = list(/obj/effect/proc_holder/spell/ethereal_jaunt, /obj/effect/proc_holder/spell/forcewall, \
/obj/effect/proc_holder/spell/aoe/knock, /obj/effect/proc_holder/spell/noclothes, /obj/effect/proc_holder/spell/fireball/toolbox)
spells_path = list(/datum/spell/ethereal_jaunt, /datum/spell/forcewall, \
/datum/spell/aoe/knock, /datum/spell/noclothes, /datum/spell/fireball/toolbox)
category = "Unique"
destroy_spellbook = TRUE
@@ -111,8 +111,8 @@
items_path = list(/obj/item/supermatter_halberd, /obj/item/clothing/gloves/color/white/supermatter_immune, \
/obj/item/clothing/suit/hooded/oblivion, /obj/item/clothing/mask/gas/voice_modulator/oblivion, /obj/item/tank/internals/emergency_oxygen/double, \
/obj/item/clothing/under/color/white/enforcer, /obj/item/clothing/shoes/white/enforcer)
spells_path = list(/obj/effect/proc_holder/spell/summonitem, /obj/effect/proc_holder/spell/charge_up/bounce/lightning, \
/obj/effect/proc_holder/spell/aoe/conjure/summon_supermatter)
spells_path = list(/datum/spell/summonitem, /datum/spell/charge_up/bounce/lightning, \
/datum/spell/aoe/conjure/summon_supermatter)
category = "Unique"
destroy_spellbook = TRUE
@@ -125,7 +125,7 @@
name = "Fireball. Fireball. Fireball."
desc = "Who cares about the rest of the spells. Become an expert in fire magic. Devote yourself to the craft. The only spell you need anyways is <b>Fireball.</b><br>\
</i>Provides fire immunity, homing fireballs, rapid-fire fireballs, and some fireball wands. Provides no mobility spells. Replaces your robes with infernal versions.<i>"
spells_path = list(/obj/effect/proc_holder/spell/sacred_flame, /obj/effect/proc_holder/spell/fireball/homing, /obj/effect/proc_holder/spell/infinite_guns/fireball)
spells_path = list(/datum/spell/sacred_flame, /datum/spell/fireball/homing, /datum/spell/infinite_guns/fireball)
category = "Unique"
destroy_spellbook = TRUE