code:
/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)
	spawn(0)
		if(charge_type == "recharge" && recharge)
			start_recharge()
	cast(targets,thearea,user)
	after_cast(targets)

/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets)
	var/A = null

	if(!randomise_selection)
		A = input("Area to teleport to", "Teleport", A) as null|anything in teleportlocs
	else
		A = pick(teleportlocs)
	if(!A)
		return
	var/area/thearea = 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)
			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/list/tempL = L
		var/attempt = null
		var/success = 0
		while(tempL.len)
			attempt = pick(tempL)
			target.Move(attempt)
			if(get_turf(target) == attempt)
				success = 1
				break
			else
				tempL.Remove(attempt)

		if(!success)
			target.loc = pick(L)
			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)]")
				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


code:
/obj/effect/proc_holder/spell/targeted/barnyardcurse
	name = "Curse of the Barnyard"
	desc = "This spell dooms the fate of any unlucky soul to the speech and facial attributes of a barnyard animal"
	school = "transmutation"
	charge_type = "recharge"
	charge_max	= 150
	charge_counter = 0
	clothes_req = 0
	stat_allowed = 0
	invocation = "KN'A FTAGHU, PUCK 'BTHNK!"
	invocation_type = "shout"
	range = 7
	cooldown_min = 30
	selection_type = "range"
	var/list/compatible_mobs = list(/mob/living/carbon/human,/mob/living/carbon/monkey)

	action_icon_state = "barn"

/obj/effect/proc_holder/spell/targeted/barnyardcurse/cast(list/targets, mob/user = usr)
	if(!targets.len)
		user << "No target found in range."
		return

	var/mob/living/carbon/target = targets[1]

	if(!(target.type in compatible_mobs))
		user << "You are unable to curse [target]'s head!"
		return

	if(!(target in oview(range)))
		user << "They are too far away!"
		return

	var/list/masks = list(/obj/item/clothing/mask/spig, /obj/item/clothing/mask/cowmask, /obj/item/clothing/mask/horsehead)
	var/list/mSounds = list("sound/magic/PigHead_curse.ogg", "sound/magic/CowHead_Curse.ogg", "sound/magic/HorseHead_curse.ogg")
	var/randM = rand(1,3)

	var/choice = masks[randM]
	var/obj/item/clothing/mask/magichead = new choice
	magichead.flags |=NODROP
	magichead.flags_inv = null
	target.visible_message("[target]'s face lights up in fire, and after the event a barnyard animal's head takes it's place!", \
						   "Your face burns up, and shortly after the fire you realise you have the face of a barnyard animal!")
	if(!target.unEquip(target.wear_mask))
		qdel(target.wear_mask)
	target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1)
	playsound(get_turf(target), mSounds[randM], 50, 1)

	target.flash_eyes()

license: mit


code:
/obj/effect/proc_holder/spell/bloodcrawl
	name = "Blood Crawl"
	desc = "Use pools of blood to phase out of existence."
	charge_max = 0
	clothes_req = 0
	selection_type = "range"
	range = 1
	cooldown_min = 0
	overlay = null
	action_icon_state = "bloodcrawl"
	action_background_icon_state = "bg_demon"
	var/phased = 0

/obj/effect/proc_holder/spell/bloodcrawl/choose_targets(mob/user = usr)
	for(var/obj/effect/decal/cleanable/target in range(range, get_turf(user)))
		if(target.can_bloodcrawl_in())
			perform(target)
			return
	revert_cast()
	user << "There must be a nearby source of blood!"

/obj/effect/proc_holder/spell/bloodcrawl/perform(obj/effect/decal/cleanable/target, recharge = 1, mob/living/user = usr)
	if(istype(user))
		if(phased)
			if(user.phasein(target))
				phased = 0
		else
			if(user.phaseout(target))
				phased = 1
		start_recharge()
		return
	revert_cast()
	user << "You are unable to blood crawl!"


code:
/obj/effect/proc_holder/spell/targeted/charge
	name = "Charge"
	desc = "This spell can be used to recharge a variety of things in your hands, from magical artifacts to electrical components. A creative wizard can even use it to grant magical power to a fellow magic user."

	school = "transmutation"
	charge_max = 600
	clothes_req = 0
	invocation = "DIRI CEL"
	invocation_type = "whisper"
	range = -1
	cooldown_min = 400 //50 deciseconds reduction per rank
	include_user = 1

/obj/effect/proc_holder/spell/targeted/charge/cast(list/targets,mob/user = usr)
	for(var/mob/living/L in targets)
		var/list/hand_items = list(L.get_active_hand(),L.get_inactive_hand())
		var/charged_item = null
		var/burnt_out = 0

		if(L.pulling && (istype(L.pulling, /mob/living)))
			var/mob/living/M =	L.pulling
			if(M.mob_spell_list.len != 0 || (M.mind && M.mind.spell_list.len != 0))
				for(var/obj/effect/proc_holder/spell/S in M.mob_spell_list)
					S.charge_counter = S.charge_max
				if(M.mind)
					for(var/obj/effect/proc_holder/spell/S in M.mind.spell_list)
						S.charge_counter = S.charge_max
				M <<"you feel raw magic flowing through you, it feels good!"
			else
				M <<"you feel very strange for a moment, but then it passes."
				burnt_out = 1
			charged_item = M
			break
		for(var/obj/item in hand_items)
			if(istype(item, /obj/item/weapon/spellbook))
				if(istype(item, /obj/item/weapon/spellbook/oneuse))
					var/obj/item/weapon/spellbook/oneuse/I = item
					if(prob(80))
						L.visible_message("[I] catches fire!")
						qdel(I)
					else
						I.used = 0
						charged_item = I
						break
				else
					L << "Glowing red letters appear on the front cover..."
					L << "[pick("NICE TRY BUT NO!","CLEVER BUT NOT CLEVER ENOUGH!", "SUCH FLAGRANT CHEESING IS WHY WE ACCEPTED YOUR APPLICATION!", "CUTE!", "YOU DIDN'T THINK IT'D BE THAT EASY, DID YOU?")]"
					burnt_out = 1
			else if(istype(item, /obj/item/weapon/gun/magic))
				var/obj/item/weapon/gun/magic/I = item
				if(prob(80) && !I.can_charge)
					I.max_charges--
				if(I.max_charges <= 0)
					I.max_charges = 0
					burnt_out = 1
				I.charges = I.max_charges
				if(istype(item,/obj/item/weapon/gun/magic/wand) && I.max_charges != 0)
					var/obj/item/weapon/gun/magic/W = item
					W.icon_state = initial(W.icon_state)
				charged_item = I
				break
			else if(istype(item, /obj/item/weapon/stock_parts/cell/))
				var/obj/item/weapon/stock_parts/cell/C = item
				if(prob(80))
					C.maxcharge -= 200
				if(C.maxcharge <= 1) //Div by 0 protection
					C.maxcharge = 1
					burnt_out = 1
				C.charge = C.maxcharge
				charged_item = C
				break
			else if(item.contents)
				var/obj/I = null
				for(I in item.contents)
					if(istype(I, /obj/item/weapon/stock_parts/cell/))
						var/obj/item/weapon/stock_parts/cell/C = I
						if(prob(80))
							C.maxcharge -= 200
						if(C.maxcharge <= 1) //Div by 0 protection
							C.maxcharge = 1
							burnt_out = 1
						C.charge = C.maxcharge
						item.update_icon()
						charged_item = item
						break
		if(!charged_item)
			L << "you feel magical power surging to your hands, but the feeling rapidly fades..."
		else if(burnt_out)
			L << "[charged_item] doesn't seem to be reacting to the spell..."
		else
			playsound(get_turf(L), "sound/magic/Charge.ogg", 50, 1)
			L << "[charged_item] suddenly feels very warm!"


code:
/obj/effect/proc_holder/spell/aoe_turf/conjure
	name = "Conjure"
	desc = "This spell conjures objs of the specified types in range."

	var/list/summon_type = list() //determines what exactly will be summoned
	//should be text, like list("/mob/living/simple_animal/bot/ed209")

	var/summon_lifespan = 0 // 0=permanent, any other time in deciseconds
	var/summon_amt = 1 //amount of objects summoned
	var/summon_ignore_density = 0 //if set to 1, adds dense tiles to possible spawn places
	var/summon_ignore_prev_spawn_points = 0 //if set to 1, each new object is summoned on a new spawn point

	var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet
	//should have format of list("emagged" = 1,"name" = "Wizard's Justicebot"), for example

	var/cast_sound = 'sound/items/welder.ogg'

/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets,mob/user = usr)
	playsound(get_turf(user), cast_sound, 50,1)
	for(var/turf/T in targets)
		if(T.density && !summon_ignore_density)
			targets -= T

	for(var/i=0,i<summon_amt,i++)
		if(!targets.len)
			break
		var/summoned_object_type = pick(summon_type)
		var/spawn_place = pick(targets)
		if(summon_ignore_prev_spawn_points)
			targets -= spawn_place
		if(ispath(summoned_object_type,/turf))
			var/turf/O = spawn_place
			var/N = summoned_object_type
			O.ChangeTurf(N)
		else
			var/atom/summoned_object = new summoned_object_type(spawn_place)

			for(var/varName in newVars)
				if(varName in summoned_object.vars)
					summoned_object.vars[varName] = newVars[varName]

			if(summon_lifespan)
				spawn(summon_lifespan)
					if(summoned_object)
						qdel(summoned_object)

	return

/obj/effect/proc_holder/spell/aoe_turf/conjure/summonEdSwarm //test purposes - Also a lot of fun
	name = "Dispense Wizard Justice"
	desc = "This spell dispenses wizard justice."

	summon_type = list(/mob/living/simple_animal/bot/ed209)
	summon_amt = 10
	range = 3
	newVars = list("emagged" = 2, "remote_disabled" = 1,"shoot_sound" = 'sound/weapons/laser.ogg',"projectile" = /obj/item/projectile/beam/laser, "declare_arrests" = 0,"name" = "Wizard's Justicebot")


code:
//////////////////////////////Construct Spells/////////////////////////

/obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser
	charge_max = 1800
	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/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_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_state = "lesserconstruct"
	action_background_icon_state = "bg_cult"

	summon_type = list(/turf/closed/wall/cult)

/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 = 3000
	clothes_req = 0
	invocation = "none"
	invocation_type = "none"
	range = 0
	action_icon_state = "summonsoulstone"
	action_background_icon_state = "bg_demon"

	summon_type = list(/obj/item/device/soulstone)

/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone/cult
	cult_req = 1
	charge_max = 4000

/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone/noncult
	summon_type = list(/obj/item/device/soulstone/anybody)

/obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall
	name = "Shield"
	desc = "This spell creates a temporary forcefield to shield yourself and allies from incoming fire"

	school = "transmutation"
	charge_max = 300
	clothes_req = 0
	invocation = "none"
	invocation_type = "none"
	range = 0
	summon_type = list(/obj/effect/forcefield/cult)
	summon_lifespan = 200
	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 = 200
	clothes_req = 0
	invocation = "none"
	invocation_type = "none"
	range = -1
	include_user = 1
	jaunt_duration = 50 //in deciseconds
	centcom_cancast = 0 //Stop people from getting to centcom
	action_icon_state = "phaseshift"
	action_background_icon_state = "bg_demon"

/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/jaunt_disappear(atom/movable/overlay/animation, mob/living/target)
	animation.icon_state = "phase_shift"
	animation.dir = target.dir
	flick("phase_shift",animation)

/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/jaunt_reappear(atom/movable/overlay/animation, mob/living/target)
	animation.icon_state = "phase_shift2"
	animation.dir = target.dir
	flick("phase_shift2",animation)

