Revert "PLASE"

This reverts commit 8af225e6e3.
This commit is contained in:
Fermi
2019-11-24 03:06:08 +00:00
parent aeb8606ce0
commit ba4fa1ef67
2145 changed files with 1387321 additions and 5257 deletions
+547
View File
@@ -0,0 +1,547 @@
#define TARGET_CLOSEST 1
#define TARGET_RANDOM 2
/obj/effect/proc_holder
var/panel = "Debug"//What panel the proc holder needs to go on.
var/active = FALSE //Used by toggle based abilities.
var/ranged_mousepointer
var/mob/living/ranged_ability_user
var/ranged_clickcd_override = -1
var/has_action = TRUE
var/datum/action/spell_action/action = null
var/action_icon = 'icons/mob/actions/actions_spells.dmi'
var/action_icon_state = "spell_default"
var/action_background_icon_state = "bg_spell"
var/base_action = /datum/action/spell_action
/obj/effect/proc_holder/Initialize()
. = ..()
if(has_action)
action = new base_action(src)
/obj/effect/proc_holder/proc/on_gain(mob/living/user)
return
/obj/effect/proc_holder/proc/on_lose(mob/living/user)
return
/obj/effect/proc_holder/proc/fire(mob/living/user)
return TRUE
/obj/effect/proc_holder/proc/get_panel_text()
return ""
GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for the badmin verb for now
/obj/effect/proc_holder/Destroy()
if(ranged_ability_user)
remove_ranged_ability()
return ..()
/obj/effect/proc_holder/singularity_act()
return
/obj/effect/proc_holder/singularity_pull()
return
/obj/effect/proc_holder/proc/InterceptClickOn(mob/living/caller, params, atom/A)
if(caller.ranged_ability != src || ranged_ability_user != caller) //I'm not actually sure how these would trigger, but, uh, safety, I guess?
to_chat(caller, "<span class='warning'><b>[caller.ranged_ability.name]</b> has been disabled.</span>")
caller.ranged_ability.remove_ranged_ability()
return TRUE //TRUE for failed, FALSE for passed.
if(ranged_clickcd_override >= 0)
ranged_ability_user.next_click = world.time + ranged_clickcd_override
else
ranged_ability_user.next_click = world.time + CLICK_CD_CLICK_ABILITY
ranged_ability_user.face_atom(A)
return FALSE
/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, msg, forced)
if(!user || !user.client)
return
if(user.ranged_ability && user.ranged_ability != src)
if(forced)
to_chat(user, "<span class='warning'><b>[user.ranged_ability.name]</b> has been replaced by <b>[name]</b>.</span>")
user.ranged_ability.remove_ranged_ability()
else
return
user.ranged_ability = src
user.click_intercept = src
user.update_mouse_pointer()
ranged_ability_user = user
if(msg)
to_chat(ranged_ability_user, msg)
active = TRUE
update_icon()
/obj/effect/proc_holder/proc/remove_ranged_ability(msg)
if(!ranged_ability_user || !ranged_ability_user.client || (ranged_ability_user.ranged_ability && ranged_ability_user.ranged_ability != src)) //To avoid removing the wrong ability
return
ranged_ability_user.ranged_ability = null
ranged_ability_user.click_intercept = null
ranged_ability_user.update_mouse_pointer()
if(msg)
to_chat(ranged_ability_user, msg)
ranged_ability_user = null
active = FALSE
update_icon()
/obj/effect/proc_holder/spell
name = "Spell"
desc = "A wizard spell."
panel = "Spells"
var/sound = null //The sound the spell makes when it is cast
anchored = TRUE // Crap like fireball projectiles are proc_holders, this is needed so fireballs don't get blown back into your face via atmos etc.
pass_flags = PASSTABLE
density = FALSE
opacity = 0
var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit?
var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that
var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges"
var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges"
var/still_recharging_msg = "<span class='notice'>The spell is still recharging.</span>"
var/recharging = TRUE
var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var"
var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used
var/clothes_req = 1 //see if it requires clothes
var/cult_req = 0 //SPECIAL SNOWFLAKE clothes required for cult only spells
var/human_req = 0 //spell can only be cast by humans
var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities
var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells
var/phase_allowed = 0 // If true, the spell can be cast while phased, eg. blood crawling, ethereal jaunting
var/antimagic_allowed = TRUE // If false, the spell cannot be cast while under the effect of antimagic
var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell
var/invocation_emote_self = null
var/invocation_type = "none" //can be none, whisper, emote and shout
var/range = 7 //the range of the spell; outer radius for aoe spells
var/message = "" //whatever it says to the guy affected by it
var/selection_type = "view" //can be "range" or "view"
var/spell_level = 0 //if a spell can be taken multiple times, this raises
var/level_max = 4 //The max possible level_max is 4
var/cooldown_min = 0 //This defines what spell quickened four times has as a cooldown. Make sure to set this for every spell
var/player_lock = 1 //If it can be used by simple mobs
var/overlay = 0
var/overlay_icon = 'icons/obj/wizard.dmi'
var/overlay_icon_state = "spell"
var/overlay_lifespan = 0
var/sparks_spread = 0
var/sparks_amt = 0 //cropped at 10
var/smoke_spread = 0 //1 - harmless, 2 - harmful
var/smoke_amt = 0 //cropped at 10
var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2
action_icon = 'icons/mob/actions/actions_spells.dmi'
action_icon_state = "spell_default"
action_background_icon_state = "bg_spell"
base_action = /datum/action/spell_action/spell
/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0,mob/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell
if(player_lock)
if(!user.mind || !(src in user.mind.spell_list) && !(src in user.mob_spell_list))
to_chat(user, "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>")
return FALSE
else
if(!(src in user.mob_spell_list))
return FALSE
var/turf/T = get_turf(user)
if(is_centcom_level(T.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
to_chat(user, "<span class='notice'>You can't cast this spell here.</span>")
return FALSE
if(!skipcharge)
if(!charge_check(user))
return FALSE
if(user.stat && !stat_allowed)
to_chat(user, "<span class='notice'>Not when you're incapacitated.</span>")
return FALSE
if(!antimagic_allowed)
var/antimagic = user.anti_magic_check(TRUE, FALSE, chargecost = 0, self = TRUE)
if(antimagic)
if(isitem(antimagic))
to_chat(user, "<span class='notice'>[antimagic] is interfering with your magic.</span>")
else
to_chat(user, "<span class='notice'>Magic seems to flee from you, you can't gather enough power to cast this spell.</span>")
return FALSE
if(!phase_allowed && istype(user.loc, /obj/effect/dummy))
to_chat(user, "<span class='notice'>[name] cannot be cast unless you are completely manifested in the material plane.</span>")
return FALSE
if(ishuman(user))
var/mob/living/carbon/human/H = user
if((invocation_type == "whisper" || invocation_type == "shout") && !H.can_speak_vocal())
to_chat(user, "<span class='notice'>You can't get the words out!</span>")
return FALSE
var/list/casting_clothes = typecacheof(list(/obj/item/clothing/suit/wizrobe,
/obj/item/clothing/suit/space/hardsuit/wizard,
/obj/item/clothing/head/wizard,
/obj/item/clothing/head/helmet/space/hardsuit/wizard,
/obj/item/clothing/suit/space/hardsuit/shielded/wizard,
/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard))
if(clothes_req) //clothes check
if(!is_type_in_typecache(H.wear_suit, casting_clothes))
to_chat(H, "<span class='notice'>I don't feel strong enough without my robe.</span>")
return FALSE
if(!is_type_in_typecache(H.head, casting_clothes))
to_chat(H, "<span class='notice'>I don't feel strong enough without my hat.</span>")
return FALSE
if(cult_req) //CULT_REQ CLOTHES CHECK
if(!istype(H.wear_suit, /obj/item/clothing/suit/magusred) && !istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/cult))
to_chat(H, "<span class='notice'>I don't feel strong enough without my armor.</span>")
return FALSE
if(!istype(H.head, /obj/item/clothing/head/magus) && !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/cult))
to_chat(H, "<span class='notice'>I don't feel strong enough without my helmet.</span>")
return FALSE
else
if(clothes_req || human_req)
to_chat(user, "<span class='notice'>This spell can only be cast by humans!</span>")
return FALSE
if(nonabstract_req && (isbrain(user) || ispAI(user)))
to_chat(user, "<span class='notice'>This spell can only be cast by physical beings!</span>")
return FALSE
if(!skipcharge)
switch(charge_type)
if("recharge")
charge_counter = 0 //doesn't start recharging until the targets selecting ends
if("charges")
charge_counter-- //returns the charge if the targets selecting fails
if("holdervar")
adjust_var(user, holder_var_type, holder_var_amount)
if(action)
action.UpdateButtonIcon()
return 1
/obj/effect/proc_holder/spell/proc/charge_check(mob/user, silent = FALSE)
switch(charge_type)
if("recharge")
if(charge_counter < charge_max)
if(!silent)
to_chat(user, still_recharging_msg)
return FALSE
if("charges")
if(!charge_counter)
if(!silent)
to_chat(user, "<span class='notice'>[name] has no charges left.</span>")
return FALSE
return TRUE
/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount
switch(invocation_type)
if("shout")
if(prob(50))//Auto-mute? Fuck that noise
user.say(invocation, forced = "spell")
else
user.say(replacetext(invocation," ","`"), forced = "spell")
if("whisper")
if(prob(50))
user.whisper(invocation)
else
user.whisper(replacetext(invocation," ","`"))
if("emote")
user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm
/obj/effect/proc_holder/spell/proc/playMagSound()
playsound(get_turf(usr), sound,50,1)
/obj/effect/proc_holder/spell/Initialize()
. = ..()
START_PROCESSING(SSfastprocess, src)
still_recharging_msg = "<span class='notice'>[name] is still recharging.</span>"
charge_counter = charge_max
/obj/effect/proc_holder/spell/Destroy()
STOP_PROCESSING(SSfastprocess, src)
qdel(action)
return ..()
/obj/effect/proc_holder/spell/Click()
if(cast_check())
choose_targets()
return 1
/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf
return
/obj/effect/proc_holder/spell/proc/can_target(mob/living/target)
return TRUE
/obj/effect/proc_holder/spell/proc/start_recharge()
recharging = TRUE
/obj/effect/proc_holder/spell/process()
if(recharging && charge_type == "recharge" && (charge_counter < charge_max))
charge_counter += 2 //processes 5 times per second instead of 10.
if(charge_counter >= charge_max)
action.UpdateButtonIcon()
charge_counter = charge_max
recharging = FALSE
/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = TRUE, mob/user = usr) //if recharge is started is important for the trigger spells
before_cast(targets)
invocation(user)
if(user && user.ckey)
user.log_message("<span class='danger'>cast the spell [name].</span>", LOG_ATTACK)
if(recharge)
recharging = TRUE
if(sound)
playMagSound()
cast(targets,user=user)
after_cast(targets)
if(action)
action.UpdateButtonIcon()
/obj/effect/proc_holder/spell/proc/before_cast(list/targets)
if(overlay)
for(var/atom/target in targets)
var/location
if(isliving(target))
location = target.loc
else if(isturf(target))
location = target
var/obj/effect/overlay/spell = new /obj/effect/overlay(location)
spell.icon = overlay_icon
spell.icon_state = overlay_icon_state
spell.anchored = TRUE
spell.density = FALSE
QDEL_IN(spell, overlay_lifespan)
/obj/effect/proc_holder/spell/proc/after_cast(list/targets)
for(var/atom/target in targets)
var/location
if(isliving(target))
location = target.loc
else if(isturf(target))
location = target
if(isliving(target) && message)
to_chat(target, text("[message]"))
if(sparks_spread)
do_sparks(sparks_amt, FALSE, location)
if(smoke_spread)
if(smoke_spread == 1)
var/datum/effect_system/smoke_spread/smoke = new
smoke.set_up(smoke_amt, location)
smoke.start()
else if(smoke_spread == 2)
var/datum/effect_system/smoke_spread/bad/smoke = new
smoke.set_up(smoke_amt, location)
smoke.start()
else if(smoke_spread == 3)
var/datum/effect_system/smoke_spread/sleeping/smoke = new
smoke.set_up(smoke_amt, location)
smoke.start()
/obj/effect/proc_holder/spell/proc/cast(list/targets,mob/user = usr)
return
/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge
switch(charge_type)
if("recharge")
charge_counter = charge_max
if("charges")
charge_counter++
if("holdervar")
adjust_var(user, holder_var_type, -holder_var_amount)
if(action)
action.UpdateButtonIcon()
/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types
if (!istype(target))
return
switch(type)
if("bruteloss")
target.adjustBruteLoss(amount)
if("fireloss")
target.adjustFireLoss(amount)
if("toxloss")
target.adjustToxLoss(amount)
if("oxyloss")
target.adjustOxyLoss(amount)
if("stun")
target.AdjustStun(amount)
if("knockdown")
target.AdjustKnockdown(amount)
if("unconscious")
target.AdjustUnconscious(amount)
else
target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existent vars
/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob
var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range
var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast
var/include_user = 0 //if it includes usr in the target list
var/random_target = 0 // chooses random viable target instead of asking the caster
var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target
/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends)
var/inner_radius = -1 //for all your ring spell needs
/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr)
var/list/targets = list()
switch(max_targets)
if(0) //unlimited
for(var/mob/living/target in view_or_range(range, user, selection_type))
if(!can_target(target))
continue
targets += target
if(1) //single target can be picked
if(range < 0)
targets += user
else
var/possible_targets = list()
for(var/mob/living/M in view_or_range(range, user, selection_type))
if(!include_user && user == M)
continue
if(!can_target(M))
continue
possible_targets += M
//targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets
//Adds a safety check post-input to make sure those targets are actually in range.
var/mob/M
if(!random_target)
M = input("Choose the target for the spell.", "Targeting") as null|mob in possible_targets
else
switch(random_target_priority)
if(TARGET_RANDOM)
M = pick(possible_targets)
if(TARGET_CLOSEST)
for(var/mob/living/L in possible_targets)
if(M)
if(get_dist(user,L) < get_dist(user,M))
if(los_check(user,L))
M = L
else
if(los_check(user,L))
M = L
if(M in view_or_range(range, user, selection_type))
targets += M
else
var/list/possible_targets = list()
for(var/mob/living/target in view_or_range(range, user, selection_type))
if(!can_target(target))
continue
possible_targets += target
for(var/i=1,i<=max_targets,i++)
if(!possible_targets.len)
break
if(target_ignore_prev)
var/target = pick(possible_targets)
possible_targets -= target
targets += target
else
targets += pick(possible_targets)
if(!include_user && (user in targets))
targets -= user
if(!targets.len) //doesn't waste the spell
revert_cast(user)
return
perform(targets,user=user)
/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr)
var/list/targets = list()
for(var/turf/target in view_or_range(range,user,selection_type))
if(!can_target(target))
continue
if(!(target in view_or_range(inner_radius,user,selection_type)))
targets += target
if(!targets.len) //doesn't waste the spell
revert_cast()
return
perform(targets,user=user)
/obj/effect/proc_holder/spell/proc/updateButtonIcon(status_only, force)
action.UpdateButtonIcon(status_only, force)
/obj/effect/proc_holder/spell/proc/can_be_cast_by(mob/caster)
if((human_req || clothes_req) && !ishuman(caster))
return 0
return 1
/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B)
//Checks for obstacles from A to B
var/obj/dummy = new(A.loc)
dummy.pass_flags |= PASSTABLE
for(var/turf/turf in getline(A,B))
for(var/atom/movable/AM in turf)
if(!AM.CanPass(dummy,turf,1))
qdel(dummy)
return 0
qdel(dummy)
return 1
/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr)
if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list))
return FALSE
if(!charge_check(user,TRUE))
return FALSE
if(user.stat && !stat_allowed)
return FALSE
if(!antimagic_allowed && user.anti_magic_check(TRUE, FALSE, chargecost = 0, self = TRUE))
return FALSE
if(!ishuman(user))
if(clothes_req || human_req)
return FALSE
if(nonabstract_req && (isbrain(user) || ispAI(user)))
return FALSE
return TRUE
/obj/effect/proc_holder/spell/self //Targets only the caster. Good for buffs and heals, but probably not wise for fireballs (although they usually fireball themselves anyway, honke)
range = -1 //Duh
/obj/effect/proc_holder/spell/self/choose_targets(mob/user = usr)
if(!user)
revert_cast()
return
perform(null,user=user)
/obj/effect/proc_holder/spell/self/basic_heal //This spell exists mainly for debugging purposes, and also to show how casting works
name = "Lesser Heal"
desc = "Heals a small amount of brute and burn damage."
human_req = 1
clothes_req = 0
charge_max = 100
cooldown_min = 50
invocation = "Victus sano!"
invocation_type = "whisper"
school = "restoration"
sound = 'sound/magic/staff_healing.ogg'
/obj/effect/proc_holder/spell/self/basic_heal/cast(mob/living/carbon/human/user) //Note the lack of "list/targets" here. Instead, use a "user" var depending on mob requirements.
//Also, notice the lack of a "for()" statement that looks through the targets. This is, again, because the spell can only have a single target.
user.visible_message("<span class='warning'>A wreath of gentle light passes over [user]!</span>", "<span class='notice'>You wreath yourself in healing light!</span>")
user.adjustBruteLoss(-10)
user.adjustFireLoss(-10)
@@ -0,0 +1,92 @@
/obj/effect/proc_holder/spell/targeted/area_teleport
name = "Area teleport"
desc = "This spell teleports you to a type of area of your selection."
nonabstract_req = 1
var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list
var/invocation_area = 1 //if the invocation appends the selected area
var/sound1 = 'sound/weapons/zapbang.ogg'
var/sound2 = 'sound/weapons/zapbang.ogg'
/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1,mob/living/user = usr)
var/thearea = before_cast(targets)
if(!thearea || !cast_check(1))
revert_cast()
return
invocation(thearea,user)
if(charge_type == "recharge" && recharge)
INVOKE_ASYNC(src, .proc/start_recharge)
cast(targets,thearea,user)
after_cast(targets)
/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets)
var/area/U = get_area(usr)
if(U.noteleport && !istype(U, /area/wizard_station)) // Wizard den special check for those complaining about being unable to tele on station.
to_chat(usr, "<span class='warning'>Unseen forces prevent you from casting this spell in this area</span>")
return
var/A
if(!randomise_selection)
A = input("Area to teleport to", "Teleport", A) as null|anything in GLOB.teleportlocs
else
A = pick(GLOB.teleportlocs)
if(!A)
return
var/area/thearea = GLOB.teleportlocs[A]
return thearea
/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea,mob/user = usr)
playsound(get_turf(user), sound1, 50,1)
for(var/mob/living/target in targets)
var/list/L = list()
for(var/turf/T in get_area_turfs(thearea.type))
if(!T.density)
var/clear = 1
for(var/obj/O in T)
if(O.density)
clear = 0
break
if(clear)
L+=T
if(!L.len)
to_chat(usr, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.")
return
if(target && target.buckled)
target.buckled.unbuckle_mob(target, force=1)
var/forcecheck = istype(get_area(target), /area/wizard_station)
var/list/tempL = L
var/attempt = null
var/success = 0
while(tempL.len)
attempt = pick(tempL)
do_teleport(target, attempt, channel = TELEPORT_CHANNEL_MAGIC, forced = forcecheck)
if(get_turf(target) == attempt)
success = 1
break
else
tempL.Remove(attempt)
if(!success)
do_teleport(target, L, forceMove = TRUE, channel = TELEPORT_CHANNEL_MAGIC, forced = forcecheck)
playsound(get_turf(user), sound2, 50,1)
return
/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null,mob/user = usr)
if(!invocation_area || !chosenarea)
..()
else
switch(invocation_type)
if("shout")
user.say("[invocation] [uppertext(chosenarea.name)]", forced = "spell")
if(user.gender==MALE)
playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1)
else
playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1)
if("whisper")
user.whisper("[invocation] [uppertext(chosenarea.name)]")
return
@@ -0,0 +1,322 @@
//////////////////////////////Construct Spells/////////////////////////
/obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser
charge_max = 1800
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "artificer"
action_background_icon_state = "bg_demon"
/obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser/cult
cult_req = 1
charge_max = 2500
/obj/effect/proc_holder/spell/aoe_turf/area_conversion
name = "Area Conversion"
desc = "This spell instantly converts a small area around you."
school = "transmutation"
charge_max = 50
clothes_req = 0
invocation = "none"
invocation_type = "none"
range = 2
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "areaconvert"
action_background_icon_state = "bg_cult"
/obj/effect/proc_holder/spell/aoe_turf/area_conversion/cast(list/targets, mob/user = usr)
playsound(get_turf(user), 'sound/items/welder.ogg', 75, 1)
for(var/turf/T in targets)
T.narsie_act(FALSE, TRUE, 100 - (get_dist(user, T) * 25))
/obj/effect/proc_holder/spell/aoe_turf/conjure/floor
name = "Summon Cult Floor"
desc = "This spell constructs a cult floor."
school = "conjuration"
charge_max = 20
clothes_req = 0
invocation = "none"
invocation_type = "none"
range = 0
summon_type = list(/turf/open/floor/engine/cult)
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "floorconstruct"
action_background_icon_state = "bg_cult"
/obj/effect/proc_holder/spell/aoe_turf/conjure/wall
name = "Summon Cult Wall"
desc = "This spell constructs a cult wall."
school = "conjuration"
charge_max = 100
clothes_req = 0
invocation = "none"
invocation_type = "none"
range = 0
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "lesserconstruct"
action_background_icon_state = "bg_cult"
summon_type = list(/turf/closed/wall/mineral/cult/artificer) //we don't want artificer-based runed metal farms
/obj/effect/proc_holder/spell/aoe_turf/conjure/wall/reinforced
name = "Greater Construction"
desc = "This spell constructs a reinforced metal wall."
school = "conjuration"
charge_max = 300
clothes_req = 0
invocation = "none"
invocation_type = "none"
range = 0
summon_type = list(/turf/closed/wall/r_wall)
/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone
name = "Summon Soulstone"
desc = "This spell reaches into Nar'Sie's realm, summoning one of the legendary fragments across time and space."
school = "conjuration"
charge_max = 2400
clothes_req = 0
invocation = "none"
invocation_type = "none"
range = 0
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "summonsoulstone"
action_background_icon_state = "bg_demon"
summon_type = list(/obj/item/soulstone)
/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone/cult
cult_req = 1
charge_max = 3600
/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone/noncult
summon_type = list(/obj/item/soulstone/anybody)
/obj/effect/proc_holder/spell/targeted/forcewall/cult
name = "Shield"
desc = "This spell creates a temporary forcefield to shield yourself and allies from incoming fire."
school = "transmutation"
charge_max = 400
clothes_req = FALSE
invocation = "none"
invocation_type = "none"
wall_type = /obj/effect/forcefield/cult
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "cultforcewall"
action_background_icon_state = "bg_demon"
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift
name = "Phase Shift"
desc = "This spell allows you to pass through walls."
school = "transmutation"
charge_max = 250
clothes_req = 0
invocation = "none"
invocation_type = "none"
range = -1
include_user = 1
jaunt_duration = 50 //in deciseconds
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "phaseshift"
action_background_icon_state = "bg_demon"
jaunt_in_time = 12
jaunt_in_type = /obj/effect/temp_visual/dir_setting/wraith
jaunt_out_type = /obj/effect/temp_visual/dir_setting/wraith/out
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/jaunt_steam(mobloc)
return
/obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser
name = "Lesser Magic Missile"
desc = "This spell fires several, slow moving, magic projectiles at nearby targets."
school = "evocation"
charge_max = 400
clothes_req = 0
invocation = "none"
invocation_type = "none"
proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile/lesser"
proj_lifespan = 10
max_targets = 6
action_icon_state = "magicm"
action_background_icon_state = "bg_demon"
/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile/lesser
amt_knockdown = 84
/obj/effect/proc_holder/spell/targeted/smoke/disable
name = "Paralysing Smoke"
desc = "This spell spawns a cloud of paralysing smoke."
school = "conjuration"
charge_max = 200
clothes_req = 0
invocation = "none"
invocation_type = "none"
range = -1
include_user = 1
cooldown_min = 20 //25 deciseconds reduction per rank
smoke_spread = 3
smoke_amt = 4
action_icon_state = "smoke"
action_background_icon_state = "bg_cult"
/obj/effect/proc_holder/spell/targeted/abyssal_gaze
name = "Abyssal Gaze"
desc = "This spell instills a deep terror in your target, temporarily chilling and blinding it."
charge_max = 750
range = 5
include_user = FALSE
selection_type = "range"
stat_allowed = FALSE
school = "evocation"
clothes_req = FALSE
invocation = "none"
invocation_type = "none"
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_background_icon_state = "bg_demon"
action_icon_state = "abyssal_gaze"
/obj/effect/proc_holder/spell/targeted/abyssal_gaze/cast(list/targets, mob/user = usr)
if(!LAZYLEN(targets))
to_chat(user, "<span class='notice'>No target found in range.</span>")
revert_cast()
return
var/mob/living/carbon/target = targets[1]
if(!(target in oview(range)))
to_chat(user, "<span class='notice'>[target] is too far away!</span>")
revert_cast()
return
if(target.anti_magic_check(TRUE, TRUE))
to_chat(target, "<span class='warning'>You feel a freezing darkness closing in on you, but it rapidly dissipates.</span>")
return
to_chat(target, "<span class='userdanger'>A freezing darkness surrounds you...</span>")
target.playsound_local(get_turf(target), 'sound/hallucinations/i_see_you1.ogg', 50, 1)
user.playsound_local(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1)
target.become_blind(ABYSSAL_GAZE_BLIND)
addtimer(CALLBACK(src, .proc/cure_blindness, target), 40)
target.adjust_bodytemperature(-200)
/obj/effect/proc_holder/spell/targeted/abyssal_gaze/proc/cure_blindness(mob/target)
if(isliving(target))
var/mob/living/L = target
L.cure_blind(ABYSSAL_GAZE_BLIND)
/obj/effect/proc_holder/spell/targeted/dominate
name = "Dominate"
desc = "This spell dominates the mind of a lesser creature to the will of Nar'Sie, allying it only to her direct followers."
charge_max = 600
range = 7
include_user = FALSE
selection_type = "range"
stat_allowed = FALSE
school = "evocation"
clothes_req = FALSE
invocation = "none"
invocation_type = "none"
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_background_icon_state = "bg_demon"
action_icon_state = "dominate"
/obj/effect/proc_holder/spell/targeted/dominate/cast(list/targets, mob/user = usr)
if(!LAZYLEN(targets))
to_chat(user, "<span class='notice'>No target found in range.</span>")
revert_cast()
return
var/mob/living/simple_animal/S = targets[1]
if(S.ckey)
to_chat(user, "<span class='warning'>[S] is too intelligent to dominate!</span>")
revert_cast()
return
if(S.stat)
to_chat(user, "<span class='warning'>[S] is dead!</span>")
revert_cast()
return
if(S.sentience_type != SENTIENCE_ORGANIC)
to_chat(user, "<span class='warning'>[S] cannot be dominated!</span>")
revert_cast()
return
if(!(S in oview(range)))
to_chat(user, "<span class='notice'>[S] is too far away!</span>")
revert_cast()
return
S.add_atom_colour("#990000", FIXED_COLOUR_PRIORITY)
S.faction = list("cult")
playsound(get_turf(S), 'sound/effects/ghost.ogg', 100, 1)
new /obj/effect/temp_visual/cult/sac(get_turf(S))
/obj/effect/proc_holder/spell/targeted/dominate/can_target(mob/living/target)
if(!isanimal(target) || target.stat)
return FALSE
if("cult" in target.faction)
return FALSE
return TRUE
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/golem
charge_max = 800
jaunt_in_type = /obj/effect/temp_visual/dir_setting/cult/phase
jaunt_out_type = /obj/effect/temp_visual/dir_setting/cult/phase/out
/obj/effect/proc_holder/spell/dumbfire/juggernaut
name = "Gauntlet Echo"
desc = "Channels energy into your gauntlet - firing its essence forward in a slow moving, yet devastating, attack."
proj_icon_state = "cultfist"
proj_name = "gauntlet echo"
proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/juggernaut" //IMPORTANT use only subtypes of this
proj_lifespan = 15
proj_step_delay = 7
charge_max = 350
clothes_req = FALSE
action_icon = 'icons/mob/actions/actions_cult.dmi'
action_icon_state = "cultfist"
action_background_icon_state = "bg_demon"
sound = 'sound/weapons/resonator_blast.ogg'
proj_trigger_range = 0
ignore_factions = list("cult")
check_holy = TRUE
/obj/effect/proc_holder/spell/targeted/inflict_handler/juggernaut
name = "Gauntlet Echo"
alpha = 180
amt_dam_brute = 30
amt_knockdown = 84
amt_dam_stam = 30
sound = 'sound/weapons/punch3.ogg'
/obj/effect/proc_holder/spell/targeted/inflict_handler/juggernaut/cast(list/targets,mob/user = usr)
var/turf/T = get_turf(src)
playsound(T, 'sound/weapons/resonator_blast.ogg', 100, FALSE)
new /obj/effect/temp_visual/cult/sac(T)
for(var/obj/O in range(src,1))
if(O.density && !istype(O, /obj/structure/destructible/cult))
O.take_damage(90, BRUTE, "melee", 0)
new /obj/effect/temp_visual/cult/turf/floor
..()
@@ -0,0 +1,40 @@
/obj/effect/proc_holder/spell/targeted/forcewall
name = "Forcewall"
desc = "Create a magical barrier that only you can pass through. Does not require wizard garb."
school = "transmutation"
charge_max = 100
clothes_req = 0
invocation = "TARCOL MINTI ZHERI"
invocation_type = "shout"
sound = 'sound/magic/forcewall.ogg'
action_icon_state = "shield"
range = -1
include_user = 1
cooldown_min = 50 //12 deciseconds reduction per rank
var/wall_type = /obj/effect/forcefield/wizard
/obj/effect/proc_holder/spell/targeted/forcewall/cast(list/targets,mob/user = usr)
new wall_type(get_turf(user),user)
if(user.dir == SOUTH || user.dir == NORTH)
new wall_type(get_step(user, EAST),user)
new wall_type(get_step(user, WEST),user)
else
new wall_type(get_step(user, NORTH),user)
new wall_type(get_step(user, SOUTH),user)
/obj/effect/forcefield/wizard
var/mob/wizard
/obj/effect/forcefield/wizard/Initialize(mapload, mob/summoner)
. = ..()
wizard = summoner
/obj/effect/forcefield/wizard/CanPass(atom/movable/mover, turf/target)
if(mover == wizard)
return TRUE
if(ismob(mover))
var/mob/M = mover
if(M.anti_magic_check(chargecost = 0))
return TRUE
return FALSE
@@ -0,0 +1,57 @@
/obj/effect/proc_holder/spell/targeted/inflict_handler
name = "Inflict Handler"
desc = "This spell blinds and/or destroys/damages/heals and/or knockdowns/stuns the target."
var/amt_knockdown = 0
var/amt_hardstun
var/amt_unconscious = 0
var/amt_stun = 0
//set to negatives for healing
var/amt_dam_stam
var/amt_dam_fire = 0
var/amt_dam_brute = 0
var/amt_dam_oxy = 0
var/amt_dam_tox = 0
var/amt_eye_blind = 0
var/amt_eye_blurry = 0
var/destroys = "none" //can be "none", "gib" or "disintegrate"
var/summon_type = null //this will put an obj at the target's location
var/check_anti_magic = TRUE
var/check_holy = FALSE
/obj/effect/proc_holder/spell/targeted/inflict_handler/cast(list/targets,mob/user = usr)
for(var/mob/living/target in targets)
playsound(target,sound, 50,1)
if(target.anti_magic_check(check_anti_magic, check_holy))
return
switch(destroys)
if("gib")
target.gib()
if("disintegrate")
target.dust()
if(!target)
continue
//damage/healing
target.adjustBruteLoss(amt_dam_brute)
target.adjustFireLoss(amt_dam_fire)
target.adjustToxLoss(amt_dam_tox)
target.adjustOxyLoss(amt_dam_oxy)
//disabling
if(!amt_knockdown && amt_dam_stam)
target.adjustStaminaLoss(amt_dam_stam)
else
target.Knockdown(amt_knockdown, override_hardstun = amt_hardstun, override_stamdmg = amt_dam_stam)
target.Unconscious(amt_unconscious)
target.Stun(amt_stun)
target.blind_eyes(amt_eye_blind)
target.blur_eyes(amt_eye_blurry)
//summoning
if(summon_type)
new summon_type(target.loc, target)
@@ -0,0 +1,88 @@
/obj/effect/proc_holder/spell/targeted/mind_transfer
name = "Mind Transfer"
desc = "This spell allows the user to switch bodies with a target."
school = "transmutation"
charge_max = 600
clothes_req = 0
invocation = "GIN'YU CAPAN"
invocation_type = "whisper"
range = 1
cooldown_min = 200 //100 deciseconds reduction per rank
var/unconscious_amount_caster = 400 //how much the caster is stunned for after the spell
var/unconscious_amount_victim = 400 //how much the victim is stunned for after the spell
action_icon_state = "mindswap"
/*
Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do.
Make sure spells that are removed from spell_list are actually removed and deleted when mind transferring.
Also, you never added distance checking after target is selected. I've went ahead and did that.
*/
/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/living/user = usr, distanceoverride, silent = FALSE)
if(!targets.len)
if(!silent)
to_chat(user, "<span class='warning'>No mind found!</span>")
return
if(targets.len > 1)
if(!silent)
to_chat(user, "<span class='warning'>Too many minds! You're not a hive damnit!</span>")
return
var/mob/living/target = targets[1]
var/t_He = target.p_they(TRUE)
var/t_is = target.p_are()
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
if(!silent)
to_chat(user, "<span class='warning'>[t_He] [t_is] too far away!</span>")
return
if(ismegafauna(target))
if(!silent)
to_chat(user, "<span class='warning'>This creature is too powerful to control!</span>")
return
if(target.stat == DEAD)
if(!silent)
to_chat(user, "<span class='warning'>You don't particularly want to be dead!</span>")
return
if(!target.key || !target.mind)
if(!silent)
to_chat(user, "<span class='warning'>[t_He] appear[target.p_s()] to be catatonic! Not even magic can affect [target.p_their()] vacant mind.</span>")
return
if(user.suiciding)
if(!silent)
to_chat(user, "<span class='warning'>You're killing yourself! You can't concentrate enough to do this!</span>")
return
var/datum/mind/TM = target.mind
if((target.anti_magic_check(TRUE, FALSE) || TM.has_antag_datum(/datum/antagonist/wizard) || TM.has_antag_datum(/datum/antagonist/cult) || TM.has_antag_datum(/datum/antagonist/clockcult) || TM.has_antag_datum(/datum/antagonist/changeling) || TM.has_antag_datum(/datum/antagonist/rev)) || cmptext(copytext(target.key,1,2),"@"))
if(!silent)
to_chat(user, "<span class='warning'>[target.p_their(TRUE)] mind is resisting your spell!</span>")
return
var/mob/living/victim = target//The target of the spell whos body will be transferred to.
var/mob/living/caster = user//The wizard/whomever doing the body transferring.
//MIND TRANSFER BEGIN
var/mob/dead/observer/ghost = victim.ghostize(FALSE, TRUE)
caster.mind.transfer_to(victim)
ghost.mind.transfer_to(caster)
if(ghost.key)
ghost.transfer_ckey(caster) //have to transfer the key since the mind was not active
qdel(ghost)
//MIND TRANSFER END
//Here we knock both mobs out for a time.
caster.Unconscious(unconscious_amount_caster)
victim.Unconscious(unconscious_amount_victim)
SEND_SOUND(caster, sound('sound/magic/mandswap.ogg'))
SEND_SOUND(victim, sound('sound/magic/mandswap.ogg'))// only the caster and victim hear the sounds, that way no one knows for sure if the swap happened
return TRUE
@@ -0,0 +1,173 @@
/obj/effect/proc_holder/spell/targeted/shapeshift
name = "Shapechange"
desc = "Take on the shape of another for a time to use their natural abilities. Once you've made your choice it cannot be changed."
clothes_req = 0
human_req = 0
charge_max = 200
cooldown_min = 50
range = -1
include_user = 1
invocation = "RAC'WA NO!"
invocation_type = "shout"
action_icon_state = "shapeshift"
var/revert_on_death = TRUE
var/die_with_shapeshifted_form = TRUE
var/convert_damage = TRUE //If you want to convert the caster's health to the shift, and vice versa.
var/convert_damage_type = BRUTE //Since simplemobs don't have advanced damagetypes, what to convert damage back into.
var/shapeshift_type
var/list/possible_shapes = list(/mob/living/simple_animal/mouse,\
/mob/living/simple_animal/pet/dog/corgi,\
/mob/living/simple_animal/hostile/carp/ranged/chaos,\
/mob/living/simple_animal/bot/ed209,\
/mob/living/simple_animal/hostile/poison/giant_spider/hunter/viper,\
/mob/living/simple_animal/hostile/construct/armored)
/obj/effect/proc_holder/spell/targeted/shapeshift/cast(list/targets,mob/user = usr)
if(src in user.mob_spell_list)
user.mob_spell_list.Remove(src)
user.mind.AddSpell(src)
if(user.buckled)
user.buckled.unbuckle_mob(src,force=TRUE)
for(var/mob/living/M in targets)
if(!shapeshift_type)
var/list/animal_list = list()
for(var/path in possible_shapes)
var/mob/living/simple_animal/A = path
animal_list[initial(A.name)] = path
var/new_shapeshift_type = input(M, "Choose Your Animal Form!", "It's Morphing Time!", null) as null|anything in animal_list
if(shapeshift_type)
return
shapeshift_type = new_shapeshift_type
if(!shapeshift_type) //If you aren't gonna decide I am!
shapeshift_type = pick(animal_list)
shapeshift_type = animal_list[shapeshift_type]
var/obj/shapeshift_holder/S = locate() in M
if(S)
Restore(M)
else
Shapeshift(M)
/obj/effect/proc_holder/spell/targeted/shapeshift/proc/Shapeshift(mob/living/caster)
var/obj/shapeshift_holder/H = locate() in caster
if(H)
to_chat(caster, "<span class='warning'>You're already shapeshifted!</span>")
return
var/mob/living/shape = new shapeshift_type(caster.loc)
H = new(shape,src,caster)
clothes_req = 0
human_req = 0
/obj/effect/proc_holder/spell/targeted/shapeshift/proc/Restore(mob/living/shape)
var/obj/shapeshift_holder/H = locate() in shape
if(!H)
return
H.restore()
clothes_req = initial(clothes_req)
human_req = initial(human_req)
/obj/effect/proc_holder/spell/targeted/shapeshift/dragon
name = "Dragon Form"
desc = "Take on the shape a lesser ash drake."
invocation = "RAAAAAAAAWR!"
shapeshift_type = /mob/living/simple_animal/hostile/megafauna/dragon/lesser
/obj/shapeshift_holder
name = "Shapeshift holder"
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF
var/mob/living/stored
var/mob/living/shape
var/restoring = FALSE
var/datum/soullink/shapeshift/slink
var/obj/effect/proc_holder/spell/targeted/shapeshift/source
/obj/shapeshift_holder/Initialize(mapload,obj/effect/proc_holder/spell/targeted/shapeshift/source,mob/living/caster)
. = ..()
src.source = source
shape = loc
if(!istype(shape))
CRASH("shapeshift holder created outside mob/living")
stored = caster
if(stored.mind)
stored.mind.transfer_to(shape)
stored.forceMove(src)
stored.notransform = TRUE
if(source.convert_damage)
var/damage_percent = (stored.maxHealth - stored.health)/stored.maxHealth;
var/damapply = damage_percent * shape.maxHealth;
shape.apply_damage(damapply, source.convert_damage_type, forced = TRUE);
slink = soullink(/datum/soullink/shapeshift, stored , shape)
slink.source = src
/obj/shapeshift_holder/Destroy()
if(!restoring)
restore()
stored = null
shape = null
. = ..()
/obj/shapeshift_holder/Moved()
. = ..()
if(!restoring || QDELETED(src))
restore()
/obj/shapeshift_holder/handle_atom_del(atom/A)
if(A == stored && !restoring)
restore()
/obj/shapeshift_holder/Exited(atom/movable/AM)
if(AM == stored && !restoring)
restore()
/obj/shapeshift_holder/proc/casterDeath()
//Something kills the stored caster through direct damage.
if(source.revert_on_death)
restore(death=TRUE)
else
shape.death()
/obj/shapeshift_holder/proc/shapeDeath()
//Shape dies.
if(source.die_with_shapeshifted_form)
if(source.revert_on_death)
restore(death=TRUE)
else
restore()
/obj/shapeshift_holder/proc/restore(death=FALSE)
restoring = TRUE
qdel(slink)
stored.forceMove(get_turf(src))
stored.notransform = FALSE
if(shape.mind)
shape.mind.transfer_to(stored)
if(death)
stored.death()
else if(source.convert_damage)
stored.revive(full_heal = TRUE)
var/damage_percent = (shape.maxHealth - shape.health)/shape.maxHealth;
var/damapply = stored.maxHealth * damage_percent
stored.apply_damage(damapply, source.convert_damage_type, forced = TRUE)
qdel(shape)
qdel(src)
/datum/soullink/shapeshift
var/obj/shapeshift_holder/source
/datum/soullink/shapeshift/ownerDies(gibbed, mob/living/owner)
if(source)
source.casterDeath(gibbed)
/datum/soullink/shapeshift/sharerDies(gibbed, mob/living/sharer)
if(source)
source.shapeDeath(gibbed)
@@ -0,0 +1,123 @@
/obj/effect/proc_holder/spell/spacetime_dist
name = "Spacetime Distortion"
desc = "Entangle the strings of spacetime to deny easy movement around you. The strings vibrate..."
charge_max = 300
var/duration = 150
range = 7
var/list/effects
var/ready = TRUE
centcom_cancast = FALSE
sound = 'sound/effects/magic.ogg'
cooldown_min = 300
level_max = 0
/obj/effect/proc_holder/spell/spacetime_dist/can_cast(mob/user = usr)
if(ready)
return ..()
return FALSE
/obj/effect/proc_holder/spell/spacetime_dist/choose_targets(mob/user = usr)
var/list/turfs = spiral_range_turfs(range, user)
if(!turfs.len)
revert_cast()
return
ready = FALSE
var/list/turf_steps = list()
var/length = round(turfs.len * 0.5)
for(var/i in 1 to length)
turf_steps[pick_n_take(turfs)] = pick_n_take(turfs)
if(turfs.len > 0)
var/turf/loner = pick(turfs)
var/area/A = get_area(user)
turf_steps[loner] = get_turf(pick(A.contents))
perform(turf_steps,user=user)
/obj/effect/proc_holder/spell/spacetime_dist/after_cast(list/targets)
addtimer(CALLBACK(src, .proc/clean_turfs), duration)
/obj/effect/proc_holder/spell/spacetime_dist/cast(list/targets, mob/user = usr)
effects = list()
for(var/V in targets)
var/turf/T0 = V
var/turf/T1 = targets[V]
var/obj/effect/cross_action/spacetime_dist/STD0 = new /obj/effect/cross_action/spacetime_dist(T0)
var/obj/effect/cross_action/spacetime_dist/STD1 = new /obj/effect/cross_action/spacetime_dist(T1)
STD0.linked_dist = STD1
STD0.add_overlay(T1.photograph())
STD1.linked_dist = STD0
STD1.add_overlay(T0.photograph())
STD1.set_light(4, 30, "#c9fff5")
effects += STD0
effects += STD1
/obj/effect/proc_holder/spell/spacetime_dist/proc/clean_turfs()
for(var/effect in effects)
qdel(effect)
effects.Cut()
effects = null
ready = TRUE
/obj/effect/cross_action
name = "cross me"
desc = "for crossing"
anchored = TRUE
/obj/effect/cross_action/spacetime_dist
name = "spacetime distortion"
desc = "A distortion in spacetime. You can hear faint music..."
icon_state = ""
var/obj/effect/cross_action/spacetime_dist/linked_dist
var/busy = FALSE
var/sound
var/walks_left = 50 //prevents the game from hanging in extreme cases (such as minigun fire)
/obj/effect/cross_action/singularity_act()
return
/obj/effect/cross_action/singularity_pull()
return
/obj/effect/cross_action/spacetime_dist/Initialize(mapload)
. = ..()
setDir(pick(GLOB.cardinals))
/obj/effect/cross_action/spacetime_dist/proc/walk_link(atom/movable/AM)
if(ismob(AM))
var/mob/M = AM
if(M.anti_magic_check(chargecost = 0))
return
if(linked_dist && walks_left > 0)
flick("purplesparkles", src)
linked_dist.get_walker(AM)
walks_left--
/obj/effect/cross_action/spacetime_dist/proc/get_walker(atom/movable/AM)
busy = TRUE
flick("purplesparkles", src)
AM.forceMove(get_turf(src))
playsound(get_turf(src),sound,70,0)
busy = FALSE
/obj/effect/cross_action/spacetime_dist/Crossed(atom/movable/AM)
if(!busy)
walk_link(AM)
/obj/effect/cross_action/spacetime_dist/attackby(obj/item/W, mob/user, params)
if(user.temporarilyRemoveItemFromInventory(W))
walk_link(W)
else
walk_link(user)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/effect/cross_action/spacetime_dist/attack_hand(mob/user)
walk_link(user)
/obj/effect/cross_action/spacetime_dist/attack_paw(mob/user)
walk_link(user)
/obj/effect/cross_action/spacetime_dist/Destroy()
busy = TRUE
linked_dist = null
return ..()
@@ -0,0 +1,118 @@
/obj/effect/proc_holder/spell/targeted/summonitem
name = "Instant Summons"
desc = "This spell can be used to recall a previously marked item to your hand from anywhere in the universe."
school = "transmutation"
charge_max = 100
clothes_req = 0
invocation = "GAR YOK"
invocation_type = "whisper"
range = -1
level_max = 0 //cannot be improved
cooldown_min = 100
include_user = 1
var/obj/marked_item
action_icon_state = "summons"
/obj/effect/proc_holder/spell/targeted/summonitem/cast(list/targets,mob/user = usr)
for(var/mob/living/L in targets)
var/list/hand_items = list(L.get_active_held_item(),L.get_inactive_held_item())
var/message
if(!marked_item) //linking item to the spell
message = "<span class='notice'>"
for(var/obj/item/item in hand_items)
if(item.item_flags & ABSTRACT)
continue
if(HAS_TRAIT(item, TRAIT_NODROP))
message += "Though it feels redundant, "
marked_item = item
message += "You mark [item] for recall.</span>"
name = "Recall [item]"
break
if(!marked_item)
if(hand_items)
message = "<span class='caution'>You aren't holding anything that can be marked for recall.</span>"
else
message = "<span class='notice'>You must hold the desired item in your hands to mark it for recall.</span>"
else if(marked_item && marked_item in hand_items) //unlinking item to the spell
message = "<span class='notice'>You remove the mark on [marked_item] to use elsewhere.</span>"
name = "Instant Summons"
marked_item = null
else if(marked_item && QDELETED(marked_item)) //the item was destroyed at some point
message = "<span class='warning'>You sense your marked item has been destroyed!</span>"
name = "Instant Summons"
marked_item = null
else //Getting previously marked item
var/obj/item_to_retrieve = marked_item
var/infinite_recursion = 0 //I don't want to know how someone could put something inside itself but these are wizards so let's be safe
if(!item_to_retrieve.loc)
if(isorgan(item_to_retrieve)) // Organs are usually stored in nullspace
var/obj/item/organ/organ = item_to_retrieve
if(organ.owner)
// If this code ever runs I will be happy
log_combat(L, organ.owner, "magically removed [organ.name] from", addition="INTENT: [uppertext(L.a_intent)]")
organ.Remove(organ.owner)
else
while(!isturf(item_to_retrieve.loc) && infinite_recursion < 10) //if it's in something you get the whole thing.
if(isitem(item_to_retrieve.loc))
var/obj/item/I = item_to_retrieve.loc
if(I.item_flags & ABSTRACT) //Being able to summon abstract things because your item happened to get placed there is a no-no
break
if(ismob(item_to_retrieve.loc)) //If its on someone, properly drop it
var/mob/M = item_to_retrieve.loc
if(issilicon(M)) //Items in silicons warp the whole silicon
M.loc.visible_message("<span class='warning'>[M] suddenly disappears!</span>")
M.forceMove(L.loc)
M.loc.visible_message("<span class='caution'>[M] suddenly appears!</span>")
item_to_retrieve = null
break
M.dropItemToGround(item_to_retrieve)
if(iscarbon(M)) //Edge case housekeeping
var/mob/living/carbon/C = M
if(C.stomach_contents && item_to_retrieve in C.stomach_contents)
C.stomach_contents -= item_to_retrieve
for(var/X in C.bodyparts)
var/obj/item/bodypart/part = X
if(item_to_retrieve in part.embedded_objects)
part.embedded_objects -= item_to_retrieve
to_chat(C, "<span class='warning'>The [item_to_retrieve] that was embedded in your [L] has mysteriously vanished. How fortunate!</span>")
if(!C.has_embedded_objects())
C.clear_alert("embeddedobject")
SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "embedded")
break
else
if(istype(item_to_retrieve.loc, /obj/machinery/portable_atmospherics/)) //Edge cases for moved machinery
var/obj/machinery/portable_atmospherics/P = item_to_retrieve.loc
P.disconnect()
P.update_icon()
item_to_retrieve = item_to_retrieve.loc
infinite_recursion += 1
if(!item_to_retrieve)
return
if(item_to_retrieve.loc)
item_to_retrieve.loc.visible_message("<span class='warning'>The [item_to_retrieve.name] suddenly disappears!</span>")
if(!L.put_in_hands(item_to_retrieve))
item_to_retrieve.forceMove(L.drop_location())
item_to_retrieve.loc.visible_message("<span class='caution'>The [item_to_retrieve.name] suddenly appears!</span>")
playsound(get_turf(L), 'sound/magic/summonitems_generic.ogg', 50, 1)
else
item_to_retrieve.loc.visible_message("<span class='caution'>The [item_to_retrieve.name] suddenly appears in [L]'s hand!</span>")
playsound(get_turf(L), 'sound/magic/summonitems_generic.ogg', 50, 1)
if(message)
to_chat(L, message)