From c54d746cdfbf5a006e3818a4e09fa57c75b8a143 Mon Sep 17 00:00:00 2001
From: DGamerL <108773801+DGamerL@users.noreply.github.com>
Date: Tue, 14 Jan 2025 21:46:36 +0100
Subject: [PATCH 1/4] Adds conical spells from TG

---
 code/datums/spell_targeting/cone.dm           | 75 +++++++++++++++++++
 code/datums/spells/cones/TESTING_cone.dm      |  1 +
 code/datums/spells/cones/cone_spell.dm        | 43 +++++++++++
 .../datums/{spell.dm => spells/spell_base.dm} |  0
 .../{spells.dm => observer_spells.dm}         |  0
 paradise.dme                                  |  7 +-
 6 files changed, 124 insertions(+), 2 deletions(-)
 create mode 100644 code/datums/spell_targeting/cone.dm
 create mode 100644 code/datums/spells/cones/TESTING_cone.dm
 create mode 100644 code/datums/spells/cones/cone_spell.dm
 rename code/datums/{spell.dm => spells/spell_base.dm} (100%)
 rename code/modules/mob/dead/observer/{spells.dm => observer_spells.dm} (100%)

diff --git a/code/datums/spell_targeting/cone.dm b/code/datums/spell_targeting/cone.dm
new file mode 100644
index 0000000000000..7bd690add87ed
--- /dev/null
+++ b/code/datums/spell_targeting/cone.dm
@@ -0,0 +1,75 @@
+
+/datum/spell_targeting/cone
+	max_targets = INFINITY
+	use_intercept_click = TRUE
+	/// This controls how many levels the cone has. Increase this value to make a bigger cone.
+	var/cone_levels = 3
+	/// This value determines if the cone penetrates walls.
+	var/respect_density = FALSE
+
+/datum/spell_targeting/cone/choose_targets(mob/user, datum/spell/spell, params, atom/clicked_atom)
+	var/list/turfs_to_return = list()
+	var/turf/turf_to_use = get_turf(user)
+	var/turf/left_turf
+	var/turf/right_turf
+	var/dir_to_use = get_dir(user, clicked_atom)
+	var/right_dir
+	var/left_dir
+	switch(dir_to_use)
+		if(NORTH)
+			left_dir = WEST
+			right_dir = EAST
+		if(SOUTH)
+			left_dir = EAST
+			right_dir = WEST
+		if(EAST)
+			left_dir = NORTH
+			right_dir = SOUTH
+		if(WEST)
+			left_dir = SOUTH
+			right_dir = NORTH
+
+	// Go though every level of the cone levels and generate the cone.
+	for(var/level in 1 to cone_levels)
+		var/list/level_turfs = list()
+		// Our center turf always exists, it's straight ahead of the caster.
+		turf_to_use = get_step(turf_to_use, dir_to_use)
+		level_turfs += turf_to_use
+		// Level 1 only ever has 1 turf, it's a cone.
+		if(level != 1)
+			var/level_width_in_each_direction = round((calculate_cone_shape(level) - 1) / 2)
+			left_turf = turf_to_use
+			right_turf = turf_to_use
+			// Check turfs to the left...
+			for(var/left_of_center in 1 to level_width_in_each_direction)
+				if(respect_density && left_turf.density)
+					break
+				left_turf = get_step(left_turf, left_dir)
+				level_turfs += left_turf
+			// And turfs to the right.
+			for(var/right_of_enter in 1 to level_width_in_each_direction)
+				if(respect_density && right_turf.density)
+					break
+				right_turf = get_step(right_turf, right_dir)
+				level_turfs += right_turf
+		// Add the list of all turfs on this level to the turfs to return
+		turfs_to_return += list(level_turfs)
+
+		// If we're at the last level, we're done
+		if(level == cone_levels)
+			break
+		// But if we're not at the last level, we should check that we can keep going
+		if(respect_density && turf_to_use.density)
+			break
+
+	return turfs_to_return
+
+/**
+ * Adjusts the width of the cone at the passed level.
+ * This is never called on the first level of the cone (level 1 is always 1 width)
+ *
+ * Return a number - the TOTAL width of the cone at the passed level.
+ */
+/datum/spell_targeting/cone/proc/calculate_cone_shape(current_level)
+	// Default formula: (1 (innate) -> 3 -> 5 -> 5 -> 7 -> 7 -> 9 -> 9 -> ...)
+	return current_level + (current_level % 2) + 1
diff --git a/code/datums/spells/cones/TESTING_cone.dm b/code/datums/spells/cones/TESTING_cone.dm
new file mode 100644
index 0000000000000..f869c946ac331
--- /dev/null
+++ b/code/datums/spells/cones/TESTING_cone.dm
@@ -0,0 +1 @@
+/datum/spell/cone/cold
diff --git a/code/datums/spells/cones/cone_spell.dm b/code/datums/spells/cones/cone_spell.dm
new file mode 100644
index 0000000000000..0968522ebd0f4
--- /dev/null
+++ b/code/datums/spells/cones/cone_spell.dm
@@ -0,0 +1,43 @@
+/datum/spell/cone
+
+
+/datum/spell/cone/create_new_targeting()
+	return new /datum/spell_targeting/cone
+
+/datum/spell/cone/cast(list/targets, mob/user)
+	for(var/list/turf_list in targets)
+		do_cone_effects(turf_list, user)
+
+/datum/spell/cone/proc/do_cone_effects(list/targets, mob/user)
+	for(var/turf/target_turf as anything in targets)
+		if(QDELETED(target_turf)) //if turf is no longer there
+			continue
+
+		do_turf_cone_effect(target_turf, user, spell_level)
+		if(iswallturf(target_turf))
+			continue
+
+		for(var/atom/movable/movable_content as anything in target_turf)
+			if(isobj(movable_content))
+				do_obj_cone_effect(movable_content, user, spell_level)
+			else if(isliving(movable_content))
+				do_mob_cone_effect(movable_content, user, spell_level)
+
+/datum/spell/cone/proc/do_turf_cone_effect(turf/target_turf, mob/caster, level)
+	target_turf.color = COLOR_RED
+
+/datum/spell/cone/proc/do_obj_cone_effect(obj/target_obj, mob/caster, level)
+	return
+
+/datum/spell/cone/proc/do_mob_cone_effect(mob/target_mob, mob/caster, level)
+	target_mob.color = COLOR_GREEN
+
+/datum/spell/cone/staggered
+	/// The delay between each cone level triggering.
+	var/delay_between_level = 0.2 SECONDS
+
+/datum/spell/cone/staggered/cast(list/targets, mob/user)
+	var/level_counter = 0
+	for(var/list/turf_list in targets)
+		level_counter++
+		addtimer(CALLBACK(src, PROC_REF(do_cone_effects), turf_list, user, level_counter), delay_between_level * level_counter)
diff --git a/code/datums/spell.dm b/code/datums/spells/spell_base.dm
similarity index 100%
rename from code/datums/spell.dm
rename to code/datums/spells/spell_base.dm
diff --git a/code/modules/mob/dead/observer/spells.dm b/code/modules/mob/dead/observer/observer_spells.dm
similarity index 100%
rename from code/modules/mob/dead/observer/spells.dm
rename to code/modules/mob/dead/observer/observer_spells.dm
diff --git a/paradise.dme b/paradise.dme
index 90e5468b42641..44e48e058c960 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -418,7 +418,6 @@
 #include "code\datums\ruins.dm"
 #include "code\datums\shuttles.dm"
 #include "code\datums\spawners_menu.dm"
