diff --git a/code/__DEFINES/roguetown.dm b/code/__DEFINES/roguetown.dm
index 210b8cbdc..b72e6285e 100644
--- a/code/__DEFINES/roguetown.dm
+++ b/code/__DEFINES/roguetown.dm
@@ -87,3 +87,8 @@ GLOBAL_LIST_EMPTY(job_respawn_delays)
#define TRIUMPH_CAT_CHARACTER "CHARACTER"
#define TRIUMPH_CAT_MISC "MISC!"
#define TRIUMPH_CAT_ACTIVE_DATUMS "ACTIVE"
+
+/*
+ Learnable arcane spell books
+*/
+GLOBAL_LIST_INIT(learnables, list())
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 43eea5562..5249fa8cd 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -89,6 +89,9 @@
if(patron.preference_accessible)
GLOB.preference_patrons[path] = patron
+ // Learnable Arcane Spells
+ GLOB.learnables = Get_Learnable_Spells()
+
//creates every subtype of prototype (excluding prototype) and adds it to list L.
//if no list/L is provided, one is created.
/proc/init_subtypes(prototype, list/L)
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 5bc3c6d07..9f381bb5c 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -50,6 +50,15 @@
else if(x<0)
.+=360
+//Get the all the damn arcane spells
+/proc/Get_Learnable_Spells()
+ var/ret = list()
+ for(var/S in GLOB.spells)
+ var/obj/effect/proc_holder/spell/arcane/spell = S
+ if(spell.learnable)
+ ret += spell
+ return ret
+
//Returns location. Returns null if no location was found.
/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = FALSE, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0)
/*
diff --git a/code/datums/status_effects/rogue/roguebuff.dm b/code/datums/status_effects/rogue/roguebuff.dm
index aa0c7cce4..a03323aeb 100644
--- a/code/datums/status_effects/rogue/roguebuff.dm
+++ b/code/datums/status_effects/rogue/roguebuff.dm
@@ -146,3 +146,25 @@
name = "Dazed"
desc = ""
icon_state = "weed"
+
+/atom/movable/screen/alert/status_effect/buff/feather
+ name = "Featherweight"
+ desc = "My body is light, I can jump higher and falling from heights is not painful."
+ icon_state = "buff"
+
+/datum/status_effect/buff/feather
+ id = "feather"
+ alert_type = /atom/movable/screen/alert/status_effect/buff/feather
+ duration = 1 MINUTES
+
+/datum/status_effect/buff/feather/on_apply()
+ . = ..()
+ to_chat(owner, span_warning("I feel lighter."))
+ ADD_TRAIT(owner, TRAIT_NOFALLDAMAGE1, MAGIC_TRAIT)
+ ADD_TRAIT(owner, TRAIT_ZJUMP, MAGIC_TRAIT)
+
+/datum/status_effect/buff/feather/on_remove()
+ . = ..()
+ to_chat(owner, span_warning("The feeling of lightness fades."))
+ REMOVE_TRAIT(owner, TRAIT_NOFALLDAMAGE1, MAGIC_TRAIT)
+ REMOVE_TRAIT(owner, TRAIT_ZJUMP, MAGIC_TRAIT)
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 116bdb627..ef22ca36e 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -362,7 +362,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark)
icon_state = "arrow"
/obj/effect/landmark/start/wapprentice
- name = "Magician's Apprentice"
+ name = "Magician Apprentice"
icon_state = "arrow"
/obj/effect/landmark/start/servant
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index 32e3219d1..4756205af 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -9,6 +9,7 @@
var/reading = FALSE //sanity
var/oneuse = TRUE //default this is true, but admins can var this to 0 if we wanna all have a pass around of the rod form book
var/used = FALSE //only really matters if oneuse but it might be nice to know if someone's used it for admin investigations perhaps
+ var/open = FALSE
/obj/item/book/granter/proc/turn_page(mob/user)
playsound(user, pick('sound/blank.ogg'), 30, TRUE)
@@ -124,6 +125,7 @@
var/spell
var/spellname = "conjure bugs"
+/*
/obj/item/book/granter/spell/already_known(mob/user)
if(!spell)
return TRUE
@@ -154,6 +156,7 @@
..()
if(oneuse)
user.visible_message(span_warning("[src] glows dark for a second!"))
+*/
/obj/item/book/granter/spell/fireball
spell = /obj/effect/proc_holder/spell/aimed/fireball
@@ -508,3 +511,111 @@
spellname = "Bone Chill"
icon_state ="scrolldarkred"
remarks = list("Mediolanum ventis..", "Sana damnatorum..", "Frigidus ossa mortuorum..")
+
+//! --SPELL BOOKS-- !/
+
+/obj/item/book/granter/spell/generic
+ name = "Spellbook"
+ desc = "A book of potential known only to those that can decipher its secrets."
+ icon = 'icons/roguetown/items/books.dmi'
+ icon_state = "spell_book_0"
+ var/base_icon_state = "spell_book"
+ oneuse = TRUE
+ dropshrink = 0.6
+ drop_sound = 'sound/foley/dropsound/paper_drop.ogg'
+ pickup_sound = 'sound/blank.ogg'
+ force = 6
+ bookfile = "spellbook.json"
+ var/obj/effect/proc_holder/spell/arcane/target = null
+
+/obj/item/book/granter/spell/generic/Initialize(loc)
+ . = ..()
+ target = pick(GLOB.learnables)
+ var/obj/effect/proc_holder/spell/arcane/S = new target
+ spellname = S.name
+ name = "Tome of [spellname]"
+//----------------------------------------------
+
+/obj/item/book/granter/spell/generic/attack_self(mob/user)
+ if(!open)
+ attack_right(user)
+ return
+ ..()
+ user.update_inv_hands()
+
+/obj/item/book/rogue/rmb_self(mob/user)
+ attack_right(user)
+ return
+
+/obj/item/book/rogue/read(mob/user)
+ if(!open)
+ to_chat(user, span_info("Open me first."))
+ return FALSE
+ . = ..()
+
+/obj/item/book/granter/spell/generic/attackby(obj/item/I, mob/user, params)
+ return
+
+/obj/item/book/granter/spell/generic/attack_right(mob/user)
+ if(!open)
+ slot_flags &= ~ITEM_SLOT_HIP
+ open = TRUE
+ playsound(loc, 'sound/items/book_open.ogg', 100, FALSE, -1)
+/* if(user.mind.get_skill_level(/datum/skill/magic/arcane) >= 1 && user.mind.get_skill_level(/datum/skill/misc/reading) >= 3)//
+ to_chat(user, "You can grasp the arcane knowledge within this tome...")//
+ playsound(loc, 'sound/magic/churn.ogg', 100, FALSE, -1)//
+ if(user.ranged_ability == !null)//
+ user.ranged_ability.deactivate(user.ranged_ability)//
+ user.ranged_ability = target//
+ user.ranged_ability.active = TRUE//
+ user.mmb_intent_change(QINTENT_SPELL)//
+Was trying to make so you can cast the spell holding the open book but I failed miserably...*/
+ else
+ slot_flags |= ITEM_SLOT_HIP
+ open = FALSE
+ playsound(loc, 'sound/items/book_close.ogg', 100, FALSE, -1)
+// if (user.ranged_ability == target)//
+// user.mmb_intent_change(null)//
+ curpage = 1
+ update_icon()
+ user.update_inv_hands()
+
+/obj/item/book/granter/spell/generic/update_icon()
+ icon_state = "[base_icon_state]_[open]"
+
+//----------------------------------------------
+/obj/item/book/granter/spell/generic/already_known(mob/user)
+ if(!target)
+ return TRUE
+ for(var/obj/effect/proc_holder/spell/arcane/knownspell in user.mind.spell_list)
+ if(knownspell.type == target)
+ to_chat(user,"You already know this one!")
+ return TRUE
+ return FALSE
+
+/obj/item/book/granter/spell/generic/on_reading_start(mob/user)
+ to_chat(user, "I start reading about [spellname]...")
+
+/obj/item/book/granter/spell/generic/on_reading_finished(mob/user)
+ . = ..()
+ var/learnedspells = 0
+ for(var/obj/effect/proc_holder/spell/arcane/knownspell in user.mind.spell_list)
+ learnedspells += 1
+ if(learnedspells == 0)
+ to_chat(user, "...You can't make sense of the sprawling runes...")
+ return
+ else if(learnedspells >= (1 + user.mind.get_skill_level(/datum/skill/magic/arcane)))
+ to_chat(user, "You tried hard to grasp it, unfortunately you are at the limit of your current arcane power...")
+ return
+ if(oneuse)
+ name = "Siphoned Tome"
+ desc = "A book once inscribed with magical scripture. The surface is now barren of knowledge, siphoned by someone else. It's utterly useless."
+ user.visible_message("[src] has had its magic ink ripped from the book!")
+ icon_state = "used_spell_book_[open]"
+ base_icon_state = "used_spell_book"
+ to_chat(user, "Your knowledge expands, you understand how to cast [spellname]!")
+ var/S = new target
+ user.mind.AddSpell(S)
+ //user.mind.adjust_experience(/datum/skill/misc/reading, user.STAINT) //cant find the user's STAINT here for some cursed reason...
+ user.log_message("learned the spell [target.name] ([target])", LOG_ATTACK, color="orange")
+ onlearned(user)
diff --git a/code/game/objects/items/rogueitems/books.dm b/code/game/objects/items/rogueitems/books.dm
index c441ffcd2..49fdc2d56 100644
--- a/code/game/objects/items/rogueitems/books.dm
+++ b/code/game/objects/items/rogueitems/books.dm
@@ -232,7 +232,7 @@
bookfile = "law.json"
/obj/item/book/rogue/cooking
- name = "Tastes Fit For The Lord"
+ name = "Tastes Fit For The Lord"
desc = ""
icon_state ="book_0"
base_icon_state = "book"
@@ -392,14 +392,14 @@
icon_state = "basic_book_0"
base_icon_state = "basic_book"
override_find_book = TRUE
-
+
/obj/item/book/rogue/playerbook/Initialize(loc, in_round_player_generated, var/mob/living/in_round_player_mob, text)
. = ..()
is_in_round_player_generated = in_round_player_generated
if(is_in_round_player_generated)
player_book_text = text
while(!player_book_author_ckey) // doesn't have to be this, but better than defining a bool.
- player_book_title = dd_limittext(capitalize(sanitize_hear_message(input(in_round_player_mob, "What title do you want to give the book? (max 42 characters)", "Title", "Unknown"))), MAX_NAME_LEN)
+ player_book_title = dd_limittext(capitalize(sanitize_hear_message(input(in_round_player_mob, "What title do you want to give the book? (max 42 characters)", "Title", "Unknown"))), MAX_NAME_LEN)
player_book_author = "[dd_limittext(sanitize_hear_message(input(in_round_player_mob, "What do you want the author text to be? (max 42 characters)", "Author", "")), MAX_NAME_LEN)]"
player_book_icon = book_icons[input(in_round_player_mob, "Choose a book style", "Book Style") as anything in book_icons]
player_book_author_ckey = in_round_player_mob.ckey
@@ -437,6 +437,51 @@
var/qdel_source = FALSE
/obj/item/manuscript/attackby(obj/item/I, mob/living/user)
+ if(resistance_flags & ON_FIRE)
+ return ..()
+
+ if(istype(I, /obj/item/natural/feather/magic))
+ if(src.number_of_pages <= 4)
+ to_chat(user, "This manuscript does not have enough pages to write an entire spellbook in it...")
+ return
+
+ var/obj/item/natural/feather/magic/F = I
+ var/arcane_score = 0
+ var/total_score = 0
+// var/crafttime = 0 The crafting is currently instantaneous so no need for this shit (yet)
+
+// Make is so you can't write spellbooks if you are below Journeyman.
+ for(var/i in 1 to user.mind.get_skill_level(/datum/skill/magic/arcane))
+ arcane_score += 1
+ if(arcane_score <= 2)
+ to_chat(user, "You do not have enough arcane knowledge to inscribe a spellbook...")
+ return
+
+// The math here is so it is borderline impossible to get a 100% success chance, those are supposed to be hard to make
+ total_score += (arcane_score * 6) + (user.STAINT * 2)
+// crafttime = 10 - arcane_score
+// for(var/i in 1 to user.mind.get_skill_level(/datum/skill/misc/reading))
+// total_score += 4
+ if(prob(total_score))
+ var/obj/item/book/granter/spell/generic/SB = new /obj/item/book/granter/spell/generic(get_turf(I.loc), pick(GLOB.learnables))
+ if(user.Adjacent(SB))
+ SB.add_fingerprint(user)
+ user.put_in_hands(SB)
+ user.mind.adjust_experience(/datum/skill/magic/arcane, user.STAINT*3)
+ to_chat(user, "As you finish writing, the feather glows and envelops the manuscript, becoming a new spellbook.")
+ qdel(src)
+ else
+ to_chat(user, "As you write, the feather glows but the magic gets out of control and the manuscript can not contain it, imploding it into dust.")
+ var/obj/O = new /obj/item/ash(get_turf(src.loc))
+ qdel(src)
+ explosion(O.loc,0,0,0,1,FALSE,,1,,TRUE)
+ // else something here about the feather not being able to write a spellbook
+ if(F.uses >= F.max_uses)
+ to_chat(user, "The feather's magic glows dimly, and then it turns into dust.")
+ new /obj/item/ash(get_turf(user.loc))
+ qdel(F)
+ return
+
// why is a book crafting kit using the craft system, but crafting a book isn't? Well the crafting system for *some reason* is made in such a way as to make reworking it to allow you to put reqs vars in the crafted item near *impossible.*
if(istype(I, /obj/item/book_crafting_kit))
qdel(I)
diff --git a/code/game/objects/items/rogueitems/keyrings.dm b/code/game/objects/items/rogueitems/keyrings.dm
index 657583f11..fab1a283a 100644
--- a/code/game/objects/items/rogueitems/keyrings.dm
+++ b/code/game/objects/items/rogueitems/keyrings.dm
@@ -158,6 +158,9 @@
/obj/item/keyring/mage
keys = list(/obj/item/roguekey/manor, /obj/item/roguekey/tower, /obj/item/roguekey/mage)
+/obj/item/keyring/mageapprentice
+ keys = list(/obj/item/roguekey/tower, /obj/item/roguekey/mage)
+
/obj/item/keyring/innkeep
keys = list(/obj/item/roguekey/tavern, /obj/item/roguekey/roomiv, /obj/item/roguekey/roomiii, /obj/item/roguekey/roomii, /obj/item/roguekey/roomi)
diff --git a/code/game/objects/items/rogueitems/natural/feather.dm b/code/game/objects/items/rogueitems/natural/feather.dm
index cc0517ed7..2154479ca 100644
--- a/code/game/objects/items/rogueitems/natural/feather.dm
+++ b/code/game/objects/items/rogueitems/natural/feather.dm
@@ -15,4 +15,11 @@
max_integrity = 20
muteinmouth = TRUE
spitoutmouth = FALSE
- w_class = WEIGHT_CLASS_TINY
\ No newline at end of file
+ w_class = WEIGHT_CLASS_TINY
+
+/obj/item/natural/feather/magic
+ name = "magic feather"
+ color = "#ffee00"
+ desc = "A fluffy feather enveloped with magical aura."
+ var/uses = 0
+ var/max_uses = 3 // Maybe balance this more?
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/combat/mage.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/combat/mage.dm
index aa9e0c46f..6570c79a3 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/combat/mage.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/combat/mage.dm
@@ -32,7 +32,6 @@
if(H.mind)
to_chat(H, span_warning("Magic is often times refered to as an art. At times it is treated as a primordial beast, chaos incarnate. To more learned men it is a precise science, to be studied and examined. In the end, magic is all three of the above. It is Art, Chaos, and Science: a blessing, a curse, and progress. It all depends on who calls upon it, and for what purpose."))
H.mind.adjust_skillrank(/datum/skill/combat/polearms, 1, TRUE)
- H.mind.adjust_skillrank(/datum/skill/combat/bows, 1, TRUE)
H.mind.adjust_skillrank(/datum/skill/combat/wrestling, pick(0,1,2), TRUE)
H.mind.adjust_skillrank(/datum/skill/combat/unarmed, pick(0,1,2), TRUE)
H.mind.adjust_skillrank(/datum/skill/misc/swimming, 1, TRUE)
@@ -64,7 +63,15 @@
"/obj/effect/proc_holder/spell/arcane/smokescreen",
"/obj/effect/proc_holder/spell/arcane/blindness",
"/obj/effect/proc_holder/spell/arcane/invisibility",
- "/obj/effect/proc_holder/spell/arcane/projectile/fetch"
+ "/obj/effect/proc_holder/spell/arcane/projectile/fetch",
+ "/obj/effect/proc_holder/spell/arcane/mist",
+ "/obj/effect/proc_holder/spell/arcane/web",
+ "/obj/effect/proc_holder/spell/arcane/feather",
+ "/obj/effect/proc_holder/spell/arcane/magicwall",
+ "/obj/effect/proc_holder/spell/arcane/ensnare",
+ "/obj/effect/proc_holder/spell/arcane/repulse",
+ "/obj/effect/proc_holder/spell/arcane/unlock",
+ "/obj/effect/proc_holder/spell/arcane/light"
)
H.mind.AddSpell(pick(new /obj/effect/proc_holder/spell/arcane/projectile/fireball,new /obj/effect/proc_holder/spell/arcane/projectile/lightningbolt))
for(var/i=2,i>0,i--)
diff --git a/code/modules/jobs/job_types/roguetown/adventurer/types/combat/sorceress.dm b/code/modules/jobs/job_types/roguetown/adventurer/types/combat/sorceress.dm
index ed967e742..bcebf59a4 100644
--- a/code/modules/jobs/job_types/roguetown/adventurer/types/combat/sorceress.dm
+++ b/code/modules/jobs/job_types/roguetown/adventurer/types/combat/sorceress.dm
@@ -31,19 +31,23 @@
r_hand = /obj/item/rogueweapon/woodstaff
if(H.mind)
H.mind.adjust_skillrank(/datum/skill/combat/polearms, 1, TRUE)
- H.mind.adjust_skillrank(/datum/skill/combat/wrestling, pick(0,1,1), TRUE)
- H.mind.adjust_skillrank(/datum/skill/combat/unarmed, pick(0,1,1), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/combat/wrestling, pick(0,1,2), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/combat/unarmed, pick(0,1,2), TRUE)
H.mind.adjust_skillrank(/datum/skill/misc/swimming, 1, TRUE)
H.mind.adjust_skillrank(/datum/skill/misc/climbing, pick(0,1), TRUE)
H.mind.adjust_skillrank(/datum/skill/misc/athletics, 1, TRUE)
- H.mind.adjust_skillrank(/datum/skill/combat/knives, pick(0,1,1,2), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/combat/swords, pick(0,1), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/combat/knives, pick(0,1,2), TRUE)
H.mind.adjust_skillrank(/datum/skill/craft/crafting, pick(0,1), TRUE)
- H.mind.adjust_skillrank(/datum/skill/misc/reading, 4, TRUE)
- H.mind.adjust_skillrank(/datum/skill/misc/alchemy, 3, TRUE)
- H.mind.adjust_skillrank(/datum/skill/magic/arcane, 2, TRUE)
- H.mind.adjust_skillrank(/datum/skill/misc/medicine, 1, TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/medicine, pick(0,1), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/riding, 1, TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/reading, pick(2,3), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/alchemy, 2, TRUE)
+ H.mind.adjust_skillrank(/datum/skill/magic/arcane, 4, TRUE)
if(H.age == AGE_OLD)
- H.mind.adjust_skillrank(/datum/skill/magic/arcane, pick(2,3,3), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/magic/arcane, pick(3,4,4), TRUE)
+ H.change_stat("intelligence", 5)
+ H.change_stat("strength", -2)
H.change_stat("strength", -1)
H.change_stat("intelligence", 4)
H.change_stat("speed", 1)
@@ -55,7 +59,15 @@
"/obj/effect/proc_holder/spell/arcane/smokescreen",
"/obj/effect/proc_holder/spell/arcane/blindness",
"/obj/effect/proc_holder/spell/arcane/invisibility",
- "/obj/effect/proc_holder/spell/arcane/projectile/fetch"
+ "/obj/effect/proc_holder/spell/arcane/projectile/fetch",
+ "/obj/effect/proc_holder/spell/arcane/mist",
+ "/obj/effect/proc_holder/spell/arcane/web",
+ "/obj/effect/proc_holder/spell/arcane/feather",
+ "/obj/effect/proc_holder/spell/arcane/magicwall",
+ "/obj/effect/proc_holder/spell/arcane/ensnare",
+ "/obj/effect/proc_holder/spell/arcane/repulse",
+ "/obj/effect/proc_holder/spell/arcane/unlock",
+ "/obj/effect/proc_holder/spell/arcane/light"
)
H.mind.AddSpell(pick(new /obj/effect/proc_holder/spell/arcane/projectile/fireball,new /obj/effect/proc_holder/spell/arcane/projectile/lightningbolt))
for(var/i=2,i>0,i--)
diff --git a/code/modules/jobs/job_types/roguetown/courtier/magician.dm b/code/modules/jobs/job_types/roguetown/courtier/magician.dm
index dd5aa0d50..8fbc0a8dd 100644
--- a/code/modules/jobs/job_types/roguetown/courtier/magician.dm
+++ b/code/modules/jobs/job_types/roguetown/courtier/magician.dm
@@ -77,10 +77,17 @@
"/obj/effect/proc_holder/spell/arcane/swap",
"/obj/effect/proc_holder/spell/arcane/smokescreen",
"/obj/effect/proc_holder/spell/arcane/projectile/lightningbolt",
- "/obj/effect/proc_holder/spell/arcane/projectile/fireball",
"/obj/effect/proc_holder/spell/arcane/blindness",
"/obj/effect/proc_holder/spell/arcane/invisibility",
- "/obj/effect/proc_holder/spell/arcane/projectile/fetch"
+ "/obj/effect/proc_holder/spell/arcane/projectile/fetch",
+ "/obj/effect/proc_holder/spell/arcane/mist",
+ "/obj/effect/proc_holder/spell/arcane/web",
+ "/obj/effect/proc_holder/spell/arcane/chain",
+ "/obj/effect/proc_holder/spell/arcane/feather",
+ "/obj/effect/proc_holder/spell/arcane/magicwall",
+ "/obj/effect/proc_holder/spell/arcane/ensnare",
+ "/obj/effect/proc_holder/spell/arcane/repulse",
+ "/obj/effect/proc_holder/spell/arcane/firewall"
)
H.mind.AddSpell(new /obj/effect/proc_holder/spell/arcane/projectile/fireball/greater)
for(var/i=3,i>0,i--)
@@ -89,4 +96,3 @@
H.mind.AddSpell(new typepath)
possible_spells.Remove(random_item)
possible_spells = null
-
diff --git a/code/modules/jobs/job_types/roguetown/youngfolk/mage_apprentice.dm b/code/modules/jobs/job_types/roguetown/youngfolk/mage_apprentice.dm
index 177c7c3ae..122278ff1 100644
--- a/code/modules/jobs/job_types/roguetown/youngfolk/mage_apprentice.dm
+++ b/code/modules/jobs/job_types/roguetown/youngfolk/mage_apprentice.dm
@@ -1,10 +1,11 @@
-/*/datum/job/roguetown/wapprentice
- title = "Magician's Apprentice"
+/datum/job/roguetown/wapprentice
+ title = "Magician Apprentice"
flag = MAGEAPPRENTICE
department_flag = YOUNGFOLK
faction = "Station"
total_positions = 0
- spawn_positions = 1
+ spawn_positions = 0
+//I disabled it for consistency but this job looks functional so far, I even gave a weak spell selection to them just in case.
allowed_races = list(
"Humen",
@@ -20,33 +21,54 @@
tutorial = "Your master once saw potential in you, something you are uncertain if they still do with your recent studies. The path to using magic is something treacherous and untamed, and you are still decades away from calling yourself even a journeyman in the field. Listen and serve, and someday you will earn your hat."
- spells = list(/obj/effect/proc_holder/spell/invoked/projectile/fetch)
outfit = /datum/outfit/job/roguetown/wapprentice
-
display_order = JDO_MAGEAPPRENTICE
give_bank_account = TRUE
+ min_pq = -5
+ max_pq = null
/datum/outfit/job/roguetown/wapprentice/pre_equip(mob/living/carbon/human/H)
..()
if(H.mind)
+ H.mind.adjust_skillrank(/datum/skill/misc/swimming, pick(0,1), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/climbing, pick(0,1), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/athletics, pick(0,1), TRUE)
H.mind.adjust_skillrank(/datum/skill/magic/arcane, pick(1,2), TRUE)
- H.mind.adjust_skillrank(/datum/skill/misc/reading, 2, TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/reading, pick(2,3), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/sneaking, pick(0,1), TRUE)
+ H.mind.adjust_skillrank(/datum/skill/misc/stealing, pick(0,1), TRUE)
+ var/list/possible_spells = list(
+ "/obj/effect/proc_holder/spell/arcane/telepathy",
+ "/obj/effect/proc_holder/spell/arcane/ignite",
+ "/obj/effect/proc_holder/spell/arcane/smokescreen",
+ "/obj/effect/proc_holder/spell/arcane/blindness",
+ "/obj/effect/proc_holder/spell/arcane/invisibility",
+ "/obj/effect/proc_holder/spell/arcane/projectile/fetch",
+ "/obj/effect/proc_holder/spell/arcane/web",
+ "/obj/effect/proc_holder/spell/arcane/chain",
+ "/obj/effect/proc_holder/spell/arcane/feather",
+ "/obj/effect/proc_holder/spell/arcane/light"
+ )
+ for(var/i=2,i>0,i--)
+ var/random_item = pick(possible_spells)
+ var typepath = text2path(random_item)
+ H.mind.AddSpell(new typepath)
+ possible_spells.Remove(random_item)
+ possible_spells = null
if(H.gender == MALE)
pants = /obj/item/clothing/under/roguetown/tights/random
shoes = /obj/item/clothing/shoes/roguetown/simpleshoes
shirt = /obj/item/clothing/suit/roguetown/shirt/undershirt
belt = /obj/item/storage/belt/rogue/leather/rope
- beltr = /obj/item/roguekey/tower
+ beltr = /obj/item/keyring/mageapprentice
armor = /obj/item/clothing/suit/roguetown/armor/workervest
backr = /obj/item/storage/backpack/rogue/satchel
else
shoes = /obj/item/clothing/shoes/roguetown/sandals
shirt = /obj/item/clothing/suit/roguetown/shirt/undershirt
belt = /obj/item/storage/belt/rogue/leather/rope
- beltr = /obj/item/roguekey/tower
+ beltr = /obj/item/keyring/mageapprentice
armor = /obj/item/clothing/suit/roguetown/armor/workervest
backr = /obj/item/storage/backpack/rogue/satchel
-
- H.change_stat("intelligence", 1)
+ H.change_stat("intelligence", round(rand(0,5)))
H.change_stat("speed", -1)
-*/
diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm
index 57a5ec3ba..f5aee9a28 100644
--- a/code/modules/jobs/jobs.dm
+++ b/code/modules/jobs/jobs.dm
@@ -140,7 +140,7 @@ GLOBAL_LIST_INIT(youngfolk_positions, list(
"Squire",
"Clerk",
"Smithy Apprentice",
- "Magician's Apprentice",
+ "Magician Apprentice",
"Churchling",
"Servant",
"Shophand",
diff --git a/code/modules/roguetown/roguecrafting/items.dm b/code/modules/roguetown/roguecrafting/items.dm
index 52cb612ca..204b3362a 100644
--- a/code/modules/roguetown/roguecrafting/items.dm
+++ b/code/modules/roguetown/roguecrafting/items.dm
@@ -411,6 +411,14 @@
tools = list(/obj/item/needle = 1)
req_table = TRUE
+/datum/crafting_recipe/roguetown/magic_feather
+ name = "magic feather"
+ result = /obj/item/natural/feather/magic
+ reqs = list(
+ /obj/item/reagent_containers/powder = 1,
+ /obj/item/natural/feather = 1
+ )
+
/datum/crafting_recipe/roguetown/woodcross
name = "wooden amulet"
result = /obj/item/clothing/neck/roguetown/psicross/wood
diff --git a/code/modules/spells/roguetown/arcane.dm b/code/modules/spells/roguetown/arcane.dm
index 3d569679c..662614b49 100644
--- a/code/modules/spells/roguetown/arcane.dm
+++ b/code/modules/spells/roguetown/arcane.dm
@@ -17,6 +17,13 @@
chargedloop = /datum/looping_sound/invokegen
associated_skill = /datum/skill/magic/arcane
charging_slowdown = 1
+ var/learnable = FALSE //If the spell can be learnable by spellbooks, FALSE by default to avoid dumb issues
+// put TRUE only those that can be learnt via crafable spellbooks.
+
+/obj/effect/temp_visual/acast
+ icon_state = "emppulse"
+ layer = ABOVE_MOB_LAYER
+ plane = GAME_PLANE_UPPER
/obj/effect/proc_holder/spell/arcane/Click()
var/mob/living/user = usr
@@ -56,7 +63,10 @@
if(!can_cast(caller) || !cast_check(FALSE, ranged_ability_user))
return FALSE
if(perform(list(target), TRUE, user = ranged_ability_user))
- caller.mind.adjust_experience(associated_skill, (caller.STAINT*0.3))//Arcane Skill exp gain - Delete/Edit if on your leisure
+ //Arcane Skill exp gain - Delete/Edit if on your leisure
+ caller.mind.adjust_experience(associated_skill, (releasedrain*0.1)*(caller.STAINT*0.1))
+ //caller.mind.adjust_experience(associated_skill, (caller.STAINT*0.3))
+ new /obj/effect/temp_visual/acast(get_turf(caller))
return TRUE
/obj/effect/proc_holder/spell/arcane/projectile
@@ -190,4 +200,4 @@
revert_cast()
return
- perform(targets,user=user)
+ perform(targets,user=user)
\ No newline at end of file
diff --git a/code/modules/spells/roguetown/arcane/blindness.dm b/code/modules/spells/roguetown/arcane/blindness.dm
index dbce6704c..3c241a30d 100644
--- a/code/modules/spells/roguetown/arcane/blindness.dm
+++ b/code/modules/spells/roguetown/arcane/blindness.dm
@@ -12,6 +12,7 @@
movement_interrupt = FALSE
sound = 'sound/magic/churn.ogg'
antimagic_allowed = TRUE
+ learnable = TRUE
/obj/effect/proc_holder/spell/arcane/blindness/cast(list/targets, mob/user = usr)
if(isliving(targets[1]))
@@ -19,5 +20,6 @@
if(target.anti_magic_check(TRUE, TRUE))
return FALSE
target.visible_message(span_warning("[user] points at [target]'s eyes!"),span_warning("My eyes are covered in darkness!"))
- target.blind_eyes(2)
- return TRUE
\ No newline at end of file
+ target.blind_eyes(user.mind.get_skill_level(/datum/skill/magic/arcane))
+ return TRUE
+ return FALSE
\ No newline at end of file
diff --git a/code/modules/spells/roguetown/arcane/blink.dm b/code/modules/spells/roguetown/arcane/blink.dm
index 93446d3e4..49fc976fd 100644
--- a/code/modules/spells/roguetown/arcane/blink.dm
+++ b/code/modules/spells/roguetown/arcane/blink.dm
@@ -5,13 +5,14 @@
desc = ""
overlay_state = "blink"
sound = 'sound/magic/magic_nulled.ogg'
- range = 8
+ range = 6
releasedrain = 50
chargedrain = 0
chargetime = 0
- charge_max = 15 SECONDS
+ charge_max = 25 SECONDS
var/include_space = FALSE //whether it includes space tiles in possible teleport locations
var/include_dense = FALSE //whether it includes dense tiles in possible teleport locations
+ learnable = TRUE
/obj/effect/temp_visual/blink
icon_state = "anom"
@@ -28,3 +29,34 @@
do_teleport(user, location, forceMove = TRUE, channel = TELEPORT_CHANNEL_MAGIC)
return TRUE
return FALSE
+
+//TELEPORT-----------------
+
+/obj/effect/proc_holder/spell/arcane/teleport
+ name = "Teleport"
+ desc = ""
+ overlay_state = "blink"
+ sound = 'sound/magic/magic_nulled.ogg'
+ range = 8
+ releasedrain = 60
+ chargedrain = 0
+ chargetime = 0
+ charge_max = 15 SECONDS
+ var/include_space = FALSE //whether it includes space tiles in possible teleport locations
+ var/include_dense = FALSE //whether it includes dense tiles in possible teleport locations
+ learnable = FALSE
+
+/obj/effect/temp_visual/teleport
+ icon_state = "anom"
+ layer = ABOVE_MOB_LAYER
+ plane = GAME_PLANE_UPPER
+
+/obj/effect/proc_holder/spell/arcane/teleport/cast(list/targets,mob/user = usr)
+ . = ..()
+ if(isopenturf(targets[1]))
+ var/atom/location = get_turf(targets[1])
+ new /obj/effect/temp_visual/swap(get_turf(user))
+ new /obj/effect/temp_visual/swap(get_turf(location))
+ do_teleport(user, location, forceMove = TRUE, channel = TELEPORT_CHANNEL_MAGIC)
+ return TRUE
+ return FALSE
\ No newline at end of file
diff --git a/code/modules/spells/roguetown/arcane/chains.dm b/code/modules/spells/roguetown/arcane/chains.dm
new file mode 100644
index 000000000..3c0143bc8
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/chains.dm
@@ -0,0 +1,55 @@
+//CONJURE CHAINS--------------------------------
+
+/obj/effect/proc_holder/spell/arcane/chain
+ name = "Conjure Chains"
+ desc = ""
+ overlay_state = "chain"
+ sound = 'sound/foley/dropsound/chain_drop.ogg'
+ range = 8
+ releasedrain = 50
+ chargedrain = 1
+ chargetime = 10
+ charge_max = 60 SECONDS
+ learnable = TRUE
+
+/obj/effect/proc_holder/spell/arcane/chain/cast(list/targets, mob/living/user)
+ . = ..()
+ if(iscarbon(targets[1]))
+ var/mob/living/carbon/C = targets[1]
+ if(!C.lying)
+ to_chat(user, span_warning("[C] needs to be lying down."))
+ return FALSE
+ if(user.aimheight > 4)
+ if(!C.handcuffed)
+ if(C.get_num_arms(FALSE) || C.get_arm_ignore())
+ var/obj/item/cuffs = new /obj/item/rope/chain
+ cuffs.forceMove(C)
+ C.handcuffed = cuffs
+ C.update_handcuffed()
+ C.visible_message(span_warning("[user] conjured magic shackles in [C]'s hands."), \
+ span_danger("[user] ties me up with magic shackles."))
+ SSblackbox.record_feedback("tally", "handcuffs", 1, type)
+ log_combat(user, C, "handcuffed")
+ return TRUE
+ else
+ to_chat(user, span_warning("[C] has no arms to tie up."))
+ else
+ to_chat(user, span_warning("[C] is already tied."))
+ if(user.aimheight <= 4)
+ if(!C.legcuffed)
+ if(C.get_num_legs(TRUE) == 2)
+// if(do_mob(user, C, 60) && (C.get_num_legs(FALSE) < 2))
+ var/obj/item/cuffs = new /obj/item/rope/chain
+ cuffs.forceMove(C)
+ C.legcuffed = cuffs
+ C.update_inv_legcuffed()
+ C.visible_message(span_warning("[user] conjured magic shackles in [C]'s legs."), \
+ span_danger("[user] ties my legs with magic shackles."))
+ SSblackbox.record_feedback("tally", "legcuffs", 1, type)
+ log_combat(user, C, "legcuffed", TRUE)
+ return TRUE
+ else
+ to_chat(user, span_warning("[C] is missing two or one legs."))
+ else
+ to_chat(user, span_warning("[C] is already tied."))
+ return FALSE
diff --git a/code/modules/spells/roguetown/arcane/decay.dm b/code/modules/spells/roguetown/arcane/decay.dm
new file mode 100644
index 000000000..d29e41c7b
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/decay.dm
@@ -0,0 +1,39 @@
+//DECAY BOLT---------------
+
+/obj/effect/proc_holder/spell/arcane/projectile/decaybolt
+ name = "Decay Bolt"
+ desc = ""
+ overlay_state = "decay"
+ range = 8
+ projectile_type = /obj/projectile/magic/decaybolt
+ sound = 'sound/misc/portal_enter.ogg'
+ releasedrain = 30
+ chargedrain = 1
+ chargetime = 10
+ charge_max = 10 SECONDS
+ movement_interrupt = FALSE
+ charging_slowdown = 3
+ learnable = TRUE
+
+/obj/projectile/magic/decaybolt
+ name = "sickness"
+ icon_state = "necropotence"
+ damage = 10
+ damage_type = BURN
+ nodamage = FALSE
+ flag = "magic"
+ range = 15
+
+/obj/projectile/magic/decaybolt/on_hit(target,mob/user = usr)
+ . = ..()
+ if(ismob(target))
+ var/mob/M = target
+ if(M.anti_magic_check())
+ visible_message(span_warning("[src] fizzles on contact with [target]!"))
+ playsound(get_turf(target), 'sound/magic/magic_nulled.ogg', 100)
+ qdel(src)
+ return BULLET_ACT_BLOCK
+ if(iscarbon(target))
+ var/mob/living/carbon/L = target
+ L.add_nausea(rand(50,150))
+ L.adjustToxLoss(10)
diff --git a/code/modules/spells/roguetown/arcane/feather.dm b/code/modules/spells/roguetown/arcane/feather.dm
new file mode 100644
index 000000000..fcf060292
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/feather.dm
@@ -0,0 +1,22 @@
+//FEATHER-------------------------------
+
+/obj/effect/proc_holder/spell/arcane/feather
+ name = "Feather Step"
+ desc = ""
+ overlay_state = "feather"
+ sound = 'sound/magic/magic_nulled.ogg'
+ range = 8
+ releasedrain = 30
+ chargedrain = 1
+ chargetime = 15
+ charge_max = 5 SECONDS
+ learnable = TRUE
+
+/obj/effect/proc_holder/spell/arcane/feather/cast(list/targets, mob/living/user)
+ if(isliving(targets[1]))
+ var/mob/living/target = targets[1]
+ if(target.anti_magic_check(TRUE, TRUE))
+ return TRUE
+ target.apply_status_effect(/datum/status_effect/buff/feather)
+ return TRUE
+ return FALSE
diff --git a/code/modules/spells/roguetown/arcane/fetch.dm b/code/modules/spells/roguetown/arcane/fetch.dm
index f17f4a070..5f1071d3f 100644
--- a/code/modules/spells/roguetown/arcane/fetch.dm
+++ b/code/modules/spells/roguetown/arcane/fetch.dm
@@ -14,6 +14,7 @@
warnie = "spellwarning"
no_early_release = TRUE
charging_slowdown = 1
+ learnable = TRUE
/obj/projectile/magic/fetch/on_hit(target)
. = ..()
diff --git a/code/modules/spells/roguetown/arcane/fireball.dm b/code/modules/spells/roguetown/arcane/fireball.dm
index b7037c025..db1414822 100644
--- a/code/modules/spells/roguetown/arcane/fireball.dm
+++ b/code/modules/spells/roguetown/arcane/fireball.dm
@@ -17,6 +17,7 @@
no_early_release = TRUE
movement_interrupt = FALSE
charging_slowdown = 3
+ learnable = TRUE
/obj/effect/proc_holder/spell/arcane/projectile/fireball/fire_projectile(list/targets, mob/living/user)
projectile_var_overrides = list("range" = 8)
@@ -62,6 +63,7 @@
no_early_release = TRUE
movement_interrupt = TRUE
chargedloop = /datum/looping_sound/invokegen
+ learnable = FALSE
/obj/projectile/magic/aoe/fireball/rogue/great
name = "fireball"
diff --git a/code/modules/spells/roguetown/arcane/firewall.dm b/code/modules/spells/roguetown/arcane/firewall.dm
new file mode 100644
index 000000000..b3f7f7534
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/firewall.dm
@@ -0,0 +1,28 @@
+//FIRE WALL-----------------
+
+/obj/effect/proc_holder/spell/arcane/firewall
+ name = "Fire Wall"
+ desc = ""
+ overlay_state = "fwall"
+ sound = 'sound/items/firesnuff.ogg'
+ range = 8
+ releasedrain = 30
+ chargedrain = 1
+ chargetime = 20
+ charge_max = 40 SECONDS
+ learnable = TRUE
+ charging_slowdown = 2
+
+/obj/effect/proc_holder/spell/arcane/firewall/cast(list/targets,mob/user = usr)
+ . = ..()
+ if(isliving(targets[1]) || isopenturf(targets[1]))
+ var/atom/location = get_turf(targets[1])
+ explosion(location,0,0,0,0,FALSE,,1,,FALSE,FALSE)
+ if(user.dir == SOUTH || user.dir == NORTH)
+ explosion(get_step(location, EAST),0,0,0,0,FALSE,,1,,FALSE,FALSE)
+ explosion(get_step(location, WEST),0,0,0,0,FALSE,,1,,FALSE,FALSE)
+ else
+ explosion(get_step(location, NORTH),0,0,0,0,FALSE,,1,,FALSE,FALSE)
+ explosion(get_step(location, SOUTH),0,0,0,0,FALSE,,1,,FALSE,FALSE)
+ return TRUE
+ return FALSE
\ No newline at end of file
diff --git a/code/modules/spells/roguetown/arcane/ignite.dm b/code/modules/spells/roguetown/arcane/ignite.dm
index 6c08ccda2..0d042aa17 100644
--- a/code/modules/spells/roguetown/arcane/ignite.dm
+++ b/code/modules/spells/roguetown/arcane/ignite.dm
@@ -10,6 +10,7 @@
chargedrain = 0
chargetime = 0
charge_max = 10 SECONDS
+ learnable = TRUE
/obj/effect/proc_holder/spell/arcane/ignite/cast(list/targets, mob/user = usr)
. = ..()
@@ -18,7 +19,7 @@
user.visible_message("[user] points at [L]!")
if(L.anti_magic_check(TRUE, TRUE))
return FALSE
- L.adjust_fire_stacks(5)
+ L.adjust_fire_stacks(2 + user.mind.get_skill_level(/datum/skill/magic/arcane))
L.IgniteMob()
addtimer(CALLBACK(L, TYPE_PROC_REF(/mob/living, ExtinguishMob)), 5 SECONDS)
return TRUE
diff --git a/code/modules/spells/roguetown/arcane/invisibility.dm b/code/modules/spells/roguetown/arcane/invisibility.dm
index 6d1579936..c6ecca204 100644
--- a/code/modules/spells/roguetown/arcane/invisibility.dm
+++ b/code/modules/spells/roguetown/arcane/invisibility.dm
@@ -12,15 +12,17 @@
movement_interrupt = FALSE
sound = 'sound/misc/area.ogg' //This sound doesnt play for some reason. Fix me.
antimagic_allowed = TRUE
+ learnable = TRUE
/obj/effect/proc_holder/spell/arcane/invisibility/cast(list/targets, mob/living/user)
if(isliving(targets[1]))
var/mob/living/target = targets[1]
if(target.anti_magic_check(TRUE, TRUE))
- return FALSE
+ return TRUE
target.visible_message(span_warning("[target] starts to fade into thin air!"), span_notice("You start to become invisible!"))
animate(target, alpha = 0, time = 1 SECONDS, easing = EASE_IN)
target.mob_timers[MT_INVISIBILITY] = world.time + 15 SECONDS
addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living, update_sneak_invis), TRUE), 15 SECONDS)
addtimer(CALLBACK(target, TYPE_PROC_REF(/atom/movable, visible_message), span_warning("[target] fades back into view."), span_notice("You become visible again.")), 15 SECONDS)
+ return TRUE
return FALSE
\ No newline at end of file
diff --git a/code/modules/spells/roguetown/arcane/light.dm b/code/modules/spells/roguetown/arcane/light.dm
new file mode 100644
index 000000000..1715532b0
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/light.dm
@@ -0,0 +1,22 @@
+//ARCANE LIGHT------------------------------
+
+/obj/effect/proc_holder/spell/arcane/light
+ name = "Arcane Light"
+ desc = ""
+ overlay_state = "light"
+ sound = 'sound/magic/magic_nulled.ogg'
+ range = 8
+ releasedrain = 30
+ chargedrain = 1
+ chargetime = 15
+ charge_max = 40 SECONDS
+ charging_slowdown = 1
+ learnable = TRUE
+
+/obj/effect/proc_holder/spell/arcane/light/cast(list/targets, mob/living/carbon/user = usr)
+ var/light_power = clamp(4 + (user.mind.get_skill_level(/datum/skill/magic/arcane) - 3), 4, 7) // every step above journeyman should get us 1 more tile of brightness
+ var/mob/living/carbon/human/H = user
+ var/light_color = "#[H.voice_color]"//"#3FBAFD"
+ user.mob_light(_range = light_power, _power = 3, _color = light_color, _duration = 30 SECONDS)
+ user.visible_message(span_notice("[user] conjures light from the palm of [user.p_their()] hand"), span_notice("I conjure light from the palm of my hand..."))
+ return TRUE
\ No newline at end of file
diff --git a/code/modules/spells/roguetown/arcane/lightning.dm b/code/modules/spells/roguetown/arcane/lightning.dm
index 1476cdfc2..e4b1aeaeb 100644
--- a/code/modules/spells/roguetown/arcane/lightning.dm
+++ b/code/modules/spells/roguetown/arcane/lightning.dm
@@ -13,6 +13,7 @@
charge_max = 10 SECONDS
movement_interrupt = FALSE
charging_slowdown = 3
+ learnable = TRUE
/obj/projectile/magic/lightning
name = "bolt of lightning"
diff --git a/code/modules/spells/roguetown/arcane/magicwall.dm b/code/modules/spells/roguetown/arcane/magicwall.dm
new file mode 100644
index 000000000..e3b9fc06b
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/magicwall.dm
@@ -0,0 +1,58 @@
+//MAGIC WALL----------------------
+
+/obj/effect/proc_holder/spell/arcane/magicwall
+ name = "Magic Wall"
+ desc = ""
+ overlay_state = "wall"
+ range = 8
+ releasedrain = 50
+ chargedrain = 1
+ chargetime = 15
+ charge_max = 60 SECONDS
+ charging_slowdown = 3
+ sound = 'sound/magic/magic_nulled.ogg'
+ learnable = TRUE
+ var/wall_type = /obj/structure/magicwall/caster
+
+/obj/structure/magicwall
+ desc = "A wall of pure arcyne force."
+ name = "Arcane Wall"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "m_shield"
+ break_sound = 'sound/combat/hits/onstone/stonedeath.ogg'
+ attacked_sound = list('sound/combat/hits/onstone/wallhit.ogg', 'sound/combat/hits/onstone/wallhit2.ogg', 'sound/combat/hits/onstone/wallhit3.ogg')
+ opacity = 0
+ density = TRUE
+ max_integrity = 80
+ var/timeleft = 20 SECONDS
+
+/obj/structure/magicwall/Initialize()
+ . = ..()
+ if(timeleft)
+ QDEL_IN(src, timeleft) //delete after it runs out
+
+/obj/effect/proc_holder/spell/arcane/magicwall/cast(list/targets,mob/user = usr)
+ new wall_type(get_turf(user),user)
+ if(user.dir == SOUTH || user.dir == NORTH)
+ new wall_type(get_step(user, EAST),user)
+ new wall_type(get_step(user, WEST),user)
+ else
+ new wall_type(get_step(user, NORTH),user)
+ new wall_type(get_step(user, SOUTH),user)
+ return TRUE
+
+/obj/structure/magicwall
+ var/mob/caster
+
+/obj/structure/magicwall/caster/Initialize(mapload, mob/summoner)
+ . = ..()
+ caster = summoner
+
+/obj/structure/magicwall/caster/CanPass(atom/movable/mover, turf/target)//only the caster can move through this freely
+ if(mover == caster)
+ return TRUE
+ if(ismob(mover))
+ var/mob/M = mover
+ if(M.anti_magic_check(chargecost = 0))
+ return TRUE
+ return FALSE
diff --git a/code/modules/spells/roguetown/arcane/repulse.dm b/code/modules/spells/roguetown/arcane/repulse.dm
new file mode 100644
index 000000000..d55be7564
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/repulse.dm
@@ -0,0 +1,58 @@
+//FORCE PUSH-----------------------
+
+/obj/effect/proc_holder/spell/arcane/repulse
+ name = "Repulse"
+ desc = ""
+ overlay_state = "force"
+ range = 8
+ releasedrain = 50
+ chargedrain = 0
+ chargetime = 0
+ charge_max = 30 SECONDS
+ charging_slowdown = 2
+ learnable = TRUE
+ var/stun_amt = 5
+ var/maxthrow = 3
+ var/repulse_force = MOVE_FORCE_STRONG
+ var/push_range = 1
+
+/obj/effect/proc_holder/spell/arcane/repulse/cast(list/targets, mob/user)
+ var/list/thrownatoms = list()
+ var/atom/throwtarget
+ var/distfromcaster
+ playsound(user,'sound/magic/swap.ogg', 75, TRUE)
+ user.visible_message("[user] conjures forth a brute force arcane wave, repelling anything in front of them!")
+ for(var/turf/T in view(push_range, user))
+ new /obj/effect/temp_visual/magicpush(T)
+ for(var/atom/movable/AM in T)
+ thrownatoms += AM
+
+ for(var/am in thrownatoms)
+ var/atom/movable/AM = am
+ if(AM == user || AM.anchored)
+ continue
+
+ if(ismob(AM))
+ var/mob/M = AM
+ if(M.anti_magic_check())
+ continue
+
+ throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(AM, user)))
+ distfromcaster = get_dist(user, AM)
+ if(distfromcaster == 0)
+ if(isliving(AM))
+ var/mob/living/M = AM
+ M.Paralyze(10)
+ M.adjustBruteLoss(5)
+ to_chat(M, "You're slammed into the floor by [user]!")
+ else
+ if(isliving(AM))
+ var/mob/living/M = AM
+ M.Paralyze(stun_amt)
+ to_chat(M, "You're thrown back by [user]!")
+ AM.safe_throw_at(throwtarget, ((CLAMP((maxthrow - (CLAMP(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1,user, force = repulse_force)//So stuff gets tossed around at the same time.
+
+/obj/effect/temp_visual/magicpush
+ icon_state = "empdisable"
+ layer = ABOVE_MOB_LAYER
+ plane = GAME_PLANE_UPPER
diff --git a/code/modules/spells/roguetown/arcane/roots.dm b/code/modules/spells/roguetown/arcane/roots.dm
new file mode 100644
index 000000000..44ec1c202
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/roots.dm
@@ -0,0 +1,49 @@
+//ENSNARING ROOTS------------------
+
+/obj/effect/proc_holder/spell/arcane/ensnare
+ name = "Ensnaring Roots"
+ desc = ""
+ overlay_state = "root"
+ range = 6
+ releasedrain = 40
+ chargedrain = 1
+ chargetime = 20
+ charge_max = 30 SECONDS
+ charging_slowdown = 3
+ learnable = TRUE
+ var/area_of_effect = 1
+ var/duration = 4 SECONDS
+
+/obj/effect/proc_holder/spell/arcane/ensnare/cast(list/targets, mob/user = usr)
+ var/turf/T = get_turf(targets[1])
+
+ for(var/turf/affected_turf in view(area_of_effect, T))
+ if(affected_turf.density)
+ continue
+ new /obj/effect/temp_visual/ensnare(affected_turf)
+
+ addtimer(CALLBACK(src, PROC_REF(apply_slowdown), T, area_of_effect, duration, user), 0.5 SECONDS)
+ playsound(T,'sound/magic/webspin.ogg', 50, TRUE)
+ return TRUE
+
+/obj/effect/proc_holder/spell/arcane/ensnare/proc/apply_slowdown(turf/T, area_of_effect, duration)
+ for(var/mob/living/simple_animal/hostile/animal in range(area_of_effect, T))
+// animal.Paralyze(duration, updating = TRUE, ignore_canstun = TRUE) //i think animal movement is coded weird, i cant seem to stun them
+ animal.Immobilize(duration)
+ animal.visible_message("[animal] is held by conjured roots!")
+ for(var/mob/living/L in range(area_of_effect, T))
+ if(L.anti_magic_check())
+ visible_message(span_warning("The roots can't seem to latch onto [L] ")) //antimagic needs some testing
+ playsound(get_turf(L), 'sound/magic/magic_nulled.ogg', 100)
+ return
+ L.Immobilize(duration)
+ L.visible_message("[L] is held by conjured roots!")
+ new /obj/effect/temp_visual/ensnare/long(get_turf(L))
+
+/obj/effect/temp_visual/ensnare
+ icon = 'icons/roguetown/misc/foliage.dmi'
+ icon_state = "thornbush"
+ duration = 1 SECONDS
+
+/obj/effect/temp_visual/ensnare/long
+ duration = 5 SECONDS
diff --git a/code/modules/spells/roguetown/arcane/smokescreen.dm b/code/modules/spells/roguetown/arcane/smokescreen.dm
index d1b30d5ae..cd66d92b2 100644
--- a/code/modules/spells/roguetown/arcane/smokescreen.dm
+++ b/code/modules/spells/roguetown/arcane/smokescreen.dm
@@ -10,14 +10,17 @@
chargedrain = 0
chargetime = 0
charge_max = 10 SECONDS
- smoke_spread = 1 //Just Smoke
- smoke_amt = 2
+ learnable = TRUE
/obj/effect/proc_holder/spell/arcane/smokescreen/cast(list/targets,mob/user = usr)
. = ..()
- if(isliving(targets[1]))
- return TRUE
- else if(isopenturf(targets[1]))
+ if(isliving(targets[1]) | isopenturf(targets[1]))
+ var/atom/location = get_turf(targets[1])
+ for(var/atom/T in oview(1,location))
+ if(T.x == location.x | T.y == location.y)//make it a cross instead of a damn square
+ var/datum/effect_system/smoke_spread/smoke = new
+ smoke.set_up(1, T)
+ smoke.start()
return TRUE
return FALSE
@@ -33,15 +36,17 @@
chargedrain = 1
chargetime = 10
charge_max = 15 SECONDS
- smoke_spread = 2 //Now it makes the target cough and drop items in hand
- smoke_amt = 1
+ learnable = FALSE
/obj/effect/proc_holder/spell/arcane/densesmoke/cast(list/targets,mob/user = usr)
. = ..()
- if(isliving(targets[1]))
- return TRUE
- else if(isopenturf(targets[1]))
- return TRUE
+ if(isliving(targets[1]) | isopenturf(targets[1]))
+ var/atom/location = get_turf(targets[1])
+ if(location in oview(range,user))
+ var/datum/effect_system/smoke_spread/bad/smoke = new
+ smoke.set_up(1, location)
+ smoke.start()
+ return TRUE
return FALSE
//SLEEPING GAS-------------------
@@ -56,13 +61,40 @@
chargedrain = 1
chargetime = 30
charge_max = 30 SECONDS
- smoke_spread = 3 //Now this will make the target to fall asleep
- smoke_amt = 2
+ learnable = FALSE
/obj/effect/proc_holder/spell/arcane/sleepgas/cast(list/targets,mob/user = usr)
. = ..()
- if(isliving(targets[1]))
- return TRUE
- else if(isopenturf(targets[1]))
- return TRUE
- return FALSE
\ No newline at end of file
+ if(isliving(targets[1]) | isopenturf(targets[1]))
+ var/atom/location = get_turf(targets[1])
+ if(location in oview(range,user))
+ var/datum/effect_system/smoke_spread/sleeping/smoke = new
+ smoke.set_up(1, location)
+ smoke.start()
+ return TRUE
+ return FALSE
+
+//MIST-----------------
+
+/obj/effect/proc_holder/spell/arcane/mist
+ name = "Conjure Mist"
+ desc = ""
+ overlay_state = "mist"
+ sound = 'sound/items/firesnuff.ogg'
+ range = 8
+ releasedrain = 50
+ chargedrain = 1
+ chargetime = 30
+ charge_max = 60 SECONDS
+ learnable = TRUE
+
+/obj/effect/proc_holder/spell/arcane/mist/cast(list/targets,mob/user = usr)
+ . = ..()
+ var/atom/location = get_turf(user)
+ for(var/atom/T in oview(5,location))
+ if(!(T in oview(1,location)))
+ if(prob(35))
+ var/datum/effect_system/smoke_spread/smoke = new
+ smoke.set_up(1, T)
+ smoke.start()
+ return TRUE
diff --git a/code/modules/spells/roguetown/arcane/swap.dm b/code/modules/spells/roguetown/arcane/swap.dm
index 2ef5e107e..6fb9b110e 100644
--- a/code/modules/spells/roguetown/arcane/swap.dm
+++ b/code/modules/spells/roguetown/arcane/swap.dm
@@ -9,8 +9,9 @@
releasedrain = 50
chargedrain = 1
chargetime = 15
- charge_max = 20 SECONDS
+ charge_max = 30 SECONDS
charging_slowdown = 3
+ learnable = TRUE
var/include_space = FALSE //whether it includes space tiles in possible teleport locations
var/include_dense = FALSE //whether it includes dense tiles in possible teleport locations
diff --git a/code/modules/spells/roguetown/arcane/telepathy.dm b/code/modules/spells/roguetown/arcane/telepathy.dm
index ed70c87cf..af036ce80 100644
--- a/code/modules/spells/roguetown/arcane/telepathy.dm
+++ b/code/modules/spells/roguetown/arcane/telepathy.dm
@@ -1,7 +1,7 @@
//TELEPATHY---------------------------
/obj/effect/proc_holder/spell/arcane/telepathy
- name = "telepathy"
+ name = "Telepathy"
desc = ""
range = 15
overlay_state = "psy"
@@ -13,6 +13,7 @@
warnie = "spellwarning"
no_early_release = TRUE
charging_slowdown = 1
+ learnable = TRUE
/obj/effect/proc_holder/spell/arcane/telepathy/cast(list/targets,mob/user = usr)
. = ..()
diff --git a/code/modules/spells/roguetown/arcane/unlock.dm b/code/modules/spells/roguetown/arcane/unlock.dm
new file mode 100644
index 000000000..bd6c77537
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/unlock.dm
@@ -0,0 +1,65 @@
+//UNLOCK----------------------
+
+/obj/effect/proc_holder/spell/arcane/unlock
+ name = "Unlock"
+ desc = ""
+ overlay_state = "lock"
+ range = 8
+ releasedrain = 60
+ chargedrain = 0
+ chargetime = 5 SECONDS
+ charge_max = 20 SECONDS
+ charging_slowdown = 2
+ movement_interrupt = TRUE
+ learnable = TRUE
+
+/obj/effect/proc_holder/spell/arcane/unlock/cast(list/targets, mob/user = usr)
+ var/atom/location = get_turf(targets[1])
+ var/prob2open = 0
+ var/diceroll = 0
+ for(var/obj/structure/mineral_door/door in location)
+ if (door in range(1, user))
+ diceroll = rand(0,100)
+ prob2open = 20 + (user.mind.get_skill_level(/datum/skill/magic/arcane) * 5)
+ if (diceroll <= prob2open)
+ playsound(get_turf(user), 'sound/misc/chestclose.ogg', 100, TRUE, -1)
+ INVOKE_ASYNC(src, PROC_REF(open_door), door)
+ else
+ to_chat(user, "You fail to cast the spell...")
+ return TRUE
+ to_chat(user, "You need to get closer...")
+ for(var/obj/structure/closet/C in location)
+ if (C in range(1, user))
+ diceroll = rand(0,100)
+ prob2open = 20 + (user.mind.get_skill_level(/datum/skill/magic/arcane) * 5)
+ if (diceroll <= prob2open)
+ playsound(get_turf(user), 'sound/misc/chestopen.ogg', 100, TRUE, -1)
+ INVOKE_ASYNC(src, PROC_REF(open_closet), C)
+ else
+ to_chat(user, "You fail to cast the spell...")
+ return TRUE
+ to_chat(user, "You need to get closer...")
+ to_chat(user, "There is nothing the spell can unlock here.")
+ return FALSE
+
+/obj/effect/proc_holder/spell/arcane/unlock/proc/open_door(obj/structure/mineral_door/door, mob/user = usr)
+ if(door.door_opened || door.isSwitchingStates || !door.locked)
+ to_chat(user, "It is already open.")
+ return
+ if(!door.keylock)
+ to_chat(user, "There's no lock on this.")
+ return
+ if(door.lockbroken)
+ to_chat(user, "The lock is broken.")
+ return
+ to_chat(user, "You unlock the door.")
+ door.locked = FALSE
+ return
+
+/obj/effect/proc_holder/spell/arcane/unlock/proc/open_closet(obj/structure/closet/C, mob/user = usr)
+ if(!C.locked)
+ to_chat(user, "It is already open.")
+ return
+ to_chat(user, "You unlock the closet.")
+ C.locked = FALSE
+ return
diff --git a/code/modules/spells/roguetown/arcane/web.dm b/code/modules/spells/roguetown/arcane/web.dm
new file mode 100644
index 000000000..6ef62a252
--- /dev/null
+++ b/code/modules/spells/roguetown/arcane/web.dm
@@ -0,0 +1,31 @@
+//SPIDER WEB
+
+/obj/effect/proc_holder/spell/arcane/web
+ name = "Conjure Web"
+ desc = ""
+ overlay_state = "web"
+ range = 8
+ sound = 'sound/magic/webspin.ogg'
+ releasedrain = 30
+ chargedrain = 1
+ chargetime = 10
+ charge_max = 60 SECONDS
+ learnable = TRUE
+
+/obj/effect/proc_holder/spell/arcane/web/cast(list/targets,mob/user = usr)
+ . = ..()
+/* if(isliving(targets[1]) | isopenturf(targets[1]))
+ var/atom/location = get_turf(targets[1])
+ if(location in oview(range,user))
+ new /obj/structure/spider/stickyweb(location)
+ return TRUE
+ return FALSE*/
+ if(isliving(targets[1]) | isopenturf(targets[1]))
+ var/atom/location = get_turf(targets[1])
+ new /obj/structure/spider/stickyweb(location)
+ for(var/atom/T in oview(1,location))
+ if(T.x == location.x | T.y == location.y)//make it a cross instead of a damn square
+ if(prob(50))
+ new /obj/structure/spider/stickyweb(T)
+ return TRUE
+ return FALSE
\ No newline at end of file
diff --git a/icons/mob/actions/roguespells.dmi b/icons/mob/actions/roguespells.dmi
index ddc68f3a6..ec4960b0c 100644
Binary files a/icons/mob/actions/roguespells.dmi and b/icons/mob/actions/roguespells.dmi differ
diff --git a/roguetown.dme b/roguetown.dme
index 40cdb514c..95e47ba49 100644
--- a/roguetown.dme
+++ b/roguetown.dme
@@ -3150,6 +3150,7 @@
#include "code\modules\roguetown\mapgen\forest.dm"
#include "code\modules\roguetown\mapgen\mountains.dm"
#include "code\modules\roguetown\mapgen\rogueoutdoors.dm"
+#include "code\modules\roguetown\roguecrafting\alchemy.dm"
#include "code\modules\roguetown\roguecrafting\cooking.dm"
#include "code\modules\roguetown\roguecrafting\fishing.dm"
#include "code\modules\roguetown\roguecrafting\items.dm"
@@ -3272,14 +3273,24 @@
#include "code\modules\spells\roguetown\acolyte\pestra.dm"
#include "code\modules\spells\roguetown\arcane\blindness.dm"
#include "code\modules\spells\roguetown\arcane\blink.dm"
+#include "code\modules\spells\roguetown\arcane\chains.dm"
+#include "code\modules\spells\roguetown\arcane\decay.dm"
+#include "code\modules\spells\roguetown\arcane\feather.dm"
#include "code\modules\spells\roguetown\arcane\fetch.dm"
#include "code\modules\spells\roguetown\arcane\fireball.dm"
+#include "code\modules\spells\roguetown\arcane\firewall.dm"
#include "code\modules\spells\roguetown\arcane\ignite.dm"
#include "code\modules\spells\roguetown\arcane\invisibility.dm"
+#include "code\modules\spells\roguetown\arcane\light.dm"
#include "code\modules\spells\roguetown\arcane\lightning.dm"
+#include "code\modules\spells\roguetown\arcane\magicwall.dm"
+#include "code\modules\spells\roguetown\arcane\repulse.dm"
+#include "code\modules\spells\roguetown\arcane\roots.dm"
#include "code\modules\spells\roguetown\arcane\smokescreen.dm"
#include "code\modules\spells\roguetown\arcane\swap.dm"
#include "code\modules\spells\roguetown\arcane\telepathy.dm"
+#include "code\modules\spells\roguetown\arcane\unlock.dm"
+#include "code\modules\spells\roguetown\arcane\web.dm"
#include "code\modules\spells\roguetown\other\grant_title.dm"
#include "code\modules\spells\roguetown\other\jester.dm"
#include "code\modules\spells\roguetown\other\recruiting.dm"
diff --git a/strings/books/spellbook.json b/strings/books/spellbook.json
new file mode 100644
index 000000000..52db1a2bc
--- /dev/null
+++ b/strings/books/spellbook.json
@@ -0,0 +1,3 @@
+{"Contents":["
+[all you can see is weird symbols mixed with unrecognizable words in a unrecognizable language...]
+"]}