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/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/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index a845359981..6cfb3d4eb2 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -438,6 +438,47 @@ 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/Initialize(loc, 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/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 + ) 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/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