-#include "code\datums\spell.dm"
 #include "code\datums\station_state.dm"
 #include "code\datums\tgs_event_handler.dm"
 #include "code\datums\verb_callbacks.dm"
@@ -593,6 +592,7 @@
 #include "code\datums\spell_targeting\aoe.dm"
 #include "code\datums\spell_targeting\click_spell_targeting.dm"
 #include "code\datums\spell_targeting\clicked_atom.dm"
+#include "code\datums\spell_targeting\cone.dm"
 #include "code\datums\spell_targeting\matter_eater_targeting.dm"
 #include "code\datums\spell_targeting\reachable_turfs.dm"
 #include "code\datums\spell_targeting\remoteview_targeting.dm"
@@ -635,6 +635,7 @@
 #include "code\datums\spells\sentient_sword_lunge.dm"
 #include "code\datums\spells\shapeshift.dm"
 #include "code\datums\spells\spacetime_dist.dm"
+#include "code\datums\spells\spell_base.dm"
 #include "code\datums\spells\summon_supermatter.dm"
 #include "code\datums\spells\summonitem.dm"
 #include "code\datums\spells\touch_attacks.dm"
@@ -653,6 +654,8 @@
 #include "code\datums\spells\alien_spells\tail_lash.dm"
 #include "code\datums\spells\alien_spells\transfer_plasma.dm"
 #include "code\datums\spells\alien_spells\whisper.dm"
+#include "code\datums\spells\cones\cone_spell.dm"
+#include "code\datums\spells\cones\TESTING_cone.dm"
 #include "code\datums\station_traits\_station_trait.dm"
 #include "code\datums\station_traits\admin_panel.dm"
 #include "code\datums\station_traits\negative_traits.dm"
@@ -2217,8 +2220,8 @@
 #include "code\modules\mob\dead\observer\observer_login.dm"
 #include "code\modules\mob\dead\observer\observer_logout.dm"
 #include "code\modules\mob\dead\observer\observer_say.dm"
+#include "code\modules\mob\dead\observer\observer_spells.dm"
 #include "code\modules\mob\dead\observer\orbit.dm"
-#include "code\modules\mob\dead\observer\spells.dm"
 #include "code\modules\mob\living\autohiss.dm"
 #include "code\modules\mob\living\damage_procs.dm"
 #include "code\modules\mob\living\death.dm"

From c84850e603002c79d7a23ea4993a7873a7729a9c Mon Sep 17 00:00:00 2001
From: DGamerL <108773801+DGamerL@users.noreply.github.com>
Date: Tue, 14 Jan 2025 21:48:22 +0100
Subject: [PATCH 2/4] Remove testing stuff