/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_lifespan = 10
	max_targets = 6
	action_icon_state = "magicm"
	action_background_icon_state = "bg_demon"

/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 = "parasmoke"
	action_background_icon_state = "bg_cult"


code:
/obj/effect/proc_holder/spell/dumbfire

	var/projectile_type = ""
	var/activate_on_collision = 1

	var/proj_icon = 'icons/obj/projectiles.dmi'
	var/proj_icon_state = "spell"
	var/proj_name = "a spell projectile"

	var/proj_trail = 0 //if it leaves a trail
	var/proj_trail_lifespan = 0 //deciseconds
	var/proj_trail_icon = 'icons/obj/wizard.dmi'
	var/proj_trail_icon_state = "trail"

	var/proj_type = "/obj/effect/proc_holder/spell" //IMPORTANT use only subtypes of this

	var/proj_insubstantial = 0 //if it can pass through dense objects or not
	var/proj_trigger_range = 1 //the range from target at which the projectile triggers cast(target)

	var/proj_lifespan = 100 //in deciseconds * proj_step_delay
	var/proj_step_delay = 1 //lower = faster

/obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr)

	var/turf/T = get_turf(user)
	for(var/i = 1; i < range; i++)
		var/turf/new_turf = get_step(T, user.dir)
		if(new_turf.density)
			break
		T = new_turf
	perform(list(T),user = user)

/obj/effect/proc_holder/spell/dumbfire/cast(list/targets, mob/user = usr)
	playMagSound()
	for(var/turf/target in targets)
		spawn(0)
			var/obj/effect/proc_holder/spell/targeted/projectile
			if(istext(proj_type))
				var/projectile_type = text2path(proj_type)
				projectile = new projectile_type(user)
			if(istype(proj_type,/obj/effect/proc_holder/spell))
				projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user)
				projectile:linked_spells += proj_type
			projectile.icon = proj_icon
			projectile.icon_state = proj_icon_state
			projectile.dir = get_dir(projectile, target)
			projectile.name = proj_name

			var/current_loc = user.loc

			projectile.loc = current_loc

			for(var/i = 0,i < proj_lifespan,i++)
				if(!projectile)
					break

				if(proj_insubstantial)
					projectile.loc = get_step(projectile, projectile.dir)
				else
					step(projectile, projectile.dir)

				if(projectile.loc == current_loc || i == proj_lifespan)
					projectile.cast(current_loc,user=user)
					break

				var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user
				if(L && L.stat != DEAD)
					projectile.cast(L.loc,user=user)
					break

				if(proj_trail && projectile)
					spawn(0)
						if(projectile)
							var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc)
							trail.icon = proj_trail_icon
							trail.icon_state = proj_trail_icon_state
							trail.density = 0
							spawn(proj_trail_lifespan)
								qdel(trail)

				current_loc = projectile.loc

				sleep(proj_step_delay)

			if(projectile)
				qdel(projectile)


code:
/obj/effect/proc_holder/spell/targeted/emplosion
	name = "Emplosion"
	desc = "This spell emplodes an area."

	var/emp_heavy = 2
	var/emp_light = 3

	action_icon_state = "emp"
	sound = "sound/weapons/ZapBang.ogg"

/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets,mob/user = usr)
	playsound(get_turf(user), sound, 50,1)
	for(var/mob/living/target in targets)
		empulse(target.loc, emp_heavy, emp_light)

	return


code:
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt
	name = "Ethereal Jaunt"
	desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls."

	school = "transmutation"
	charge_max = 300
	clothes_req = 1
	invocation = "none"
	invocation_type = "none"
	range = -1
	cooldown_min = 100 //50 deciseconds reduction per rank
	include_user = 1
	centcom_cancast = 0 //Prevent people from getting to centcom
	nonabstract_req = 1
	var/jaunt_duration = 50 //in deciseconds
	action_icon_state = "jaunt"

/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets,mob/user = usr) //magnets, so mostly hardcoded
	playsound(get_turf(user), 'sound/magic/Ethereal_Enter.ogg', 50, 1, -1)
	for(var/mob/living/target in targets)
		target.notransform = 1 //protects the mob from being transformed (replaced) midjaunt and getting stuck in bluespace
		spawn(0)
			var/turf/mobloc = get_turf(target.loc)
			var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt( mobloc )
			var/atom/movable/overlay/animation = new /atom/movable/overlay( mobloc )
			animation.name = "water"
			animation.density = 0
			animation.anchored = 1
			animation.icon = 'icons/mob/mob.dmi'
			animation.layer = 5
			animation.master = holder
			target.ExtinguishMob()
			if(target.buckled)
				target.buckled.unbuckle_mob(target,force=1)
			if(target.pulledby)
				target.pulledby.stop_pulling()
			target.stop_pulling()
			if(target.buckled_mobs.len)
				target.unbuckle_all_mobs(force=1)
			jaunt_disappear(animation, target)
			target.loc = holder
			target.reset_perspective(holder)
			target.notransform=0 //mob is safely inside holder now, no need for protection.
			jaunt_steam(mobloc)

			mute(target)
			sleep(jaunt_duration)
			unmute(target)

			if(target.loc != holder) //mob warped out of the warp
				qdel(holder)
				return
			mobloc = get_turf(target.loc)
			animation.loc = mobloc
			jaunt_steam(mobloc)
			target.canmove = 0
			holder.reappearing = 1
			playsound(get_turf(user), 'sound/magic/Ethereal_Exit.ogg', 50, 1, -1)
			sleep(20)
			if(!qdeleted(target))
				jaunt_reappear(animation, target)
			sleep(5)
			qdel(animation)
			qdel(holder)
			if(!qdeleted(target))
				if(mobloc.density)
					for(var/direction in list(1,2,4,8,5,6,9,10))
						var/turf/T = get_step(mobloc, direction)
						if(T)
							if(target.Move(T))
								break
				target.canmove = 1

//Silence wizard during jaunt so they cannot spell cast while invisible
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/mute(target)
	if(!istype(target, /mob/living/carbon))
		return FALSE

	var/mob/living/carbon/mob_to_mute = target
	if(!mob_to_mute.dna)
		return FALSE

	mob_to_mute.dna.add_mutation(MUT_MUTE)
	return TRUE

/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/unmute(target)
	if(!istype(target, /mob/living/carbon))
		return FALSE

	var/mob/living/carbon/mob_to_mute = target
	if(!mob_to_mute.dna)
		return FALSE

	mob_to_mute.dna.remove_mutation(MUT_MUTE)
	return TRUE

/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_disappear(atom/movable/overlay/animation, mob/living/target)
	animation.icon_state = "liquify"
	flick("liquify",animation)

/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_reappear(atom/movable/overlay/animation, mob/living/target)
	flick("reappear",animation)

/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc)
	var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread()
	steam.set_up(10, 0, mobloc)
	steam.start()

/obj/effect/dummy/spell_jaunt
	name = "water"
	icon = 'icons/effects/effects.dmi'
	icon_state = "nothing"
	var/canmove = 1
	var/reappearing = 0
	density = 0
	anchored = 1
	invisibility = 60
	burn_state = LAVA_PROOF

/obj/effect/dummy/spell_jaunt/Destroy()
	// Eject contents if deleted somehow
	for(var/atom/movable/AM in src)
		AM.forceMove(get_turf(src))
	return ..()

/obj/effect/dummy/spell_jaunt/relaymove(var/mob/user, direction)
	if (!src.canmove || reappearing || !direction) return
	var/turf/newLoc = get_step(src,direction)
	if(!(newLoc.flags & NOJAUNT))
		loc = newLoc
	else
		user << "Some strange aura is blocking the way!"
	src.canmove = 0
	spawn(2) src.canmove = 1

/obj/effect/dummy/spell_jaunt/ex_act(blah)
	return
/obj/effect/dummy/spell_jaunt/bullet_act(blah)
	return


code:
/obj/effect/proc_holder/spell/targeted/explosion
	name = "Explosion"
	desc = "This spell explodes an area."

	var/ex_severe = 1
	var/ex_heavy = 2
	var/ex_light = 3
	var/ex_flash = 4

/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets,mob/user = usr)

	for(var/mob/living/target in targets)
		explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash)

	return


code:
/obj/effect/proc_holder/spell/targeted/genetic
	name = "Genetic"
	desc = "This spell inflicts a set of mutations and disabilities upon the target."

	var/disabilities = 0 //bits
	var/list/mutations = list() //mutation strings
	var/duration = 100 //deciseconds
	/*
		Disabilities
			1st bit - ?
			2nd bit - ?
			3rd bit - ?
			4th bit - ?
			5th bit - ?
			6th bit - ?
	*/

/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets,mob/user = usr)
	playMagSound()
	for(var/mob/living/carbon/target in targets)
		if(!target.dna)
			continue
		for(var/A in mutations)
			target.dna.add_mutation(A)
		target.disabilities |= disabilities
		spawn(duration)
			if(target && !qdeleted(target))
				for(var/A in mutations)
					target.dna.remove_mutation(A)
				target.disabilities &= ~disabilities

	return


code:
/obj/item/weapon/melee/touch_attack
	name = "\improper outstretched hand"
	desc = "High Five?"
	var/catchphrase = "High Five!"
	var/on_use_sound = null
	var/obj/effect/proc_holder/spell/targeted/touch/attached_spell
	icon = 'icons/obj/weapons.dmi'
	icon_state = "syndballoon"
	item_state = null
	flags = ABSTRACT | NODROP
	w_class = 5
	force = 0
	throwforce = 0
	throw_range = 0
	throw_speed = 0

/obj/item/weapon/melee/touch_attack/New(var/spell)
	attached_spell = spell
	..()

/obj/item/weapon/melee/touch_attack/attack(mob/target, mob/living/carbon/user)
	if(!iscarbon(user)) //Look ma, no hands
		return
	if(user.lying || user.handcuffed)
		user << "You can't reach out!"
		return
	..()

/obj/item/weapon/melee/touch_attack/afterattack(atom/target, mob/user, proximity)
	user.say(catchphrase)
	playsound(get_turf(user), on_use_sound,50,1)
	if(attached_spell)
		attached_spell.attached_hand = null
	qdel(src)

/obj/item/weapon/melee/touch_attack/dropped()
	if(attached_spell)
		attached_spell.attached_hand = null
	qdel(src)

/obj/item/weapon/melee/touch_attack/disintegrate
	name = "\improper disintegrating touch"
	desc = "This hand of mine glows with an awesome power!"
	catchphrase = "EI NATH!!"
	on_use_sound = "sound/magic/Disintegrate.ogg"
	icon_state = "disintegrate"
	item_state = "disintegrate"

/obj/item/weapon/melee/touch_attack/disintegrate/afterattack(atom/target, mob/living/carbon/user, proximity)
	if(!proximity || target == user || !ismob(target) || !iscarbon(user) || user.lying || user.handcuffed) //exploding after touching yourself would be bad
		return
	var/mob/M = target
	var/datum/effect_system/spark_spread/sparks = new
	sparks.set_up(4, 0, M.loc) //no idea what the 0 is
	sparks.start()
	M.gib()
	..()

/obj/item/weapon/melee/touch_attack/fleshtostone
	name = "\improper petrifying touch"
	desc = "That's the bottom line, because flesh to stone said so!"
	catchphrase = "STAUN EI!!"
	on_use_sound = "sound/magic/FleshToStone.ogg"
	icon_state = "fleshtostone"
	item_state = "fleshtostone"

/obj/item/weapon/melee/touch_attack/fleshtostone/afterattack(atom/target, mob/living/carbon/user, proximity)
	if(!proximity || target == user || !isliving(target) || !iscarbon(user) || user.lying || user.handcuffed) //getting hard after touching yourself would also be bad
		return
	if(user.lying || user.handcuffed)
		user << "You can't reach out!"
		return
	var/mob/living/M = target
	M.Stun(2)
	M.petrify()
	..()


