From f6398b069088903cf0f4411a085511b1af4b58a1 Mon Sep 17 00:00:00 2001
From: NGGJamie <6971628+NGGJamie@users.noreply.github.com>
Date: Sat, 11 May 2024 16:36:26 -0500
Subject: [PATCH 1/3] Initial spell system
---
code/__DEFINES/flags.dm | 8 ++++
code/__DEFINES/misc.dm | 1 +
code/__HELPERS/global_lists.dm | 2 +
code/__HELPERS/unsorted.dm | 8 ++++
code/datums/mutations/actions.dm | 2 +
code/datums/mutations/antenna.dm | 2 +
code/datums/mutations/touch.dm | 2 +
code/game/objects/items/granters.dm | 40 +++++++++++++++++++
.../carbon/human/species_types/golems.dm | 2 +
code/modules/spells/roguetown/necromancer.dm | 8 ++++
code/modules/spells/roguetown/wizard.dm | 8 ++++
code/modules/spells/spell.dm | 3 +-
code/modules/spells/spell_types/aimed.dm | 2 +
code/modules/spells/spell_types/knock.dm | 2 +
code/modules/spells/spell_types/wizard.dm | 4 ++
tools/mapmerge2/requirements.txt | 2 +-
16 files changed, 94 insertions(+), 2 deletions(-)
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index 4d2bd566d8..ec944d52d9 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -146,3 +146,11 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
//alternate appearance flags
#define AA_TARGET_SEE_APPEARANCE (1<<0)
#define AA_MATCH_TARGET_OVERLAYS (1<<1)
+
+// spell rarities
+#define SPELL_COMMON (1 << 0)
+#define SPELL_UNCOMMON (1 << 1)
+#define SPELL_RARE (1 << 2)
+#define SPELL_EPIC (1 << 3)
+#define SPELL_LEGENDARY (1 << 4)
+#define SPELL_ALL (SPELL_COMMON | SPELL_UNCOMMON | SPELL_RARE | SPELL_EPIC | SPELL_LEGENDARY)
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index e39bd59cb1..0031c75867 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -235,6 +235,7 @@ GLOBAL_LIST_INIT(ghost_accs_options, list(GHOST_ACCS_NONE, GHOST_ACCS_DIR, GHOST
#define GHOST_MAX_VIEW_RANGE_DEFAULT 10
#define GHOST_MAX_VIEW_RANGE_MEMBER 14
+GLOBAL_LIST_INIT(learnables, list())
GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DEFAULT_SPRITE, GHOST_OTHERS_THEIR_SETTING)) //Same as ghost_accs_options.
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 3a51204f30..8cc07c9721 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -74,6 +74,8 @@
for(var/god in subtypesof(/datum/patrongods))
var/datum/patrongods/A = new god()
GLOB.patronlist[A.name] = A
+
+ 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.
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index c935879266..add6d4f01f 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -41,6 +41,14 @@
else if(dx<0)
.+=360
+/proc/Get_Learnable_Spells()
+ var/ret = list()
+ for(var/S in GLOB.spells)
+ var/obj/effect/proc_holder/spell/spell = S
+ if(spell.learnable)
+ ret += spell
+ return ret
+
/proc/Get_Pixel_Angle(y, x)//for getting the angle when animating something's pixel_x and pixel_y
if(!y)
return (x>=0)?90:270
diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm
index fed36a1f97..cba188863d 100644
--- a/code/datums/mutations/actions.dm
+++ b/code/datums/mutations/actions.dm
@@ -119,6 +119,8 @@
school = "evocation"
charge_max = 600
clothes_req = FALSE
+ learnable = TRUE
+ rarity = 0
range = 20
projectile_type = /obj/projectile/magic/aoe/fireball/firebreath
base_icon_state = "fireball"
diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm
index bc880e2661..96480e616a 100644
--- a/code/datums/mutations/antenna.dm
+++ b/code/datums/mutations/antenna.dm
@@ -53,6 +53,8 @@
name = "Mindread"
desc = ""
charge_max = 50
+ learnable = TRUE
+ rarity = 0
range = 7
clothes_req = FALSE
action_icon_state = "mindread"
diff --git a/code/datums/mutations/touch.dm b/code/datums/mutations/touch.dm
index 7232041f88..59cbc42855 100644
--- a/code/datums/mutations/touch.dm
+++ b/code/datums/mutations/touch.dm
@@ -15,6 +15,8 @@
drawmessage = "You channel electricity into your hand."
dropmessage = "You let the electricity from your hand dissipate."
hand_path = /obj/item/melee/touch_attack/shock
+ learnable = TRUE
+ rarity = 0
charge_max = 100
clothes_req = FALSE
action_icon_state = "zap"
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index a845359981..146cc302c5 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -438,6 +438,46 @@
drop_sound = 'sound/foley/dropsound/paper_drop.ogg'
pickup_sound = 'sound/blank.ogg'
+/obj/item/book/granter/spell/generic
+ name = "Spellbook"
+ desc = "A book of potential known only to those that can decipher its secrets."
+ icon = 'icons/obj/library.dmi'
+ icon_state = "book1"
+ oneuse = TRUE
+ drop_sound = 'sound/foley/dropsound/paper_drop.ogg'
+ pickup_sound = 'sound/blank.ogg'
+ var/obj/effect/proc_holder/spell/target = null
+
+/obj/item/book/granter/spell/generic/proc/pick_spell(rarity = SPELL_ALL)
+ target = pick(GLOB.learnables)
+ var/obj/effect/proc_holder/spell/S = new target
+ spellname = S.name
+ name = "Book of [spellname]"
+
+/obj/item/book/granter/spell/generic/already_known(mob/user)
+ if(!target)
+ return TRUE
+ for(var/obj/effect/proc_holder/spell/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)
+ . = ..()
+ if(oneuse)
+ name = "Siphoned Book of [target.name]"
+ 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!")
+ to_chat(user, "Your knowledge expands, you understand how to cast [spellname]!")
+ var/S = new target
+ user.mind.AddSpell(S)
+ user.log_message("learned the spell [target.name] ([target])", LOG_ATTACK, color="orange")
+ onlearned(user)
+
/obj/item/book/granter/spell/blackstone/onlearned(mob/living/carbon/user)
..()
if(oneuse == TRUE)
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index 5bfb8bae7d..440ce6a74c 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -1048,6 +1048,8 @@
name = "Snowball"
desc = ""
item_type = /obj/item/toy/snowball
+ learnable = TRUE
+ rarity = 0
charge_max = 15
action_icon = 'icons/obj/toy.dmi'
action_icon_state = "snowball"
diff --git a/code/modules/spells/roguetown/necromancer.dm b/code/modules/spells/roguetown/necromancer.dm
index c7110d75e8..a6b46e06a9 100644
--- a/code/modules/spells/roguetown/necromancer.dm
+++ b/code/modules/spells/roguetown/necromancer.dm
@@ -4,6 +4,8 @@
releasedrain = 30
chargetime = 5
range = 7
+ learnable = TRUE
+ rarity = 0
warnie = "sydwarning"
movement_interrupt = FALSE
chargedloop = null
@@ -37,6 +39,8 @@
overlay_state = "raiseskele"
releasedrain = 30
chargetime = 15
+ learnable = TRUE
+ rarity = 0
range = 7
warnie = "sydwarning"
movement_interrupt = FALSE
@@ -63,6 +67,8 @@
desc = ""
clothes_req = FALSE
range = 7
+ learnable = TRUE
+ rarity = 0
overlay_state = "raiseskele"
sound = list('sound/magic/magnet.ogg')
releasedrain = 40
@@ -86,6 +92,8 @@
name = "Ray of Sickness"
desc = ""
clothes_req = FALSE
+ learnable = TRUE
+ rarity = 0
range = 15
projectile_type = /obj/projectile/magic/sickness
overlay_state = "raiseskele"
diff --git a/code/modules/spells/roguetown/wizard.dm b/code/modules/spells/roguetown/wizard.dm
index 1b31638f8b..07d5823bed 100644
--- a/code/modules/spells/roguetown/wizard.dm
+++ b/code/modules/spells/roguetown/wizard.dm
@@ -3,6 +3,8 @@
name = "Bolt of Lightning"
desc = ""
clothes_req = FALSE
+ learnable = TRUE
+ rarity = 0
overlay_state = "lightning"
sound = 'sound/magic/lightning.ogg'
range = 8
@@ -154,6 +156,8 @@
/obj/effect/proc_holder/spell/invoked/projectile/fireball
name = "Fireball"
desc = ""
+ learnable = TRUE
+ rarity = 80
clothes_req = FALSE
range = 8
projectile_type = /obj/projectile/magic/aoe/fireball/rogue
@@ -205,6 +209,8 @@
desc = ""
clothes_req = FALSE
range = 8
+ learnable = TRUE
+ rarity = 10
projectile_type = /obj/projectile/magic/aoe/fireball/rogue/great
overlay_state = "fireball"
sound = list('sound/magic/fireball.ogg')
@@ -230,6 +236,8 @@
name = "Fetch"
desc = ""
clothes_req = FALSE
+ learnable = TRUE
+ rarity = 0
range = 15
projectile_type = /obj/projectile/magic/fetch
overlay_state = ""
diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm
index 0f19fabbe0..ad25623c54 100644
--- a/code/modules/spells/spell.dm
+++ b/code/modules/spells/spell.dm
@@ -1,7 +1,6 @@
#define TARGET_CLOSEST 1
#define TARGET_RANDOM 2
-
/obj/effect/proc_holder
var/panel = "Debug"//What panel the proc holder needs to go on.
var/active = FALSE //Used by toggle based abilities.
@@ -129,6 +128,8 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
opacity = 0
var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit?
+ var/learnable = FALSE // The spell is acquirable outside of admin intervention or through spawning. Default to "No" because many shouldn't be learned.
+ var/rarity = 0 // Percentage chance to scribe.
var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that
diff --git a/code/modules/spells/spell_types/aimed.dm b/code/modules/spells/spell_types/aimed.dm
index 4ae996b1d9..e26117a4ac 100644
--- a/code/modules/spells/spell_types/aimed.dm
+++ b/code/modules/spells/spell_types/aimed.dm
@@ -101,6 +101,8 @@
desc = ""
school = "evocation"
charge_max = 200
+ learnable = TRUE
+ rarity = 0
clothes_req = TRUE
invocation = "UN'LTD P'WAH"
invocation_type = "shout"
diff --git a/code/modules/spells/spell_types/knock.dm b/code/modules/spells/spell_types/knock.dm
index 341eae2ec2..13b027432c 100644
--- a/code/modules/spells/spell_types/knock.dm
+++ b/code/modules/spells/spell_types/knock.dm
@@ -4,6 +4,8 @@
school = "transmutation"
charge_max = 100
+ learnable = TRUE
+ rarity = 0
clothes_req = FALSE
invocation = "AULIE OXIN FIERA"
invocation_type = "whisper"
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index aff55b5a36..4284219327 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -5,6 +5,8 @@
school = "evocation"
charge_max = 200
clothes_req = TRUE
+ learnable = TRUE
+ rarity = 0
invocation = "FORTI GY AMA"
invocation_type = "shout"
range = 7
@@ -232,6 +234,8 @@
desc = ""
school = "transmutation"
+ learnable = TRUE
+ rarity = 0
charge_max = 300
clothes_req = FALSE
invocation = "STI KALY"
diff --git a/tools/mapmerge2/requirements.txt b/tools/mapmerge2/requirements.txt
index fad43483a7..d24cb40dcc 100644
--- a/tools/mapmerge2/requirements.txt
+++ b/tools/mapmerge2/requirements.txt
@@ -1,3 +1,3 @@
-#pygit2==0.27.2
+pygit2==0.27.2
bidict==0.13.1
Pillow==6.2.0
From af596bf7b2be7539cf69a61f4d23d50117077dd9 Mon Sep 17 00:00:00 2001
From: NGGJamie <6971628+NGGJamie@users.noreply.github.com>
Date: Sat, 11 May 2024 17:52:18 -0500
Subject: [PATCH 2/3] remove a lot of TG spells from learnable (they mostly
don't work)
---
code/datums/mutations/actions.dm | 2 --
code/datums/mutations/touch.dm | 2 --
code/modules/mob/living/carbon/human/species_types/golems.dm | 2 --
code/modules/spells/spell_types/aimed.dm | 2 --
code/modules/spells/spell_types/knock.dm | 2 --
code/modules/spells/spell_types/wizard.dm | 4 ----
6 files changed, 14 deletions(-)
diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm
index cba188863d..fed36a1f97 100644
--- a/code/datums/mutations/actions.dm
+++ b/code/datums/mutations/actions.dm
@@ -119,8 +119,6 @@
school = "evocation"
charge_max = 600
clothes_req = FALSE
- learnable = TRUE
- rarity = 0
range = 20
projectile_type = /obj/projectile/magic/aoe/fireball/firebreath
base_icon_state = "fireball"
diff --git a/code/datums/mutations/touch.dm b/code/datums/mutations/touch.dm
index 59cbc42855..7232041f88 100644
--- a/code/datums/mutations/touch.dm
+++ b/code/datums/mutations/touch.dm
@@ -15,8 +15,6 @@
drawmessage = "You channel electricity into your hand."
dropmessage = "You let the electricity from your hand dissipate."
hand_path = /obj/item/melee/touch_attack/shock
- learnable = TRUE
- rarity = 0
charge_max = 100
clothes_req = FALSE
action_icon_state = "zap"
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index 440ce6a74c..5bfb8bae7d 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -1048,8 +1048,6 @@
name = "Snowball"
desc = ""
item_type = /obj/item/toy/snowball
- learnable = TRUE
- rarity = 0
charge_max = 15
action_icon = 'icons/obj/toy.dmi'
action_icon_state = "snowball"
diff --git a/code/modules/spells/spell_types/aimed.dm b/code/modules/spells/spell_types/aimed.dm
index e26117a4ac..4ae996b1d9 100644
--- a/code/modules/spells/spell_types/aimed.dm
+++ b/code/modules/spells/spell_types/aimed.dm
@@ -101,8 +101,6 @@
desc = ""
school = "evocation"
charge_max = 200
- learnable = TRUE
- rarity = 0
clothes_req = TRUE
invocation = "UN'LTD P'WAH"
invocation_type = "shout"
diff --git a/code/modules/spells/spell_types/knock.dm b/code/modules/spells/spell_types/knock.dm
index 13b027432c..341eae2ec2 100644
--- a/code/modules/spells/spell_types/knock.dm
+++ b/code/modules/spells/spell_types/knock.dm
@@ -4,8 +4,6 @@
school = "transmutation"
charge_max = 100
- learnable = TRUE
- rarity = 0
clothes_req = FALSE
invocation = "AULIE OXIN FIERA"
invocation_type = "whisper"
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index 4284219327..aff55b5a36 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -5,8 +5,6 @@
school = "evocation"
charge_max = 200
clothes_req = TRUE
- learnable = TRUE
- rarity = 0
invocation = "FORTI GY AMA"
invocation_type = "shout"
range = 7
@@ -234,8 +232,6 @@
desc = ""
school = "transmutation"
- learnable = TRUE
- rarity = 0
charge_max = 300
clothes_req = FALSE
invocation = "STI KALY"
From 86b986e6f8eed006ebf906ec1ee18530ac92690b Mon Sep 17 00:00:00 2001
From: NGGJamie <6971628+NGGJamie@users.noreply.github.com>
Date: Mon, 13 May 2024 03:31:54 -0500
Subject: [PATCH 3/3] Basic creation, kinda. Exact rarity determination is WIP
---
code/game/objects/items/granters.dm | 3 +-
code/game/objects/items/rogueitems/books.dm | 46 +++++++++++++++++++
.../items/rogueitems/natural/feather.dm | 10 +++-
code/modules/roguetown/roguecrafting/items.dm | 8 ++++
4 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index 146cc302c5..6cfb3d4eb2 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -448,7 +448,8 @@
pickup_sound = 'sound/blank.ogg'
var/obj/effect/proc_holder/spell/target = null
-/obj/item/book/granter/spell/generic/proc/pick_spell(rarity = SPELL_ALL)
+/obj/item/book/granter/spell/generic/Initialize(loc, rarity = SPELL_ALL)
+ . = ..()
target = pick(GLOB.learnables)
var/obj/effect/proc_holder/spell/S = new target
spellname = S.name
diff --git a/code/game/objects/items/rogueitems/books.dm b/code/game/objects/items/rogueitems/books.dm
index 0f65d45013..ee899e990d 100644
--- a/code/game/objects/items/rogueitems/books.dm
+++ b/code/game/objects/items/rogueitems/books.dm
@@ -432,6 +432,52 @@
var/qdel_source = FALSE
/obj/item/manuscript/attackby(obj/item/I, mob/living/user)
+ if(resistance_flags & ON_FIRE)
+ return ..()
+
+ if(is_blind(user))
+ return ..()
+
+ if(istype(I, /obj/item/natural/feather/magic))
+ var/qualifying = 0
+ var/exp_gain = 0
+ var/obj/item/natural/feather/magic/F = I
+
+ if(prob(5)) // They rolled a nat20
+ qualifying = SPELL_EPIC | SPELL_LEGENDARY
+ exp_gain = 100
+ to_chat(user, "You feel your feather moving with a life of its own, as you struggle to comprehend what you write.")
+ else
+ var/skill_score = 0
+ var/spell_score = 0
+ if(/datum/skill/magic/arcane in user.mind.known_skills)
+ skill_score = user.mind.known_skills[/datum/skill/magic/arcane] * 0.1
+ spell_score = number_of_pages * 10 + skill_score
+ if(spell_score >= 10) // No, this doesn't make sense right now. I'm going to sleep on it
+ qualifying |= SPELL_COMMON // and figure out how to balance this thing.
+ if(spell_score >= 50)
+ qualifying |= SPELL_UNCOMMON
+ if(spell_score >= 95)
+ qualifying |= SPELL_RARE
+ if(spell_score >= 105)
+ qualifying |= SPELL_EPIC
+ if(spell_score >= 115)
+ qualifying |= SPELL_LEGENDARY
+
+ if(qualifying)
+ var/obj/item/book/granter/spell/generic/SB = new /obj/item/book/granter/spell/generic(get_turf(I.loc), qualifying)
+ if(user.Adjacent(SB))
+ SB.add_fingerprint(user)
+ user.put_in_hands(SB)
+ if(exp_gain)
+ user.mind.adjust_experience(/datum/skill/magic/arcane, exp_gain)
+ to_chat(user, "As you finish writing, the feather glows and envelops the manuscript, becoming a new spellbook.")
+ // 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))
var/obj/item/book/rogue/playerbook/PB = new /obj/item/book/rogue/playerbook(get_turf(I.loc), TRUE, user, compiled_pages)
diff --git a/code/game/objects/items/rogueitems/natural/feather.dm b/code/game/objects/items/rogueitems/natural/feather.dm
index cc0517ed75..03392025d7 100644
--- a/code/game/objects/items/rogueitems/natural/feather.dm
+++ b/code/game/objects/items/rogueitems/natural/feather.dm
@@ -15,4 +15,12 @@
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 that seems to shimmer with a faint magical aura."
+ var/uses = 0
+ var/max_uses = 3 // TODO: Balance this
+
\ No newline at end of file
diff --git a/code/modules/roguetown/roguecrafting/items.dm b/code/modules/roguetown/roguecrafting/items.dm
index fd96f30b41..4f29ed8afd 100644
--- a/code/modules/roguetown/roguecrafting/items.dm
+++ b/code/modules/roguetown/roguecrafting/items.dm
@@ -238,3 +238,11 @@
/obj/item/natural/cloth = 1)
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
+ )