---
 code/datums/spells/cones/TESTING_cone.dm | 1 -
 code/datums/spells/cones/cone_spell.dm   | 4 ++--
 paradise.dme                             | 1 -
 3 files changed, 2 insertions(+), 4 deletions(-)
 delete mode 100644 code/datums/spells/cones/TESTING_cone.dm

diff --git a/code/datums/spells/cones/TESTING_cone.dm b/code/datums/spells/cones/TESTING_cone.dm
deleted file mode 100644
index f869c946ac331..0000000000000
--- a/code/datums/spells/cones/TESTING_cone.dm
+++ /dev/null
@@ -1 +0,0 @@
-/datum/spell/cone/cold
diff --git a/code/datums/spells/cones/cone_spell.dm b/code/datums/spells/cones/cone_spell.dm
index 0968522ebd0f4..7d9194b233a51 100644
--- a/code/datums/spells/cones/cone_spell.dm
+++ b/code/datums/spells/cones/cone_spell.dm
@@ -24,13 +24,13 @@
 				do_mob_cone_effect(movable_content, user, spell_level)
 
 /datum/spell/cone/proc/do_turf_cone_effect(turf/target_turf, mob/caster, level)
-	target_turf.color = COLOR_RED
+	return
 
 /datum/spell/cone/proc/do_obj_cone_effect(obj/target_obj, mob/caster, level)
 	return
 
 /datum/spell/cone/proc/do_mob_cone_effect(mob/target_mob, mob/caster, level)
-	target_mob.color = COLOR_GREEN
+	return
 
 /datum/spell/cone/staggered
 	/// The delay between each cone level triggering.
diff --git a/paradise.dme b/paradise.dme
index 44e48e058c960..faabc0767d376 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -655,7 +655,6 @@
 #include "code\datums\spells\alien_spells\transfer_plasma.dm"
 #include "code\datums\spells\alien_spells\whisper.dm"
 #include "code\datums\spells\cones\cone_spell.dm"
-#include "code\datums\spells\cones\TESTING_cone.dm"
 #include "code\datums\station_traits\_station_trait.dm"
 #include "code\datums\station_traits\admin_panel.dm"
 #include "code\datums\station_traits\negative_traits.dm"

From 795274ce9cd9cfa67c6e2282a6cef9fa939f897e Mon Sep 17 00:00:00 2001
From: DGamerL <108773801+DGamerL@users.noreply.github.com>
Date: Tue, 14 Jan 2025 21:52:50 +0100
Subject: [PATCH 3/4] Errant whitespace

---
 code/datums/spell_targeting/cone.dm | 1 -
 1 file changed, 1 deletion(-)

diff --git a/code/datums/spell_targeting/cone.dm b/code/datums/spell_targeting/cone.dm
index 7bd690add87ed..7cc9b7a956100 100644
--- a/code/datums/spell_targeting/cone.dm
+++ b/code/datums/spell_targeting/cone.dm
@@ -1,4 +1,3 @@
-
 /datum/spell_targeting/cone
 	max_targets = INFINITY
 	use_intercept_click = TRUE

From 9b2f9e6e018a546b8d6b211b7ab6af2b9869f4ed Mon Sep 17 00:00:00 2001
From: DGamerL <108773801+DGamerL@users.noreply.github.com>
Date: Tue, 14 Jan 2025 21:55:03 +0100
Subject: [PATCH 4/4] Fix levels

---
 code/datums/spells/cones/cone_spell.dm | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/code/datums/spells/cones/cone_spell.dm b/code/datums/spells/cones/cone_spell.dm
index 7d9194b233a51..11b249c9f0d3d 100644
--- a/code/datums/spells/cones/cone_spell.dm
+++ b/code/datums/spells/cones/cone_spell.dm
@@ -5,23 +5,25 @@
 	return new /datum/spell_targeting/cone
 
 /datum/spell/cone/cast(list/targets, mob/user)
+	var/level_counter = 1
 	for(var/list/turf_list in targets)
-		do_cone_effects(turf_list, user)
+		do_cone_effects(turf_list, user, level_counter)
+		level_counter++
 
-/datum/spell/cone/proc/do_cone_effects(list/targets, mob/user)
+/datum/spell/cone/proc/do_cone_effects(list/targets, mob/user, level)
 	for(var/turf/target_turf as anything in targets)
 		if(QDELETED(target_turf)) //if turf is no longer there
 			continue
 
-		do_turf_cone_effect(target_turf, user, spell_level)
+		do_turf_cone_effect(target_turf, user, level)
 		if(iswallturf(target_turf))
 			continue
 
 		for(var/atom/movable/movable_content as anything in target_turf)
 			if(isobj(movable_content))
-				do_obj_cone_effect(movable_content, user, spell_level)
+				do_obj_cone_effect(movable_content, user, level)
 			else if(isliving(movable_content))
-				do_mob_cone_effect(movable_content, user, spell_level)
+				do_mob_cone_effect(movable_content, user, level)
 
 /datum/spell/cone/proc/do_turf_cone_effect(turf/target_turf, mob/caster, level)
 	return