code:
/obj/effect/proc_holder/spell/targeted/infinite_guns
	name = "Lesser Summon Guns"
	desc = "Why reload when you have infinite guns? Summons an unending stream of bolt action rifles. Requires both hands free to use."
	invocation_type = "none"
	include_user = 1
	range = -1

	school = "conjuration"
	charge_max = 750
	clothes_req = 1
	cooldown_min = 10 //Gun wizard
	action_icon_state = "bolt_action"

/obj/effect/proc_holder/spell/targeted/infinite_guns/cast(list/targets, mob/user = usr)
	for(var/mob/living/carbon/C in targets)
		C.drop_item()
		C.swap_hand()
		C.drop_item()
		var/obj/item/weapon/gun/projectile/shotgun/boltaction/enchanted/GUN = new
		C.put_in_hands(GUN)


code:
/obj/effect/proc_holder/spell/targeted/inflict_handler
	name = "Inflict Handler"
	desc = "This spell blinds and/or destroys/damages/heals and/or weakens/stuns the target."

	var/amt_weakened = 0
	var/amt_paralysis = 0
	var/amt_stunned = 0

	//set to negatives for healing
	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

/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)
		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
		target.Weaken(amt_weakened)
		target.Paralyse(amt_paralysis)
		target.Stun(amt_stunned)

		target.blind_eyes(amt_eye_blind)
		target.blur_eyes(amt_eye_blurry)
		//summoning
		if(summon_type)
			new summon_type(target.loc, target)


code:
/obj/effect/proc_holder/spell/aoe_turf/knock
	name = "Knock"
	desc = "This spell opens nearby doors and does not require wizard garb."

	school = "transmutation"
	charge_max = 100
	clothes_req = 0
	invocation = "AULIE OXIN FIERA"
	invocation_type = "whisper"
	range = 3
	cooldown_min = 20 //20 deciseconds reduction per rank

	action_icon_state = "knock"

/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets,mob/user = usr)
	user << sound("sound/magic/Knock.ogg")
	for(var/turf/T in targets)
		for(var/obj/machinery/door/door in T.contents)
			spawn(1)
				if(istype(door,/obj/machinery/door/airlock))
					door:locked = 0
				door.open()
		for(var/obj/structure/closet/C in T.contents)
			spawn(1)
				C.locked = 0
				C.open()

	return


code:
/obj/effect/proc_holder/spell/targeted/lichdom
	name = "Bind Soul"
	desc = "A dark necromantic pact that can forever bind your soul to an item of your choosing. So long as both your body and the item remain intact and on the same plane you can revive from death, though the time between reincarnations grows steadily with use."
	school = "necromancy"
	charge_max = 10
	clothes_req = 0
	centcom_cancast = 0
	invocation = "NECREM IMORTIUM!"
	invocation_type = "shout"
	range = -1
	level_max = 0 //cannot be improved
	cooldown_min = 10
	include_user = 1

	var/obj/marked_item
	var/mob/living/current_body
	var/resurrections = 0
	var/existence_stops_round_end = 0

	action_icon_state = "skeleton"

/obj/effect/proc_holder/spell/targeted/lichdom/New()
	if(initial(ticker.mode.round_ends_with_antag_death))
		existence_stops_round_end = 1
		ticker.mode.round_ends_with_antag_death = 0
	..()

/obj/effect/proc_holder/spell/targeted/lichdom/Destroy()
	for(var/datum/mind/M in ticker.mode.wizards) //Make sure no other bones are about
		for(var/obj/effect/proc_holder/spell/S in M.spell_list)
			if(istype(S,/obj/effect/proc_holder/spell/targeted/lichdom) && S != src)
				return ..()
	if(existence_stops_round_end)
		ticker.mode.round_ends_with_antag_death = 1
	..()

/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets,mob/user = usr)
	for(var/mob/M in targets)
		var/list/hand_items = list()
		if(iscarbon(M))
			hand_items = list(M.get_active_hand(),M.get_inactive_hand())

		if(marked_item && !stat_allowed) //sanity, shouldn't happen without badminry
			marked_item = null
			return

		if(stat_allowed) //Death is not my end!
			if(M.stat == CONSCIOUS && iscarbon(M))
				M << "You aren't dead enough to revive!" //Usually a good problem to have
				charge_counter = charge_max
				return

			if(!marked_item || qdeleted(marked_item)) //Wait nevermind
				M << "Your phylactery is gone!"
				return

			var/turf/user_turf = get_turf(M)
			var/turf/item_turf = get_turf(marked_item)

			if(user_turf.z != item_turf.z)
				M << "Your phylactery is out of range!"
				return

			if(isobserver(M))
				var/mob/dead/observer/O = M
				O.reenter_corpse()

			var/mob/living/carbon/human/lich = new /mob/living/carbon/human(item_turf)

			lich.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(lich), slot_shoes)
			lich.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(lich), slot_w_uniform)
			lich.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(lich), slot_wear_suit)
			lich.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(lich), slot_head)

			lich.real_name = M.mind.name
			M.mind.transfer_to(lich)
			lich.hardset_dna(null,null,lich.real_name,null,/datum/species/skeleton)
			lich << "Your bones clatter and shutter as they're pulled back into this world!"
			charge_max += 600
			var/mob/old_body = current_body
			var/turf/body_turf = get_turf(old_body)
			current_body = lich
			lich.Weaken(10+10*resurrections)
			++resurrections
			if(old_body && old_body.loc)
				if(iscarbon(old_body))
					var/mob/living/carbon/C = old_body
					for(var/obj/item/W in C)
						C.unEquip(W)
					for(var/obj/item/organ/internal/I in C.internal_organs)
						I.Remove(C)
						I.forceMove(body_turf)
				var/wheres_wizdo = dir2text(get_dir(body_turf, item_turf))
				if(wheres_wizdo)
					old_body.visible_message("Suddenly [old_body.name]'s corpse falls to pieces! You see a strange energy rise from the remains, and speed off towards the [wheres_wizdo]!")
					body_turf.Beam(item_turf,icon_state="lichbeam",icon='icons/effects/effects.dmi',time=10+10*resurrections,maxdistance=INFINITY)
				old_body.dust()

		if(!marked_item) //linking item to the spell
			message = ""
			for(var/obj/item in hand_items)
				if(ABSTRACT in item.flags || NODROP in item.flags)
					continue
				marked_item = 		item
				M << "You begin to focus your very being into the [item.name]..."
				break

			if(!marked_item)
				M << "You must hold an item you wish to make your phylactery..."
				return
			spawn(50)
				if(marked_item.loc != M) //I changed my mind I don't want to put my soul in a cheeseburger!
					M << "Your soul snaps back to your body as you drop the [marked_item.name]!"
					marked_item = null
					return
				name = "RISE!"
				desc = "Rise from the dead! You will reform at the location of your phylactery and your old body will crumble away."
				charge_max = 1800 //3 minute cooldown, if you rise in sight of someone and killed again, you're probably screwed.
				charge_counter = 1800
				stat_allowed = 1
				marked_item.name = "Ensouled [marked_item.name]"
				marked_item.desc = "A terrible aura surrounds this item, its very existence is offensive to life itself..."
				marked_item.color = "#003300"
				M << "With a hideous feeling of emptiness you watch in horrified fascination as skin sloughs off bone! Blood boils, nerves disintegrate, eyes boil in their sockets! As your organs crumble to dust in your fleshless chest you come to terms with your choice. You're a lich!"
				M.set_species(/datum/species/skeleton)
				current_body = M.mind.current
				if(ishuman(M))
					var/mob/living/carbon/human/H = M
					H.unEquip(H.wear_suit)
					H.unEquip(H.head)
					H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), slot_wear_suit)
					H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(H), slot_head)


code:
/obj/effect/proc_holder/spell/targeted/lightning
	name = "Lightning Bolt"
	desc = "Charges up and throws a lightning bolt at nearby enemies. Classic."
	charge_type = "recharge"
	charge_max	= 300
	clothes_req = 1
	invocation = "UN'LTD P'WAH!"
	invocation_type = "shout"
	range = 7
	cooldown_min = 30
	selection_type = "view"
	random_target = 1
	var/ready = 0
	var/image/halo = null
	var/sound/Snd // so far only way i can think of to stop a sound, thank MSO for the idea.

	action_icon_state = "lightning"

/obj/effect/proc_holder/spell/targeted/lightning/Click()
	if(!ready && cast_check())
		StartChargeup()
	return 1

/obj/effect/proc_holder/spell/targeted/lightning/proc/StartChargeup(mob/user = usr)
	ready = 1
	user << "You start gathering the power."
	Snd = new/sound('sound/magic/lightning_chargeup.ogg',channel = 7)
	halo = image("icon"='icons/effects/effects.dmi',"icon_state" ="electricity","layer" = EFFECTS_LAYER)
	user.overlays.Add(halo)
	playsound(get_turf(user), Snd, 50, 0)
	if(do_mob(user,user,100,1))
		if(ready && cast_check(skipcharge=1))
			choose_targets()
		else
			Reset(user)

/obj/effect/proc_holder/spell/targeted/lightning/proc/Reset(mob/user = usr)
	ready = 0
	if(halo)
		user.overlays.Remove(halo)

/obj/effect/proc_holder/spell/targeted/lightning/revert_cast(mob/user = usr)
	user << "No target found in range."
	Reset(user)
	..()

/obj/effect/proc_holder/spell/targeted/lightning/cast(list/targets, mob/user = usr)
	ready = 0
	var/mob/living/carbon/target = targets[1]
	Snd=sound(null, repeat = 0, wait = 1, channel = Snd.channel) //byond, why you suck?
	playsound(get_turf(user),Snd,50,0)// Sorry MrPerson, but the other ways just didn't do it the way i needed to work, this is the only way.
	if(get_dist(user,target)>range)
		user << "They are too far away!"
		Reset(user)
		return

	playsound(get_turf(user), 'sound/magic/lightningbolt.ogg', 50, 1)
	user.Beam(target,icon_state="lightning[rand(1,12)]",icon='icons/effects/effects.dmi',time=5)

	Bolt(user,target,30,5,user)
	Reset(user)

/obj/effect/proc_holder/spell/targeted/lightning/proc/Bolt(mob/origin,mob/target,bolt_energy,bounces,mob/user = usr)
	origin.Beam(target,icon_state="lightning[rand(1,12)]",icon='icons/effects/effects.dmi',time=5)
	var/mob/living/carbon/current = target
	if(bounces < 1)
		current.electrocute_act(bolt_energy,"Lightning Bolt",safety=1)
		playsound(get_turf(current), 'sound/magic/LightningShock.ogg', 50, 1, -1)
	else
		current.electrocute_act(bolt_energy,"Lightning Bolt",safety=1)
		playsound(get_turf(current), 'sound/magic/LightningShock.ogg', 50, 1, -1)
		var/list/possible_targets = new
		for(var/mob/living/M in view_or_range(range,target,"view"))
			if(user == M || target == M && los_check(current,M)) // || origin == M ? Not sure double shockings is good or not
				continue
			possible_targets += M
		if(!possible_targets.len)
			return
		var/mob/living/next = pick(possible_targets)
		if(next)
			Bolt(current,next,max((bolt_energy-5),5),bounces-1,user)


code:
/obj/effect/proc_holder/spell/aoe_turf/conjure/mime_wall
	name = "Invisible wall"
	desc = "The mime's performance transmutates into physical reality."
	school = "mime"
	panel = "Mime"
	summon_type = list(/obj/effect/forcefield/mime)
	invocation_type = "emote"
	invocation_emote_self = "You form a wall in front of yourself."
	summon_lifespan = 300
	charge_max = 300
	clothes_req = 0
	range = 0
	cast_sound = null
	human_req = 1

	action_icon_state = "mime"
	action_background_icon_state = "bg_mime"

/obj/effect/proc_holder/spell/aoe_turf/conjure/mime_wall/Click()
	if(usr && usr.mind)
		if(!usr.mind.miming)
			usr << "You must dedicate yourself to silence first."
			return
		invocation = "[usr.real_name] looks as if a wall is in front of them."
	else
		invocation_type ="none"
	..()

/obj/effect/proc_holder/spell/targeted/mime/speak
	name = "Speech"
	desc = "Make or break a vow of silence."
	school = "mime"
	panel = "Mime"
	clothes_req = 0
	human_req = 1
	charge_max = 3000
	range = -1
	include_user = 1

	action_icon_state = "mime"
	action_background_icon_state = "bg_mime"

/obj/effect/proc_holder/spell/targeted/mime/speak/Click()
	if(!usr)
		return
	if(!ishuman(usr))
		return
	var/mob/living/carbon/human/H = usr
	if(H.mind.miming)
		still_recharging_msg = "You can't break your vow of silence that fast!"
	else
		still_recharging_msg = "You'll have to wait before you can give your vow of silence again!"
	..()

/obj/effect/proc_holder/spell/targeted/mime/speak/cast(list/targets,mob/user = usr)
	for(var/mob/living/carbon/human/H in targets)
		H.mind.miming=!H.mind.miming
		if(H.mind.miming)
			H << "You make a vow of silence."
		else
			H << "You break your vow of silence."


code:
/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/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell
	var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell
	var/paralysis_amount_victim = 20 //how much the victim is paralysed 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 transfering.
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/user = usr, distanceoverride)
	if(!targets.len)
		user << "No mind found!"
		return

	if(targets.len > 1)
		user << "Too many minds! You're not a hive damnit!"//Whaa...aat?
		return

	var/mob/living/target = targets[1]

	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.
		user << "They are too far away!"
		return

	if(target.stat == DEAD)
		user << "You don't particularly want to be dead!"
		return

	if(!target.key || !target.mind)
		user << "They appear to be catatonic! Not even magic can affect their vacant mind."
		return

	if(user.suiciding)
		user << "You're killing yourself! You can't concentrate enough to do this!"
		return

	if((target.mind.special_role in protected_roles) || cmptext(copytext(target.key,1,2),"@"))
		user << "Their mind is resisting your spell!"
		return

	var/mob/living/victim = target//The target of the spell whos body will be transferred to.
	var/mob/caster = user//The wizard/whomever doing the body transferring.

	//MIND TRANSFER BEGIN
	if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list.
		for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot.
			caster.verbs -= V//But a safety nontheless.

	if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs.
		for(var/V in victim.mind.special_verbs)
			victim.verbs -= V

	var/mob/dead/observer/ghost = victim.ghostize(0)
	caster.mind.transfer_to(victim)

	if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster.
		for(var/V in caster.mind.special_verbs)//Not too important but could come into play.
			caster.verbs += V

	ghost.mind.transfer_to(caster)
	if(ghost.key)
		caster.key = ghost.key	//have to transfer the key since the mind was not active
	qdel(ghost)

	if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here.
		for(var/V in caster.mind.special_verbs)
			caster.verbs += V
	//MIND TRANSFER END

	//Here we paralyze both mobs and knock them out for a time.
	caster.Paralyse(paralysis_amount_caster)
	victim.Paralyse(paralysis_amount_victim)
	caster << sound('sound/magic/MandSwap.ogg')
	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


code:
/obj/effect/proc_holder/spell/targeted/projectile
	name = "Projectile"
	desc = "This spell summons projectiles which try to hit the targets."

	var/proj_icon = 'icons/obj/projectiles.dmi'
	var/proj_icon_state = "spell"
	var/proj_name = "a spell projectile"

	var/proj_trail = 0 //if it leaves a trail
	var/proj_trail_lifespan = 0 //deciseconds
	var/proj_trail_icon = 'icons/obj/wizard.dmi'
	var/proj_trail_icon_state = "trail"

	var/proj_type = "/obj/effect/proc_holder/spell/targeted" //IMPORTANT use only subtypes of this

	var/proj_lingering = 0 //if it lingers or disappears upon hitting an obstacle
	var/proj_homing = 1 //if it follows the target
	var/proj_insubstantial = 0 //if it can pass through dense objects or not
	var/proj_trigger_range = 0 //the range from target at which the projectile triggers cast(target)

	var/proj_lifespan = 15 //in deciseconds * proj_step_delay
	var/proj_step_delay = 1 //lower = faster

/obj/effect/proc_holder/spell/targeted/projectile/cast(list/targets, mob/user = usr)
	playMagSound()
	for(var/mob/living/target in targets)
		spawn(0)
			var/obj/effect/proc_holder/spell/targeted/projectile
			if(istext(proj_type))
				var/projectile_type = text2path(proj_type)
				projectile = new projectile_type(user)
			if(istype(proj_type,/obj/effect/proc_holder/spell))
				projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user)
				projectile:linked_spells += proj_type
			projectile.icon = proj_icon
			projectile.icon_state = proj_icon_state
			projectile.dir = get_dir(target,projectile)
			projectile.name = proj_name

			var/current_loc = user.loc

			projectile.loc = current_loc

			for(var/i = 0,i < proj_lifespan,i++)
				if(!projectile)
					break

				if(proj_homing)
					if(proj_insubstantial)
						projectile.dir = get_dir(projectile,target)
						projectile.loc = get_step_to(projectile,target)
					else
						step_to(projectile,target)
				else
					if(proj_insubstantial)
						projectile.loc = get_step(projectile,dir)
					else
						step(projectile,dir)

				if(!projectile) // step and step_to sleeps so we'll have to check again.
					break

				if(!target || (!proj_lingering && projectile.loc == current_loc)) //if it didn't move since last time
					qdel(projectile)
					break

				if(proj_trail && projectile)
					spawn(0)
						if(projectile)
							var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc)
							trail.icon = proj_trail_icon
							trail.icon_state = proj_trail_icon_state
							trail.density = 0
							spawn(proj_trail_lifespan)
								qdel(trail)

				if(projectile.loc in range(target.loc,proj_trigger_range))
					projectile.perform(list(target),user=user)
					break

				current_loc = projectile.loc

				sleep(proj_step_delay)

			if(projectile)
				qdel(projectile)


code:
//In this file: Summon Magic/Summon Guns/Summon Events

/proc/rightandwrong(summon_type, mob/user, survivor_probability) //0 = Summon Guns, 1 = Summon Magic
	var/list/gunslist 			= list("taser","egun","laser","revolver","detective","c20r","nuclear","deagle","gyrojet","pulse","suppressed","cannon","doublebarrel","shotgun","combatshotgun","bulldog","mateba","sabr","crossbow","saw","car","boltaction","speargun","arg","uzi","alienpistol","dragnet","turret","pulsecarbine","decloner","mindflayer","hyperkinetic","advplasmacutter","wormhole","wt550","bulldog","grenadelauncher","goldenrevolver","sniper","medibeam","scatterbeam")
	var/list/magiclist 			= list("fireball","smoke","blind","mindswap","forcewall","knock","horsemask","charge", "summonitem", "wandnothing", "wanddeath", "wandresurrection", "wandpolymorph", "wandteleport", "wanddoor", "wandfireball", "staffchange", "staffhealing", "armor", "scrying","staffdoor","voodoo", "special")
	var/list/magicspeciallist	= list("staffchange","staffanimation", "wandbelt", "contract", "staffchaos", "necromantic")

	if(user) //in this case either someone holding a spellbook or a badmin
		user << "You summoned [summon_type ? "magic" : "guns"]!"
		message_admins("[key_name_admin(user, 1)] summoned [summon_type ? "magic" : "guns"]!")
		log_game("[key_name(user)] summoned [summon_type ? "magic" : "guns"]!")
	for(var/mob/living/carbon/human/H in player_list)
		if(H.stat == 2 || !(H.client)) continue
		if(H.mind)
			if(H.mind.special_role == "Wizard" || H.mind.special_role == "apprentice" || H.mind.special_role == "survivalist") continue
		if(prob(survivor_probability) && !(H.mind in ticker.mode.traitors))
			ticker.mode.traitors += H.mind
			var/datum/objective/summon_guns/guns = new
			guns.owner = H.mind
			H.mind.objectives += guns
			H.mind.special_role = "survivalist"
			var/datum/objective/survive/survive = new
			survive.owner = H.mind
			H.mind.objectives += survive
			H.attack_log += "\[[time_stamp()]\] Was made into a survivalist, and trusts no one!"
			H << "You are the survivalist! Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, by any means necessary. Kill anyone who gets in your way."
			var/obj_count = 1
			for(var/datum/objective/OBJ in H.mind.objectives)
				H << "Objective #[obj_count]: [OBJ.explanation_text]"
				obj_count++
		var/randomizeguns 			= pick(gunslist)
		var/randomizemagic 			= pick(magiclist)
		var/randomizemagicspecial 	= pick(magicspeciallist)
		if(!summon_type)
			var/obj/item/weapon/gun/G
			switch (randomizeguns)
				if("taser")
					G = new /obj/item/weapon/gun/energy/gun/advtaser(get_turf(H))
				if("egun")
					G = new /obj/item/weapon/gun/energy/gun(get_turf(H))
				if("laser")
					G = new /obj/item/weapon/gun/energy/laser(get_turf(H))
				if("revolver")
					G = new /obj/item/weapon/gun/projectile/revolver(get_turf(H))
				if("detective")
					G = new /obj/item/weapon/gun/projectile/revolver/detective(get_turf(H))
				if("deagle")
					G = new /obj/item/weapon/gun/projectile/automatic/pistol/deagle/camo(get_turf(H))
				if("gyrojet")
					G = new /obj/item/weapon/gun/projectile/automatic/gyropistol(get_turf(H))
				if("pulse")
					G = new /obj/item/weapon/gun/energy/pulse(get_turf(H))
				if("suppressed")
					G = new /obj/item/weapon/gun/projectile/automatic/pistol(get_turf(H))
					new /obj/item/weapon/suppressor(get_turf(H))
				if("doublebarrel")
					G = new /obj/item/weapon/gun/projectile/revolver/doublebarrel(get_turf(H))
				if("shotgun")
					G = new /obj/item/weapon/gun/projectile/shotgun(get_turf(H))
				if("combatshotgun")
					G = new /obj/item/weapon/gun/projectile/shotgun/automatic/combat(get_turf(H))
				if("arg")
					G = new /obj/item/weapon/gun/projectile/automatic/ar(get_turf(H))
				if("mateba")
					G = new /obj/item/weapon/gun/projectile/revolver/mateba(get_turf(H))
				if("boltaction")
					G = new /obj/item/weapon/gun/projectile/shotgun/boltaction(get_turf(H))
				if("speargun")
					G = new /obj/item/weapon/gun/projectile/automatic/speargun(get_turf(H))
				if("uzi")
					G = new /obj/item/weapon/gun/projectile/automatic/mini_uzi(get_turf(H))
				if("cannon")
					G = new /obj/item/weapon/gun/energy/lasercannon(get_turf(H))
				if("crossbow")
					G = new /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow/large(get_turf(H))
				if("nuclear")
					G = new /obj/item/weapon/gun/energy/gun/nuclear(get_turf(H))
				if("sabr")
					G = new /obj/item/weapon/gun/projectile/automatic/proto(get_turf(H))
				if("bulldog")
					G = new /obj/item/weapon/gun/projectile/automatic/shotgun/bulldog(get_turf(H))
				if("c20r")
					G = new /obj/item/weapon/gun/projectile/automatic/c20r(get_turf(H))
				if("saw")
					G = new /obj/item/weapon/gun/projectile/automatic/l6_saw(get_turf(H))
				if("car")
					G = new /obj/item/weapon/gun/projectile/automatic/m90(get_turf(H))
				if("alienpistol")
					G = new /obj/item/weapon/gun/energy/alien(get_turf(H))
				if("dragnet")
					G = new /obj/item/weapon/gun/energy/gun/dragnet(get_turf(H))
				if("turret")
					G = new /obj/item/weapon/gun/energy/gun/turret(get_turf(H))
				if("pulsecarbine")
					G = new /obj/item/weapon/gun/energy/pulse/carbine(get_turf(H))
				if("decloner")
					G = new /obj/item/weapon/gun/energy/decloner(get_turf(H))
				if("mindflayer")
					G = new /obj/item/weapon/gun/energy/mindflayer(get_turf(H))
				if("hyperkinetic")
					G = new /obj/item/weapon/gun/energy/kinetic_accelerator/hyper(get_turf(H))
				if("advplasmacutter")
					G = new /obj/item/weapon/gun/energy/plasmacutter/adv(get_turf(H))
				if("wormhole")
					G = new /obj/item/weapon/gun/energy/wormhole_projector(get_turf(H))
				if("wt550")
					G = new /obj/item/weapon/gun/projectile/automatic/wt550(get_turf(H))
				if("bulldog")
					G = new /obj/item/weapon/gun/projectile/automatic/shotgun(get_turf(H))
				if("grenadelauncher")
					G = new /obj/item/weapon/gun/projectile/revolver/grenadelauncher(get_turf(H))
				if("goldenrevolver")
					G = new /obj/item/weapon/gun/projectile/revolver/golden(get_turf(H))
				if("sniper")
					G = new /obj/item/weapon/gun/projectile/sniper_rifle(get_turf(H))
				if("medibeam")
					G = new /obj/item/weapon/gun/medbeam(get_turf(H))
				if("scatterbeam")
					G = new /obj/item/weapon/gun/energy/laser/scatter(get_turf(H))
			G.unlock()
			playsound(get_turf(H),'sound/magic/Summon_guns.ogg', 50, 1)

		else
			switch (randomizemagic)
				if("fireball")
					new /obj/item/weapon/spellbook/oneuse/fireball(get_turf(H))
				if("smoke")
					new /obj/item/weapon/spellbook/oneuse/smoke(get_turf(H))
				if("blind")
					new /obj/item/weapon/spellbook/oneuse/blind(get_turf(H))
				if("mindswap")
					new /obj/item/weapon/spellbook/oneuse/mindswap(get_turf(H))
				if("forcewall")
					new /obj/item/weapon/spellbook/oneuse/forcewall(get_turf(H))
				if("knock")
					new /obj/item/weapon/spellbook/oneuse/knock(get_turf(H))
				if("horsemask")
					new /obj/item/weapon/spellbook/oneuse/barnyard(get_turf(H))
				if("charge")
					new /obj/item/weapon/spellbook/oneuse/charge(get_turf(H))
				if("summonitem")
					new /obj/item/weapon/spellbook/oneuse/summonitem(get_turf(H))
				if("wandnothing")
					new /obj/item/weapon/gun/magic/wand(get_turf(H))
				if("wanddeath")
					new /obj/item/weapon/gun/magic/wand/death(get_turf(H))
				if("wandresurrection")
					new /obj/item/weapon/gun/magic/wand/resurrection(get_turf(H))
				if("wandpolymorph")
					new /obj/item/weapon/gun/magic/wand/polymorph(get_turf(H))
				if("wandteleport")
					new /obj/item/weapon/gun/magic/wand/teleport(get_turf(H))
				if("wanddoor")
					new /obj/item/weapon/gun/magic/wand/door(get_turf(H))
				if("wandfireball")
					new /obj/item/weapon/gun/magic/wand/fireball(get_turf(H))
				if("staffhealing")
					new /obj/item/weapon/gun/magic/staff/healing(get_turf(H))
				if("staffdoor")
					new /obj/item/weapon/gun/magic/staff/door(get_turf(H))
				if("armor")
					new /obj/item/clothing/suit/space/hardsuit/wizard(get_turf(H))
				if("scrying")
					new /obj/item/weapon/scrying(get_turf(H))
					if (!(H.dna.check_mutation(XRAY)))
						H.dna.add_mutation(XRAY)
						H << "The walls suddenly disappear."
				if("voodoo")
					new /obj/item/voodoo(get_turf(H))
				if("special")
					magiclist -= "special" //only one super OP item per summoning max
					switch (randomizemagicspecial)
						if("staffchange")
							new /obj/item/weapon/gun/magic/staff/change(get_turf(H))
						if("staffanimation")
							new /obj/item/weapon/gun/magic/staff/animate(get_turf(H))
						if("wandbelt")
							new /obj/item/weapon/storage/belt/wands/full(get_turf(H))
						if("contract")
							new /obj/item/weapon/antag_spawner/contract(get_turf(H))
						if("staffchaos")
							new /obj/item/weapon/gun/magic/staff/chaos(get_turf(H))
						if("necromantic")
							new /obj/item/device/necromantic_stone(get_turf(H))
					H << "You suddenly feel lucky."
			playsound(get_turf(H),'sound/magic/Summon_Magic.ogg', 50, 1)

/proc/summonevents()
	if(!SSevent.wizardmode)
		SSevent.frequency_lower = 600									//1 minute lower bound
		SSevent.frequency_upper = 3000									//5 minutes upper bound
		SSevent.toggleWizardmode()
		SSevent.reschedule()

	else 																//Speed it up
		SSevent.frequency_upper -= 600	//The upper bound falls a minute each time, making the AVERAGE time between events lessen
		if(SSevent.frequency_upper < SSevent.frequency_lower) //Sanity
			SSevent.frequency_upper = SSevent.frequency_lower

		SSevent.reschedule()
		message_admins("Summon Events intensifies, events will now occur every [SSevent.frequency_lower / 600] to [SSevent.frequency_upper / 600] minutes.")
		log_game("Summon Events was increased!")


code:
//Santa spells!
/obj/effect/proc_holder/spell/aoe_turf/conjure/presents
	name = "Conjure Presents!"
	desc = "This spell lets you reach into S-space and retrieve presents! Yay!"
	school = "santa"
	charge_max = 600
	clothes_req = 0
	invocation = "HO HO HO"
	invocation_type = "shout"
	range = 3
	cooldown_min = 50

	summon_type = list("/obj/item/weapon/a_gift")
	summon_lifespan = 0
	summon_amt = 5


code:
/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/shapeshift_type
	var/list/current_shapes = list()
	var/list/current_casters = list()
	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/construct/armored)

/obj/effect/proc_holder/spell/targeted/shapeshift/cast(list/targets,mob/user = usr)
	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
			shapeshift_type = input(M, "Choose Your Animal Form!", "It's Morphing Time!", null) as anything in animal_list
			if(!shapeshift_type) //If you aren't gonna decide I am!
				shapeshift_type = pick(animal_list)
			shapeshift_type = animal_list[shapeshift_type]
		if(M in current_shapes)
			Restore(M)
		else
			Shapeshift(M)

/obj/effect/proc_holder/spell/targeted/shapeshift/proc/Shapeshift(mob/living/caster)
	for(var/mob/living/M in caster)
		if(M.status_flags & GODMODE)
			caster << "You're already shapeshifted!"
			return

	var/mob/living/shape = new shapeshift_type(caster.loc)
	caster.loc = shape
	caster.status_flags |= GODMODE

	current_shapes |= shape
	current_casters |= caster
	clothes_req = 0
	human_req = 0

	caster.mind.transfer_to(shape)

/obj/effect/proc_holder/spell/targeted/shapeshift/proc/Restore(mob/living/shape)
	var/mob/living/caster
	for(var/mob/living/M in shape)
		if(M in current_casters)
			caster = M
			break
	if(!caster)
		return
	caster.loc = shape.loc
	caster.status_flags &= ~GODMODE

	clothes_req = initial(clothes_req)
	human_req = initial(human_req)
	current_casters.Remove(caster)
	current_shapes.Remove(shape)

	shape.mind.transfer_to(caster)
	qdel(shape) //Gib it maybe ?


code:
/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_hand(),L.get_inactive_hand())
		var/butterfingers = 0
		var/message

		if(!marked_item) //linking item to the spell
			message = ""
			for(var/obj/item in hand_items)
				if(ABSTRACT in item.flags)
					continue
				if(NODROP in item.flags)
					message += "Though it feels redundant, "
				marked_item = 		item
				message += "You mark [item] for recall."
				name = "Recall [item]"
				break

			if(!marked_item)
				if(hand_items)
					message = "You aren't holding anything that can be marked for recall."
				else
					message = "You must hold the desired item in your hands to mark it for recall."

		else if(marked_item && marked_item in hand_items) //unlinking item to the spell
			message = "You remove the mark on [marked_item] to use elsewhere."
			name = "Instant Summons"
			marked_item = 		null

		else if(marked_item && qdeleted(marked_item)) //the item was destroyed at some point
			message = "You sense your marked item has been destroyed!"
			name = "Instant Summons"
			marked_item = 		null

		else	//Getting previously marked item
			var/obj/item_to_retrive = 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_retrive.loc)
				if(isorgan(item_to_retrive)) // Organs are usually stored in nullspace
					var/obj/item/organ/internal/organ = item_to_retrive
					if(organ.owner)
						// If this code ever runs I will be happy
						add_logs(L, organ.owner, "magically removed [organ.name] from", addition="INTENT: [uppertext(L.a_intent)]")
						organ.Remove(organ.owner)
			else
				while(!isturf(item_to_retrive.loc) && infinite_recursion < 10) //if it's in something you get the whole thing.
					if(ismob(item_to_retrive.loc)) //If its on someone, properly drop it
						var/mob/M = item_to_retrive.loc

						if(issilicon(M)) //Items in silicons warp the whole silicon
							M.loc.visible_message("[M] suddenly disappears!")
							M.loc = L.loc
							M.loc.visible_message("[M] suddenly appears!")
							item_to_retrive = null
							break
						M.unEquip(item_to_retrive)

						if(iscarbon(M)) //Edge case housekeeping
							var/mob/living/carbon/C = M
							if(C.stomach_contents && item_to_retrive in C.stomach_contents)
								C.stomach_contents -= item_to_retrive

					else
						if(istype(item_to_retrive.loc,/obj/machinery/portable_atmospherics/)) //Edge cases for moved machinery
							var/obj/machinery/portable_atmospherics/P = item_to_retrive.loc
							P.disconnect()
							P.update_icon()

						item_to_retrive = item_to_retrive.loc

					infinite_recursion += 1

			if(!item_to_retrive)
				return

			if(item_to_retrive.loc)
				item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly disappears!")

			if(L.hand) //left active hand
				if(!L.equip_to_slot_if_possible(item_to_retrive, slot_l_hand, 0, 1, 1))
					if(!L.equip_to_slot_if_possible(item_to_retrive, slot_r_hand, 0, 1, 1))
						butterfingers = 1
			else			//right active hand
				if(!L.equip_to_slot_if_possible(item_to_retrive, slot_r_hand, 0, 1, 1))
					if(!L.equip_to_slot_if_possible(item_to_retrive, slot_l_hand, 0, 1, 1))
						butterfingers = 1
			if(butterfingers)
				item_to_retrive.loc = L.loc
				item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly appears!")
				playsound(get_turf(L),"sound/magic/SummonItems_generic.ogg",50,1)
			else
				item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly appears in [L]'s hand!")
				playsound(get_turf(L),"sound/magic/SummonItems_generic.ogg",50,1)

		if(message)
			L << message


code:
/obj/effect/proc_holder/spell/targeted/touch/
	var/hand_path = "/obj/item/weapon/melee/touch_attack"
	var/obj/item/weapon/melee/touch_attack/attached_hand = null
	invocation_type = "none" //you scream on connecting, not summoning
	include_user = 1
	range = -1

/obj/effect/proc_holder/spell/targeted/touch/Click(mob/user = usr)
	if(attached_hand)
		qdel(attached_hand)
		charge_counter = charge_max
		attached_hand = null
		user << "You draw the power out of your hand."
		return 0
	..()

/obj/effect/proc_holder/spell/targeted/touch/cast(list/targets,mob/user = usr)
	for(var/mob/living/carbon/C in targets)
		if(!attached_hand)
			if(!ChargeHand(C))
				return 0
	while(attached_hand) //hibernate untill the spell is actually used
		charge_counter = 0
		sleep(1)

/obj/effect/proc_holder/spell/targeted/touch/proc/ChargeHand(mob/living/carbon/user)
	var/hand_handled = 1
	attached_hand = new hand_path(src)
	if(user.hand) 	//left active hand
		if(!user.equip_to_slot_if_possible(attached_hand, slot_l_hand, 0, 1, 1))
			if(!user.equip_to_slot_if_possible(attached_hand, slot_r_hand, 0, 1, 1))
				hand_handled = 0
	else			//right active hand
		if(!user.equip_to_slot_if_possible(attached_hand, slot_r_hand, 0, 1, 1))
			if(!user.equip_to_slot_if_possible(attached_hand, slot_l_hand, 0, 1, 1))
				hand_handled = 0
	if(!hand_handled)
		qdel(attached_hand)
		charge_counter = charge_max
		attached_hand = null
		user << "Your hands are full!"
		return 0
	user << "You channel the power of the spell to your hand."
	return 1

/obj/effect/proc_holder/spell/targeted/touch/disintegrate
	name = "Disintegrate"
	desc = "This spell charges your hand with vile energy that can be used to violently explode victims."
	hand_path = "/obj/item/weapon/melee/touch_attack/disintegrate"

	school = "evocation"
	charge_max = 600
	clothes_req = 1
	cooldown_min = 200 //100 deciseconds reduction per rank

	action_icon_state = "gib"

/obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone
	name = "Flesh to Stone"
	desc = "This spell charges your hand with the power to turn victims into inert statues for a long period of time."
	hand_path = "/obj/item/weapon/melee/touch_attack/fleshtostone"

	school = "transmutation"
	charge_max = 600
	clothes_req = 1
	cooldown_min = 200 //100 deciseconds reduction per rank

	action_icon_state = "statue"
	sound = "sound/magic/FleshToStone.ogg"


code:
/obj/effect/proc_holder/spell/targeted/trigger
	name = "Trigger"
	desc = "This spell triggers another spell or a few."

	var/list/linked_spells = list() //those are just referenced by the trigger spell and are unaffected by it directly
	var/list/starting_spells = list() //those are added on New() to contents from default spells and are deleted when the trigger spell is deleted to prevent memory leaks

/obj/effect/proc_holder/spell/targeted/trigger/New()
	..()

	for(var/spell in starting_spells)
		var/spell_to_add = text2path(spell)
		new spell_to_add(src) //should result in adding to contents, needs testing

/obj/effect/proc_holder/spell/targeted/trigger/Destroy()
	for(var/spell in contents)
		qdel(spell)
	linked_spells = null
	starting_spells = null
	return ..()

/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets,mob/user = usr)
	playMagSound()
	for(var/mob/living/target in targets)
		for(var/obj/effect/proc_holder/spell/spell in contents)
			spell.perform(list(target),0)
		for(var/obj/effect/proc_holder/spell/spell in linked_spells)
			spell.perform(list(target),0)

	return


code:
/obj/effect/proc_holder/spell/targeted/turf_teleport
	name = "Turf Teleport"
	desc = "This spell teleports the target to the turf in range."
	nonabstract_req = 1

	var/inner_tele_radius = 1
	var/outer_tele_radius = 2

	var/include_space = 0 //whether it includes space tiles in possible teleport locations
	var/include_dense = 0 //whether it includes dense tiles in possible teleport locations
	var/sound1 = "sound/weapons/ZapBang.ogg"
	var/sound2 = "sound/weapons/ZapBang.ogg"

/obj/effect/proc_holder/spell/targeted/turf_teleport/cast(list/targets,mob/user = usr)
	playsound(get_turf(user), sound1, 50,1)
	for(var/mob/living/target in targets)
		var/list/turfs = new/list()
		for(var/turf/T in range(target,outer_tele_radius))
			if(T in range(target,inner_tele_radius)) continue
			if(istype(T,/turf/open/space) && !include_space) continue
			if(T.density && !include_dense) continue
			if(T.x>world.maxx-outer_tele_radius || T.x<outer_tele_radius)
				continue	//putting them at the edge is dumb
			if(T.y>world.maxy-outer_tele_radius || T.y<outer_tele_radius)
				continue
			turfs += T

		if(!turfs.len)
			var/list/turfs_to_pick_from = list()
			for(var/turf/T in orange(target,outer_tele_radius))
				if(!(T in orange(target,inner_tele_radius)))
					turfs_to_pick_from += T
			turfs += pick(/turf in turfs_to_pick_from)

		var/turf/picked = pick(turfs)

		if(!picked || !isturf(picked))
			return

		if(!target.Move(picked))
			if(target.buckled)
				target.buckled.unbuckle_mob(target,force=1)
			if(target.buckled_mobs.len)
				target.unbuckle_all_mobs(force=1)
			target.loc = picked
			playsound(get_turf(user), sound2, 50,1)


code:
/obj/effect/proc_holder/spell/targeted/projectile/magic_missile
	name = "Magic Missile"
	desc = "This spell fires several, slow moving, magic projectiles at nearby targets."

	school = "evocation"
	charge_max = 200
	clothes_req = 1
	invocation = "FORTI GY AMA"
	invocation_type = "shout"
	range = 7
	cooldown_min = 60 //35 deciseconds reduction per rank

	max_targets = 0

	proj_icon_state = "magicm"
	proj_name = "a magic missile"
	proj_lingering = 1
	proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile"

	proj_lifespan = 20
	proj_step_delay = 5

	proj_trail = 1
	proj_trail_lifespan = 5
	proj_trail_icon_state = "magicmd"

	action_icon_state = "magicm"
	sound = "sound/magic/MAGIC_MISSILE.ogg"

/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile
	amt_weakened = 3
	sound = "sound/magic/MM_Hit.ogg"

/obj/effect/proc_holder/spell/targeted/genetic/mutate
	name = "Mutate"
	desc = "This spell causes you to turn into a hulk and gain laser vision for a short while."

	school = "transmutation"
	charge_max = 400
	clothes_req = 1
	invocation = "BIRUZ BENNAR"
	invocation_type = "shout"
	range = -1
	include_user = 1
	centcom_cancast = 0

	mutations = list(LASEREYES, HULK)
	duration = 300
	cooldown_min = 300 //25 deciseconds reduction per rank

	action_icon_state = "mutate"
	sound = "sound/magic/Mutate.ogg"

/obj/effect/proc_holder/spell/targeted/smoke
	name = "Smoke"
	desc = "This spell spawns a cloud of choking smoke at your location and does not require wizard garb."

	school = "conjuration"
	charge_max = 120
	clothes_req = 0
	invocation = "none"
	invocation_type = "none"
	range = -1
	include_user = 1
	cooldown_min = 20 //25 deciseconds reduction per rank

	smoke_spread = 2
	smoke_amt = 4

	action_icon_state = "smoke"

/obj/effect/proc_holder/spell/targeted/emplosion/disable_tech
	name = "Disable Tech"
	desc = "This spell disables all weapons, cameras and most other technology in range."
	charge_max = 400
	clothes_req = 1
	invocation = "NEC CANTIO"
	invocation_type = "shout"
	range = -1
	include_user = 1
	cooldown_min = 200 //50 deciseconds reduction per rank

	emp_heavy = 6
	emp_light = 10
	sound = "sound/magic/Disable_Tech.ogg"

/obj/effect/proc_holder/spell/targeted/turf_teleport/blink
	name = "Blink"
	desc = "This spell randomly teleports you a short distance."

	school = "abjuration"
	charge_max = 20
	clothes_req = 1
	invocation = "none"
	invocation_type = "none"
	range = -1
	include_user = 1
	cooldown_min = 5 //4 deciseconds reduction per rank

	smoke_spread = 1
	smoke_amt = 0

	inner_tele_radius = 0
	outer_tele_radius = 6

	centcom_cancast = 0 //prevent people from getting to centcom

	action_icon_state = "blink"
	sound1="sound/magic/blink.ogg"
	sound2="sound/magic/blink.ogg"

/obj/effect/proc_holder/spell/targeted/turf_teleport/blink/cult
	name = "quickstep"

	charge_max = 100
	clothes_req = 0
	cult_req = 1

/obj/effect/proc_holder/spell/targeted/area_teleport/teleport
	name = "Teleport"
	desc = "This spell teleports you to a type of area of your selection."

	school = "abjuration"
	charge_max = 600
	clothes_req = 1
	invocation = "SCYAR NILA"
	invocation_type = "shout"
	range = -1
	include_user = 1
	cooldown_min = 200 //100 deciseconds reduction per rank

	smoke_spread = 1
	smoke_amt = 2
	sound1="sound/magic/Teleport_diss.ogg"
	sound2="sound/magic/Teleport_app.ogg"

/obj/effect/proc_holder/spell/aoe_turf/conjure/forcewall
	name = "Forcewall"
	desc = "This spell creates an unbreakable wall that lasts for 30 seconds and does not need wizard garb."

	school = "transmutation"
	charge_max = 100
	clothes_req = 0
	invocation = "TARCOL MINTI ZHERI"
	invocation_type = "whisper"
	range = 0
	cooldown_min = 50 //12 deciseconds reduction per rank

	summon_type = list("/obj/effect/forcefield")
	summon_lifespan = 300

	action_icon_state = "shield"
	cast_sound = "sound/magic/ForceWall.ogg"

/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop
	name = "Stop Time"
	desc = "This spell stops time for everyone except for you, allowing you to move freely while your enemies and even projectiles are frozen."
	charge_max = 500
	clothes_req = 1
	invocation = "TOKI WO TOMARE"
	invocation_type = "shout"
	range = 0
	cooldown_min = 100
	summon_amt = 1
	action_icon_state = "time"

	summon_type = list(/obj/effect/timestop/wizard)

/obj/effect/proc_holder/spell/aoe_turf/conjure/carp
	name = "Summon Carp"
	desc = "This spell conjures a simple carp."

	school = "conjuration"
	charge_max = 1200
	clothes_req = 1
	invocation = "NOUK FHUNMM SACP RISSKA"
	invocation_type = "shout"
	range = 1

	summon_type = list(/mob/living/simple_animal/hostile/carp)
	cast_sound = "sound/magic/Summon_Karp.ogg"

/obj/effect/proc_holder/spell/aoe_turf/conjure/construct
	name = "Artificer"
	desc = "This spell conjures a construct which may be controlled by Shades"

	school = "conjuration"
	charge_max = 600
	clothes_req = 0
	invocation = "none"
	invocation_type = "none"
	range = 0

	summon_type = list(/obj/structure/constructshell)

	action_icon_state = "artificer"
	cast_sound = "sound/magic/SummonItems_generic.ogg"

/obj/effect/proc_holder/spell/aoe_turf/conjure/creature
	name = "Summon Creature Swarm"
	desc = "This spell tears the fabric of reality, allowing horrific daemons to spill forth"

	school = "conjuration"
	charge_max = 1200
	clothes_req = 0
	invocation = "IA IA"
	invocation_type = "shout"
	summon_amt = 10
	range = 3

	summon_type = list(/mob/living/simple_animal/hostile/creature)
	cast_sound = "sound/magic/SummonItems_generic.ogg"

/obj/effect/proc_holder/spell/targeted/trigger/blind
	name = "Blind"
	desc = "This spell temporarily blinds a single person and does not require wizard garb."

	school = "transmutation"
	charge_max = 300
	clothes_req = 0
	invocation = "STI KALY"
	invocation_type = "whisper"
	message = "Your eyes cry out in pain!"
	cooldown_min = 50 //12 deciseconds reduction per rank

	starting_spells = list("/obj/effect/proc_holder/spell/targeted/inflict_handler/blind","/obj/effect/proc_holder/spell/targeted/genetic/blind")

	action_icon_state = "blind"

/obj/effect/proc_holder/spell/aoe_turf/conjure/creature/cult
	name = "Summon Creatures (DANGEROUS)"
	cult_req = 1
	charge_max = 5000
	summon_amt = 2

/obj/effect/proc_holder/spell/targeted/inflict_handler/blind
	amt_eye_blind = 10
	amt_eye_blurry = 20
	sound="sound/magic/Blind.ogg"

/obj/effect/proc_holder/spell/targeted/genetic/blind
	mutations = list(BLINDMUT)
	duration = 300
	sound="sound/magic/Blind.ogg"

/obj/effect/proc_holder/spell/dumbfire/fireball
	name = "Fireball"
	desc = "This spell fires a fireball at a target and does not require wizard garb."

	school = "evocation"
	charge_max = 60
	clothes_req = 0
	invocation = "ONI SOMA"
	invocation_type = "shout"
	range = 20
	cooldown_min = 20 //10 deciseconds reduction per rank

	proj_icon_state = "fireball"
	proj_name = "a fireball"
	proj_type = "/obj/effect/proc_holder/spell/turf/fireball"

	proj_lifespan = 200
	proj_step_delay = 1

	action_icon_state = "fireball"
	sound = "sound/magic/Fireball.ogg"

/obj/effect/proc_holder/spell/turf/fireball/cast(turf/T,mob/user = usr)
	explosion(T, -1, 0, 2, 3, 0, flame_range = 2)

/obj/effect/proc_holder/spell/targeted/inflict_handler/fireball
	amt_dam_brute = 20
	amt_dam_fire = 25

/obj/effect/proc_holder/spell/targeted/explosion/fireball
	ex_severe = -1
	ex_heavy = -1
	ex_light = 2
	ex_flash = 5

/obj/effect/proc_holder/spell/aoe_turf/repulse
	name = "Repulse"
	desc = "This spell throws everything around the user away."
	charge_max = 400
	clothes_req = 1
	invocation = "GITTAH WEIGH"
	invocation_type = "shout"
	range = 5
	cooldown_min = 150
	selection_type = "view"
	sound = 'sound/magic/Repulse.ogg'
	var/maxthrow = 5
	var/animation = "shieldsparkles"

	action_icon_state = "repulse"

/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets,mob/user = usr)
	var/list/thrownatoms = list()
	var/atom/throwtarget
	var/distfromcaster
	playMagSound()
	for(var/turf/T in targets) //Done this way so things don't get thrown all around hilariously.
		for(var/atom/movable/AM in T)
			thrownatoms += AM

	for(var/atom/movable/AM in thrownatoms)
		if(AM == user || AM.anchored) continue

		var/obj/effect/overlay/targeteffect	= new /obj/effect/overlay{icon='icons/effects/effects.dmi'; icon_state="shieldsparkles"; mouse_opacity=0; density = 0}()
		targeteffect.icon_state = animation
		AM.overlays += targeteffect
		throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(AM, user)))
		distfromcaster = get_dist(user, AM)
		spawn(10)
			AM.overlays -= targeteffect
			qdel(targeteffect)
		if(distfromcaster == 0)
			if(istype(AM, /mob/living))
				var/mob/living/M = AM
				M.Weaken(5)
				M.adjustBruteLoss(5)
				M << "You're slammed into the floor by [user]!"
		else
			if(istype(AM, /mob/living))
				var/mob/living/M = AM
				M.Weaken(2)
				M << "You're thrown back by [user]!"
			AM.throw_at_fast(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1,user)//So stuff gets tossed around at the same time.

/obj/effect/proc_holder/spell/aoe_turf/repulse/xeno //i fixed conflicts only to find out that this is in the WIZARD file instead of the xeno file?!
	name = "Tail Sweep"
	desc = "Throw back attackers with a sweep of your tail."
	sound = 'sound/magic/Tail_swing.ogg'
	charge_max = 150
	clothes_req = 0
	range = 2
	cooldown_min = 150
	invocation_type = "none"
	animation = "tailsweep"
	action_icon_state = "tailsweep"
	action_background_icon_state = "bg_alien"

/obj/effect/proc_holder/spell/aoe_turf/repulse/xeno/cast(list/targets,mob/user = usr)
	if(istype(user, /mob/living/carbon))
		var/mob/living/carbon/C = user
		playsound(C.loc, 'sound/voice/hiss5.ogg', 80, 1, 1)
		C.spin(6,1)
	..()


code:
proc/consonant()
	return pick("B","C","D","F","G","H","J","K","L","M","N","P","Q","R","S","T","V","W","X","Y","Z")

proc/vowel()
	return pick("A", "E", "I", "O", "U")

proc/ucfirst(var/S)
	return "[uppertext(ascii2text(text2ascii(S, 1)))][copytext(S, 2)]"

proc/ucfirsts(var/S)
	var/list/L = splittext(S, " ")
	var/list/M = list()
	for (var/P in L)
		M += ucfirst(P)
	return jointext(M, " ")

var/global/list/FrozenAccounts = list()

proc/list_frozen()
	for (var/A in FrozenAccounts)
		usr << "[A]: [length(FrozenAccounts[A])] borrows"

/datum/article
	var/headline = "Something big is happening"
	var/subtitle = "Investors panic as stock market collapses"
	var/article = "God, it's going to be fun to randomly generate this."
	var/author = "P. Pubbie"
	var/spacetime = ""
	var/opinion = 0
	var/ticks = 0
	var/datum/stock/about = null
	var/outlet = ""
	var/static/list/outlets = list()
	var/static/list/default_tokens = list( \
		"buy" = list("buy!", "buy, buy, buy!", "get in now!", "ride the share value to the stars!"), \
		"company" = list("company", "corporation", "conglomerate", "enterprise", "venture"), \
		"complete" = list("complete", "total", "absolute", "incredible"), \
		"country" = list("Space", "Argentina", "Hungary", "United States of America", "United Space", "Space Federation", "Nanotrasen", "The Wizard Federation", "United Kingdom", "Poland", "The Syndicate", "Australia", "Serbia", "The European Union", "The Illuminati", "The New World Order", "Eurasian Union", "Asian Union", "United Arab Emirates", "Arabian League", "United States of Africa", "Mars Federation", "Allied Colonies of Jupiter", "Saturn's Ring", "Fringe Republic of Formerly Planet Pluto"), \
		"development" = list("development", "unfolding of events", "turn of events", "new shit"), \
		"dip" = list("dip", "fall", "plunge", "decrease"), \
		"excited" = list("excited", "euphoric", "exhilarated", "thrilled", "stimulated"), \
		"expand_influence" = list("expands their influence over", "continues to dominate", "gains traction in", "rolls their new product line out in"), \
		"failure" = list("failure", "meltdown", "breakdown", "crash", "defeat", "wreck"), \
		"famous" = list("famous", "prominent", "leading", "renowned", "expert"), \
		"hit_shelves" = list("hit the shelves", "appeared on the market", "came out", "was released"), \
		"industry" = list("industry", "sector"), \
		"industrial" = list("industrial"), \
		"jobs" = list("workers"), \
		"negative_outcome" = list("it's not leaving the shelves", "nobody seems to care", "it's a huge money sink", "they have already pulled all advertising and marketing support"), \
		"neutral_outcome" = list("it's not lifting off as expected", "it's not selling according to expectations", "it's only generating enough profit to cover the marketing and manufacturing costs", "it does not look like it will become a massive success", "it's experiencing modest sales"), \
		"positive_outcome" = list("it's already sold out", "it has already sold over one billion units", "suppliers cannot keep up with the wild demand", "several companies using this new technology are already reporting a projected increase in profits"), \
		"resounding" = list("resounding", "tremendous", "total", "massive", "terrific", "colossal"), \
		"rise" = list("rise", "increase", "fly off the positive side of the charts", "skyrocket", "lift off"), \
		"sell" = list("sell!", "sell, sell, sell!", "bail!", "abandon ship!", "get out before it's too late!", "evacuate!", "withdraw!"), \
		"signifying" = list("signifying", "indicating", "implying", "displaying", "suggesting"), \
		"sneak_peek" = list("review", "sneak peek", "preview", "exclusive look"), \
		"stock_market" = list("stock market", "stock exchange"), \
		"stockholder" = list("stockholder", "shareholder"), \
		"success" = list("success", "triumph", "victory"), \
		"this_time" = list("this week", "last week", "this month", "yesterday", "today", "a few days ago") \
	)

/datum/article/New()
	..()
	if ((outlets.len && !prob(100 / (outlets.len + 1))) || !outlets.len)
		var/ON = generateOutletName()
		if (!(ON in outlets))
			outlets[ON] = list()
		outlet = ON
	else
		outlet = pick(outlets)

	var/list/authors = outlets[outlet]
	if ((authors.len && !prob(100 / (authors.len + 1))) || !authors.len)
		var/AN = generateAuthorName()
		outlets[outlet] += AN
		author = AN
	else
		author = pick(authors)

	ticks = world.time

/datum/article/proc/generateOutletName()
	var/list/locations = list("Earth", "Luna", "Mars", "Saturn", "Jupiter", "Uranus", "Pluto", "Europa", "Io", "Phobos", "Deimos", "Space", "Venus", "Neptune", "Mercury", "Kalliope", "Ganymede", "Callisto", "Amalthea", "Himalia", "Orion", "Sybil", "Basil", "Badger", "Terry", "Artyom")
	var/list/nouns = list("Post", "Herald", "Sun", "Tribune", "Mail", "Times", "Journal", "Report")
	var/list/timely = list("Daily", "Hourly", "Weekly", "Biweekly", "Monthly", "Yearly")

	switch(rand(1,2))
		if (1)
			return "The [pick(locations)] [pick(nouns)]"
		if (2)
			return "The [pick(timely)] [pick(nouns)]"

/datum/article/proc/generateAuthorName()
	switch(rand(1,3))
		if (1)
			return "[consonant()]. [pick(last_names)]"
		if (2)
			return "[prob(50) ? pick(first_names_male) : pick(first_names_female)] [consonant()].[prob(50) ? "[consonant()]. " : null] [pick(last_names)]"
		if (3)
			return "[prob(50) ? pick(first_names_male) : pick(first_names_female)] \"[prob(50) ? pick(first_names_male) : pick(first_names_female)]\" [pick(last_names)]"

/datum/article/proc/formatSpacetime()
	var/ticksc = round(ticks/100)
	ticksc = ticksc % 100000
	var/ticksp = "[ticksc]"
	while (length(ticksp) < 5)
		ticksp = "0[ticksp]"
	spacetime = "[ticksp][time2text(world.realtime, "MM")][time2text(world.realtime, "DD")]2556"

/datum/article/proc/formatArticle()
	if (spacetime == "")
		formatSpacetime()
	var/output = "

[headline]

[subtitle]

[article]

[author]

[spacetime]

"
	return output

/datum/article/proc/detokenize(var/token_string, var/list/industry_tokens, var/list/product_tokens = list())
	var/list/T_list = default_tokens.Copy()
	for (var/I in industry_tokens)
		T_list[I] = industry_tokens[I]
	for (var/I in product_tokens)
		T_list[I] = list(product_tokens[I])
	for (var/I in T_list)
		token_string = replacetext(token_string, "%[I]%", pick(T_list[I]))
	return ucfirst(token_string)


code:
/obj/machinery/computer/stockexchange
	name = "stock exchange computer"
	icon = 'icons/obj/computer.dmi'
	icon_state = "oldcomp"
	icon_screen = "stock_computer"
	icon_keyboard = "no_keyboard"
	var/logged_in = "Cargo Department"
	var/vmode = 0

/obj/machinery/computer/stockexchange/New()
	..()
	logged_in = "[world.name] Cargo Department"

/obj/machinery/computer/stockexchange/proc/balance()
	if (!logged_in)
		return 0
	return SSshuttle.points

/obj/machinery/computer/stockexchange/attack_hand(var/mob/user)
	if(..())
		return
	user.machine = src

	var/css={""}
	var/dat = "<title>[station_name()] Stock Exchange</title>[css]
Stock Exchange
"
	dat += "This is a work in progress. Certain features may not be available.
"

	dat += "Welcome, [logged_in]
Cargo Points: [balance()] points
"
	for (var/datum/stock/S in stockExchange.last_read)
		var/list/LR = stockExchange.last_read[S]
		if (!(logged_in in LR))
			LR[logged_in] = 0
	dat += "View mode: [vmode ? "compact" : "full"]"
	dat += "Stock Transaction Log: Check"
	dat += "
Listed stocks
"

	if (vmode == 0)
		for (var/datum/stock/S in stockExchange.stocks)
			var/mystocks = 0
			if (logged_in && (logged_in in S.shareholders))
				mystocks = S.shareholders[logged_in]
			dat += "

[S.name] ([S.short_name])[S.bankrupt ? " BANKRUPT" : null]
"
			if (S.last_unification)
				dat += "Unified shares [(world.time - S.last_unification) / 600] minutes ago.
"
			dat += "Current value per share: [S.current_value] | View history

"
			dat += "You currently own [mystocks] shares in this company. There are [S.available_shares] purchasable shares on the market currently.
"
			if (S.bankrupt)
				dat += "You cannot buy or sell shares in a bankrupt company!

"
			else
				dat += "Buy shares | Sell shares

"
			dat += "Prominent products:
"
			for (var/prod in S.products)
				dat += "[prod]
"
			dat += "
Borrow options:
"
			if (S.borrow_brokers.len)
				for (var/datum/borrow/B in S.borrow_brokers)
					dat += "[B.broker] offers [B.share_amount] shares for borrowing, for a deposit of [B.deposit * 100]% of the shares' value.
"
					dat += "The broker expects the return of the shares after [B.lease_time / 600] minutes, with a grace period of [B.grace_time / 600] minute(s).
"
					dat += "This offer expires in [(B.offer_expires - world.time) / 600] minutes.
"
					dat += "Note: If you do not return all shares by the end of the grace period, you will lose your deposit and the value of all unreturned shares at current value from your account!
"
					dat += "Note: You cannot withdraw or transfer money off your account while a borrow is active.
"
					dat += "Take offer (Estimated deposit: [B.deposit * S.current_value * B.share_amount] credits)

"
			else
				dat += "No borrow options available

"
			for (var/datum/borrow/B in S.borrows)
				if (B.borrower == logged_in)
					dat += "You are borrowing [B.share_amount] shares from [B.broker].
"
					dat += "Your deposit riding on the deal is [B.deposit] credits.
"
					if (world.time < B.lease_expires)
						dat += "You are expected to return the borrowed shares in [(B.lease_expires - world.time) / 600] minutes.

"
					else
						dat += "The brokering agency is collecting. You still owe them [B.share_debt] shares, which you have [(B.grace_expires - world.time) / 600] minutes to present.

"
			var/news = 0
			if (logged_in)
				var/list/LR = stockExchange.last_read[S]
				var/lrt = LR[logged_in]
				for (var/datum/article/A in S.articles)
					if (A.ticks > lrt)
						news = 1
						break
				if (!news)
					for (var/datum/stockEvent/E in S.events)
						if (E.last_change > lrt && !E.hidden)
							news = 1
							break
			dat += "View news archives[news ? " (updated)" : null]
"
	else if (vmode == 1)
		dat += "Actions: + Buy, - Sell, (A)rchives, (H)istory

"
		dat += "

 	Name	Value	Owned/Avail	Actions
"
		for (var/datum/stock/S in stockExchange.stocks)
			var/mystocks = 0
			if (logged_in && (logged_in in S.shareholders))
				mystocks = S.shareholders[logged_in]
			dat += "
[S.disp_value_change > 0 ? "+" : (S.disp_value_change < 0 ? "-" : "=")]	[S.name] "
			if (S.bankrupt)
				dat += "B"
			dat += " ([S.short_name])	[S.current_value]	[mystocks]/[S.available_shares]	"
			var/news = 0
			if (logged_in)
				var/list/LR = stockExchange.last_read[S]
				var/lrt = LR[logged_in]
				for (var/datum/article/A in S.articles)
					if (A.ticks > lrt)
						news = 1
						break
				if (!news)
					for (var/datum/stockEvent/E in S.events)
						if (E.last_change > lrt && !E.hidden)
							news = 1
							break
			dat += ""
			if (S.bankrupt)
				dat += "+ - "
			else
				dat += "+ - "
			dat += "(A) (H)
"
	dat += ""
	var/datum/browser/popup = new(user, "computer", "Stock Exchange", 600, 400)
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open()
	return

/obj/machinery/computer/stockexchange/proc/sell_some_shares(var/datum/stock/S, var/mob/user)
	if (!user || !S)
		return
	var/li = logged_in
	if (!li)
		user << "No active account on the console!"
		return
	var/b = SSshuttle.points
	var/avail = S.shareholders[logged_in]
	if (!avail)
		user << "This account does not own any shares of [S.name]!"
		return
	var/price = S.current_value
	var/amt = round(input(user, "How many shares? (Have: [avail], unit price: [price])", "Sell shares in [S.name]", 0) as num|null)
	if (!user)
		return
	if (!amt)
		return
	if (!(user in range(1, src)))
		return
	if (li != logged_in)
		return
	b = SSshuttle.points
	if (!isnum(b))
		user << "No active account on the console!"
		return
	if (amt > S.shareholders[logged_in])
		user << "You do not own that many shares!"
		return
	var/total = amt * S.current_value
	if (!S.sellShares(logged_in, amt))
		user << "Could not complete transaction."
		return
	user << "Sold [amt] shares of [S.name] at [S.current_value] a share for [total] points."
	stockExchange.add_log(/datum/stock_log/sell, user.name, S.name, amt, S.current_value, total)

/obj/machinery/computer/stockexchange/proc/buy_some_shares(var/datum/stock/S, var/mob/user)
	if (!user || !S)
		return
	var/li = logged_in
	if (!li)
		user << "No active account on the console!"
		return
	var/b = balance()
	if (!isnum(b))
		user << "No active account on the console!"
		return
	var/avail = S.available_shares
	var/price = S.current_value
	var/canbuy = round(b / price)
	var/amt = round(input(user, "How many shares? (Available: [avail], unit price: [price], can buy: [canbuy])", "Buy shares in [S.name]", 0) as num|null)
	if (!user)
		return
	if (!amt)
		return
	if (!(user in range(1, src)))
		return
	if (li != logged_in)
		return
	b = balance()
	if (!isnum(b))
		user << "No active account on the console!"
		return
	if (amt > S.available_shares)
		user << "That many shares are not available!"
		return
	var/total = amt * S.current_value
	if (total > b)
		user << "Insufficient points."
		return
	if (!S.buyShares(logged_in, amt))
		user << "<Could not complete transaction."
		return
	user << "Bought [amt] shares of [S.name] at [S.current_value] a share for [total] points."
	stockExchange.add_log(/datum/stock_log/buy, user.name, S.name, amt, S.current_value,  total)

/obj/machinery/computer/stockexchange/proc/do_borrowing_deal(var/datum/borrow/B, var/mob/user)
	if (B.stock.borrow(B, logged_in))
		user << "You successfully borrowed [B.share_amount] shares. Deposit: [B.deposit]."
		stockExchange.add_log(/datum/stock_log/borrow, user.name, B.stock.name, B.share_amount, B.deposit)
	else
		user << "Could not complete transaction. Check your account balance."

/obj/machinery/computer/stockexchange/Topic(href, href_list)
	if (..())
		return 1

	if (usr in range(1, src))
		usr.machine = src

	if (href_list["viewhistory"])
		var/datum/stock/S = locate(href_list["viewhistory"])
		if (S)
			S.displayValues(usr)

	if (href_list["logout"])
		logged_in = null

	if (href_list["buyshares"])
		var/datum/stock/S = locate(href_list["buyshares"])
		if (S)
			buy_some_shares(S, usr)

	if (href_list["sellshares"])
		var/datum/stock/S = locate(href_list["sellshares"])
		if (S)
			sell_some_shares(S, usr)

	if (href_list["take"])
		var/datum/borrow/B = locate(href_list["take"])
		if (B && !B.lease_expires)
			do_borrowing_deal(B, usr)

	if (href_list["show_logs"])
		var/dat = "<title>Stock Transaction Logs</title>
Stock Transaction Logs

Refresh

"
		for(var/D in stockExchange.logs)
			var/datum/stock_log/L = D
			if(istype(L, /datum/stock_log/buy))
				dat += "[L.time] | [L.user_name] bought [L.stocks] stocks at [L.shareprice] a share for [L.money] total credits in [L.company_name].
"
				continue
			if(istype(L, /datum/stock_log/sell))
				dat += "[L.time] | [L.user_name] sold [L.stocks] stocks at [L.shareprice] a share for [L.money] totalcredits from [L.company_name].
"
				continue
			if(istype(L, /datum/stock_log/borrow))
				dat += "[L.time] | [L.user_name] borrowed [L.stocks] stocks with a deposit of [L.money] credits in [L.company_name].
"
				continue
		var/datum/browser/popup = new(usr, "stock_logs", "Stock Transaction Logs", 600, 400)
		popup.set_content(dat)
		popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
		popup.open()

	if (href_list["archive"])
		var/datum/stock/S = locate(href_list["archive"])
		if (logged_in && logged_in != "")
			var/list/LR = stockExchange.last_read[S]
			LR[logged_in] = world.time
		var/dat = "<title>News feed for [S.name]</title>
News feed for [S.name]

Refresh
"
		dat += "

Events
"
		var/p = 0
		for (var/datum/stockEvent/E in S.events)
			if (E.hidden)
				continue
			if (p > 0)
				dat += "
"
			dat += "
[E.current_title]
[E.current_desc]
"
			p++
		dat += "

Articles
"
		p = 0
		for (var/datum/article/A in S.articles)
			if (p > 0)
				dat += "
"
			dat += "
[A.headline]
[A.subtitle]

[A.article]
- [A.author], [A.spacetime] (via [A.outlet])
"
			p++
		dat += "
"
		var/datum/browser/popup = new(usr, "archive_[S.name]", "Stock News", 600, 400)
		popup.set_content(dat)
		popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
		popup.open()

	if (href_list["cycleview"])
		vmode++
		if (vmode > 1)
			vmode = 0

	src.add_fingerprint(usr)
	src.updateUsrDialog()