diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 271cfce59512..71bfdfcab4df 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -185,6 +185,11 @@ if(MOVABLE_LIGHT_BEAM) AddComponent(/datum/component/overlay_lighting, is_directional = TRUE, is_beam = TRUE) + // HUGE NON-MODULE CHANGE + if (has_initial_mana_pool && can_have_mana_pool()) + mana_pool = initialize_mana_pool() + // END NON-MODULE CHANGE + /atom/movable/Destroy(force) QDEL_NULL(language_holder) QDEL_NULL(em_block) @@ -240,6 +245,8 @@ if (length(vis_contents)) vis_contents.Cut() + QDEL_NULL(mana_pool) // MAJOR NON-MODULE CHANGE + /atom/movable/proc/update_emissive_block() // This one is incredible. // `if (x) else { /* code */ }` is surprisingly fast, and it's faster than a switch, which is seemingly not a jump table. diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm index 5f78805d85be..4707b7b91d63 100644 --- a/code/modules/hydroponics/grown/flowers.dm +++ b/code/modules/hydroponics/grown/flowers.dm @@ -221,7 +221,7 @@ icon_grow = "moonflower-grow" icon_dead = "sunflower-dead" product = /obj/item/food/grown/moonflower - genes = list(/datum/plant_gene/trait/glow/purple, /datum/plant_gene/trait/preserved) + genes = list(/datum/plant_gene/trait/glow/purple, /datum/plant_gene/trait/preserved, /datum/plant_gene/reagent/misty_quintessence) // NON-MODULE CHANGE mutatelist = null reagents_add = list(/datum/reagent/consumable/ethanol/moonshine = 0.2, /datum/reagent/consumable/nutriment/vitamin = 0.02, /datum/reagent/consumable/nutriment = 0.02) rarity = 15 @@ -334,7 +334,7 @@ potency = 15 instability = 3 growthstages = 3 - genes = list(/datum/plant_gene/reagent/preset/carbon, /datum/plant_gene/trait/preserved) + genes = list(/datum/plant_gene/reagent/preset/carbon, /datum/plant_gene/trait/preserved, /datum/plant_gene/reagent/agnosticine) // NON-MODULE CHANGE growing_icon = 'icons/obj/service/hydroponics/growing_flowers.dmi' icon_grow = "carbonrose-grow" icon_dead = "carbonrose-dead" diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm index 0e59231141fc..1394c41d6dc4 100644 --- a/code/modules/hydroponics/grown/melon.dm +++ b/code/modules/hydroponics/grown/melon.dm @@ -47,7 +47,7 @@ species = "holymelon" plantname = "Holy Melon Vines" product = /obj/item/food/grown/holymelon - genes = list(/datum/plant_gene/trait/glow/yellow, /datum/plant_gene/trait/anti_magic) + genes = list(/datum/plant_gene/trait/glow/yellow, /datum/plant_gene/trait/anti_magic, /datum/plant_gene/reagent/foggy_agnosticine) // NON-MODULE CHANGE mutatelist = null reagents_add = list(/datum/reagent/water/holywater = 0.2, /datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/consumable/nutriment = 0.1) rarity = PLANT_MODERATELY_RARE diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm index ac8748b074c3..c127118f44a7 100644 --- a/code/modules/hydroponics/grown/mushrooms.dm +++ b/code/modules/hydroponics/grown/mushrooms.dm @@ -311,7 +311,7 @@ icon_dead = "shadowshroom-dead" plantname = "Shadowshrooms" product = /obj/item/food/grown/mushroom/glowshroom/shadowshroom - genes = list(/datum/plant_gene/trait/glow/shadow, /datum/plant_gene/trait/plant_type/fungal_metabolism) + genes = list(/datum/plant_gene/trait/glow/shadow, /datum/plant_gene/trait/plant_type/fungal_metabolism, /datum/plant_gene/reagent/fading_agnosticine) // NON-MODULE CHANGE mutatelist = null reagents_add = list(/datum/reagent/uranium/radium = 0.2, /datum/reagent/consumable/nutriment = 0.04) rarity = 30 @@ -347,7 +347,7 @@ instability = 65 growthstages = 3 product = /obj/item/food/grown/mushroom/odious_puffball - genes = list(/datum/plant_gene/trait/smoke, /datum/plant_gene/trait/plant_type/fungal_metabolism, /datum/plant_gene/trait/squash) + genes = list(/datum/plant_gene/trait/smoke, /datum/plant_gene/trait/plant_type/fungal_metabolism, /datum/plant_gene/trait/squash, /datum/plant_gene/reagent/agnosticine) // NON-MODULE CHANGE reagents_add = list(/datum/reagent/toxin/spore = 0.2, /datum/reagent/consumable/nutriment = 0.04) rarity = 35 graft_gene = /datum/plant_gene/trait/smoke diff --git a/code/modules/hydroponics/grown/peas.dm b/code/modules/hydroponics/grown/peas.dm index c232ed247c69..9ca0250551ed 100644 --- a/code/modules/hydroponics/grown/peas.dm +++ b/code/modules/hydroponics/grown/peas.dm @@ -75,7 +75,7 @@ growthstages = 3 icon_grow = "worldpeas-grow" icon_dead = "worldpeas-dead" - genes = list (/datum/plant_gene/trait/glow/blue) + genes = list (/datum/plant_gene/trait/glow/blue, /datum/plant_gene/reagent/crystalized_quintessence) // NON-MODULE CHANGE reagents_add = list (/datum/reagent/pax = 0.1, /datum/reagent/drug/happiness = 0.1, /datum/reagent/consumable/nutriment = 0.15) rarity = 50 // This absolutely will make even the most hardened Syndicate Operators relax. graft_gene = /datum/plant_gene/trait/glow/blue diff --git a/code/modules/hydroponics/grown/plum.dm b/code/modules/hydroponics/grown/plum.dm index cac12bdb1eb5..13d546699588 100644 --- a/code/modules/hydroponics/grown/plum.dm +++ b/code/modules/hydroponics/grown/plum.dm @@ -34,7 +34,7 @@ species = "plumb" plantname = "Plumb Tree" product = /obj/item/food/grown/plum/plumb - genes = list(/datum/plant_gene/trait/repeated_harvest) + genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/reagent/foggy_agnosticine) // NON-MODULE CHANGE mutatelist = null reagents_add = list(/datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/consumable/nutriment = 0.1, /datum/reagent/lead = 0.04) rarity = 30 diff --git a/code/modules/hydroponics/grown/tea_coffee.dm b/code/modules/hydroponics/grown/tea_coffee.dm index b72e1e746bae..6bc99a550a29 100644 --- a/code/modules/hydroponics/grown/tea_coffee.dm +++ b/code/modules/hydroponics/grown/tea_coffee.dm @@ -32,6 +32,7 @@ species = "teaastra" plantname = "Tea Astra Plant" product = /obj/item/food/grown/tea/astra + genes = list(/datum/plant_gene/reagent/quintessence) // NON-MODULE CHANGE mutatelist = null reagents_add = list(/datum/reagent/medicine/synaptizine = 0.1, /datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/toxin/teapowder = 0.1) rarity = PLANT_MODERATELY_RARE diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm index 2c1dbb69e52b..b3edd0e52c44 100644 --- a/code/modules/mining/lavaland/ash_flora.dm +++ b/code/modules/mining/lavaland/ash_flora.dm @@ -297,7 +297,7 @@ production = 6 yield = 3 growthstages = 4 - genes = list(/datum/plant_gene/trait/sticky, /datum/plant_gene/trait/stinging) + genes = list(/datum/plant_gene/trait/sticky, /datum/plant_gene/trait/stinging, /datum/plant_gene/reagent/quintessence) // NON-MODULE CHANGE graft_gene = /datum/plant_gene/trait/sticky growing_icon = 'icons/obj/service/hydroponics/growing_vegetables.dmi' reagents_add = list(/datum/reagent/water = 0.08, /datum/reagent/consumable/nutriment = 0.05, /datum/reagent/medicine/c2/helbital = 0.05) @@ -375,7 +375,7 @@ species = "fireblossom" growthstages = 3 product = /obj/item/food/grown/ash_flora/fireblossom - genes = list(/datum/plant_gene/trait/fire_resistance, /datum/plant_gene/trait/glow/yellow) + genes = list(/datum/plant_gene/trait/fire_resistance, /datum/plant_gene/trait/glow/yellow, /datum/plant_gene/reagent/misty_quintessence) // NON-MODULE CHANGE growing_icon = 'icons/obj/service/hydroponics/growing_flowers.dmi' reagents_add = list(/datum/reagent/consumable/tinlux = 0.04, /datum/reagent/consumable/nutriment = 0.03, /datum/reagent/carbon = 0.05) diff --git a/maplestation.dme b/maplestation.dme index c1b90d86f5ec..de13641acd61 100644 --- a/maplestation.dme +++ b/maplestation.dme @@ -6077,7 +6077,11 @@ #include "maplestation_modules\code\__DEFINES\traits.dm" #include "maplestation_modules\code\__DEFINES\vv.dm" #include "maplestation_modules\code\__DEFINES\magic\attunements.dm" +#include "maplestation_modules\code\__DEFINES\magic\magic_bitflags.dm" #include "maplestation_modules\code\__DEFINES\magic\magic_defines.dm" +#include "maplestation_modules\code\__DEFINES\magic\magic_signals.dm" +#include "maplestation_modules\code\__DEFINES\magic\mana_charge_bitflags.dm" +#include "maplestation_modules\code\__DEFINES\magic\mana_pool_process_bitflags.dm" #include "maplestation_modules\code\__DEFINES\magic\spellbook\spellbook_categories.dm" #include "maplestation_modules\code\__DEFINES\magic\spellbook\spellbook_customization_interfaces.dm" #include "maplestation_modules\code\__DEFINES\magic\spellbook\spellbook_entry_types.dm" @@ -6165,6 +6169,7 @@ #include "maplestation_modules\code\game\objects\items\devices\radio\encryptionkey.dm" #include "maplestation_modules\code\game\objects\items\devices\radio\headset.dm" #include "maplestation_modules\code\game\objects\items\devices\scanners\autopsy_scanner.dm" +#include "maplestation_modules\code\game\objects\items\devices\scanners\mana_lens.dm" #include "maplestation_modules\code\game\objects\items\other_loadout_items\loadout_inhand_items.dm" #include "maplestation_modules\code\game\objects\items\other_loadout_items\loadout_pocket_items.dm" #include "maplestation_modules\code\game\objects\items\storage\belt.dm" @@ -6331,6 +6336,7 @@ #include "maplestation_modules\code\modules\food_and_drinks\recipes\drinks_recipes.dm" #include "maplestation_modules\code\modules\food_and_drinks\recipes\food\egg_recipes.dm" #include "maplestation_modules\code\modules\hydroponics\hydroponics.dm" +#include "maplestation_modules\code\modules\hydroponics\plant_genes.dm" #include "maplestation_modules\code\modules\jobs\job_types\_job.dm" #include "maplestation_modules\code\modules\jobs\job_types\asset_protection.dm" #include "maplestation_modules\code\modules\jobs\job_types\assistant.dm" @@ -6372,17 +6378,20 @@ #include "maplestation_modules\code\modules\loadouts\loadout_ui\limbs.dm" #include "maplestation_modules\code\modules\loadouts\loadout_ui\loadout_manager.dm" #include "maplestation_modules\code\modules\loadouts\loadout_ui\loadout_outfit_helpers.dm" -#include "maplestation_modules\code\modules\magic\attunement.dm" -#include "maplestation_modules\code\modules\magic\magic.dm" -#include "maplestation_modules\code\modules\magic\magic_subsystem.dm" +#include "maplestation_modules\code\modules\magic\spell.dm" #include "maplestation_modules\code\modules\magic\components\has_mana.dm" #include "maplestation_modules\code\modules\magic\components\uses_mana.dm" +#include "maplestation_modules\code\modules\magic\mana\living_mana.dm" #include "maplestation_modules\code\modules\magic\mana\mana.dm" -#include "maplestation_modules\code\modules\magic\mana\misc_mana.dm" +#include "maplestation_modules\code\modules\magic\mana\mana_chemicals.dm" +#include "maplestation_modules\code\modules\magic\mana\mana_overload.dm" +#include "maplestation_modules\code\modules\magic\mana\mana_pool.dm" +#include "maplestation_modules\code\modules\magic\mana\attunements\attunement.dm" #include "maplestation_modules\code\modules\magic\mana\sources\mana_batteries.dm" -#include "maplestation_modules\code\modules\magic\mana\sources\transmutation.dm" -#include "maplestation_modules\code\modules\magic\mana\sources\leylines\leyline_intensities.dm" #include "maplestation_modules\code\modules\magic\mana\sources\leylines\leylines.dm" +#include "maplestation_modules\code\modules\magic\mana\sources\leylines\leyline_intensities\leyline_attunements.dm" +#include "maplestation_modules\code\modules\magic\mana\sources\leylines\leyline_intensities\leyline_intensities.dm" +#include "maplestation_modules\code\modules\magic\mana\sources\leylines\leyline_intensities\leyline_variable.dm" #include "maplestation_modules\code\modules\magic\story_spells\acid_touch.dm" #include "maplestation_modules\code\modules\magic\story_spells\airhike.dm" #include "maplestation_modules\code\modules\magic\story_spells\convect.dm" @@ -6394,13 +6403,15 @@ #include "maplestation_modules\code\modules\magic\story_spells\ice_knife.dm" #include "maplestation_modules\code\modules\magic\story_spells\illusion.dm" #include "maplestation_modules\code\modules\magic\story_spells\mage_hand.dm" +#include "maplestation_modules\code\modules\magic\story_spells\mana_charge.dm" +#include "maplestation_modules\code\modules\magic\story_spells\mana_sense.dm" #include "maplestation_modules\code\modules\magic\story_spells\shock_touch.dm" #include "maplestation_modules\code\modules\magic\story_spells\soothe.dm" #include "maplestation_modules\code\modules\magic\story_spells\thaumatergic_sense.dm" #include "maplestation_modules\code\modules\magic\story_spells\water_control.dm" -#include "maplestation_modules\code\modules\magic\story_spells\components\pointed_component.dm" #include "maplestation_modules\code\modules\magic\story_spells\components\story_spell_component.dm" #include "maplestation_modules\code\modules\magic\story_spells\components\touch_component.dm" +#include "maplestation_modules\code\modules\magic\subsystems\magic_subsystem.dm" #include "maplestation_modules\code\modules\mining\mining_redemption.dm" #include "maplestation_modules\code\modules\mining\order_consumables.dm" #include "maplestation_modules\code\modules\mining\ores_coins.dm" diff --git a/maplestation_modules/code/__DEFINES/magic/attunements.dm b/maplestation_modules/code/__DEFINES/magic/attunements.dm index 8c0c1da8cef3..40fdb40cfbd3 100644 --- a/maplestation_modules/code/__DEFINES/magic/attunements.dm +++ b/maplestation_modules/code/__DEFINES/magic/attunements.dm @@ -20,6 +20,9 @@ #define MAGIC_ELEMENT_ICE /datum/attunement/ice #define MAGIC_ELEMENT_LIGHT /datum/attunement/light #define MAGIC_ELEMENT_WIND /datum/attunement/wind +#define MAGIC_ELEMENT_LIFE /datum/attunement/life +#define MAGIC_ELEMENT_EARTH /datum/attunement/earth +#define MAGIC_ELEMENT_ELECTRIC /datum/attunement/electric // When other elements are used, add them here // Alignments diff --git a/maplestation_modules/code/__DEFINES/magic/magic_bitflags.dm b/maplestation_modules/code/__DEFINES/magic/magic_bitflags.dm new file mode 100644 index 000000000000..93c99c927756 --- /dev/null +++ b/maplestation_modules/code/__DEFINES/magic/magic_bitflags.dm @@ -0,0 +1,11 @@ + +#define NO_MANA_POOL (1<<0) +#define MANA_POOL_FULL (1<<1) + +#define MANA_POOL_TRANSFER_START (1<<2) +#define MANA_POOL_TRANSFER_STOP (1<<3) + +#define MANA_POOL_ALREADY_TRANSFERRING (1<<4) +#define MANA_POOL_CANNOT_TRANSFER (1<<5) + +#define MANA_POOL_TRANSFER_SKIP_ACTIVE (1<<6) diff --git a/maplestation_modules/code/__DEFINES/magic/magic_defines.dm b/maplestation_modules/code/__DEFINES/magic/magic_defines.dm index a331ddffb6f0..58869e1b1d22 100644 --- a/maplestation_modules/code/__DEFINES/magic/magic_defines.dm +++ b/maplestation_modules/code/__DEFINES/magic/magic_defines.dm @@ -1,11 +1,46 @@ /// Magic -#define BASE_STORY_MAGIC_CAST_COST_MULT 1 -#define NO_CATALYST_COST_MULT 4 - // Assumes we are at average leyline intensity -#define LEYLINE_BASE_CAPACITY 600 -#define LEYLINE_BASE_RECHARGE 2 // Per second, we recharge this much mana +#define LEYLINE_BASE_RECHARGE 0.1 // Per second, we recharge this much man + +#define MANA_CRYSTAL_BASE_HARDCAP 200 +#define MANA_CRYSTAL_BASE_RECHARGE 0.001 + +#define BASE_MANA_CAPACITY 1000 +#define MANA_CRYSTAL_BASE_MANA_CAPACITY (BASE_MANA_CAPACITY * 0.2) +#define CARBON_BASE_MANA_CAPACITY (BASE_MANA_CAPACITY) +#define LEYLINE_BASE_CAPACITY 600 //todo: standardize + +#define BASE_MANA_SOFTCAP (BASE_MANA_CAPACITY * 0.2) //20 percent +#define BASE_MANA_CRYSTAL_SOFTCAP MANA_CRYSTAL_BASE_MANA_CAPACITY +#define BASE_CARBON_MANA_SOFTCAP (CARBON_BASE_MANA_CAPACITY * 0.2) + +#define BASE_MANA_OVERLOAD_THRESHOLD (BASE_MANA_CAPACITY * 0.9) +#define MANA_CRYSTAL_OVERLOAD_THRESHOLD MANA_CRYSTAL_BASE_MANA_CAPACITY +#define CARBON_MANA_OVERLOAD_THRESHOLD BASE_CARBON_MANA_SOFTCAP + +#define BASE_MANA_OVERLOAD_COEFFICIENT 1 +#define MANA_CRYSTAL_OVERLOAD_COEFFICIENT 0.1 +#define CARBON_MANA_OVERLOAD_COEFFICIENT 2 + +#define ROBOTIC_MANA_OVERLOAD_COEFFICIENT_MULT 3 +#define ROBOTIC_MANA_SOFTCAP_MULT 0.5 +#define ROBOTIC_MANA_OVERLOAD_THRESHOLD_MULT ROBOTIC_MANA_SOFTCAP_MULT + +#define MANA_OVERLOAD_DAMAGE_THRESHOLD 2 +#define MANA_OVERLOAD_BASE_DAMAGE 1 + +// inverse - higher numbers decrease the intensity of the decay +#define BASE_MANA_EXPONENTIAL_DIVISOR 60 // careful with this value - low numbers will cause some fuckery +#define BASE_CARBON_MANA_EXPONENTIAL_DIVISOR (BASE_MANA_EXPONENTIAL_DIVISOR * 0.5) +#define MANA_CRYSTAL_BASE_DECAY_DIVISOR (BASE_MANA_EXPONENTIAL_DIVISOR * 5) + +// in vols per second +#define BASE_MANA_DONATION_RATE (BASE_MANA_CAPACITY * 0.5) +#define BASE_MANA_CRYSTAL_DONATION_RATE (BASE_MANA_DONATION_RATE * 0.1) +#define BASE_LEYLINE_DONATION_RATE 30 + +#define MANA_BATTERY_MAX_TRANSFER_DISTANCE 3 #define MAGIC_MATERIAL_NAME "Volite" #define MAGIC_UNIT_OF_MEASUREMENT "Vol" @@ -17,3 +52,7 @@ #define THAUMATERGIC_SENSE_POOL_DISCERNMENT_LEVEL_ZERO 0 #define THAUMATERGIC_SENSE_POOL_DISCERNMENT_LEVEL_ONE 1 #define THAUMATERGIC_SENSE_POOL_DISCERNMENT_LEVEL_TWO 2 + +// MAGIC TRAITS GO HERE +// give this to an object to declare that its pool can be used during cast. +#define TRAIT_POOL_AVAILABLE_FOR_CAST "pool_available_for_cast" diff --git a/maplestation_modules/code/__DEFINES/magic/magic_signals.dm b/maplestation_modules/code/__DEFINES/magic/magic_signals.dm new file mode 100644 index 000000000000..5e22a0012136 --- /dev/null +++ b/maplestation_modules/code/__DEFINES/magic/magic_signals.dm @@ -0,0 +1,2 @@ +#define COMSIG_MANA_POOL_INTRINSIC_RECHARGE_UPDATE "mana_pool_intrinsic_recharge_update" +#define COMSIG_ATOM_MANA_POOL_CHANGED "atom_mana_pool_changed" diff --git a/maplestation_modules/code/__DEFINES/magic/mana_charge_bitflags.dm b/maplestation_modules/code/__DEFINES/magic/mana_charge_bitflags.dm new file mode 100644 index 000000000000..0275405b7b2a --- /dev/null +++ b/maplestation_modules/code/__DEFINES/magic/mana_charge_bitflags.dm @@ -0,0 +1,5 @@ + +#define MANA_ALL_LEYLINES (1<<1) + +#define MANA_DISPERSE_EVENLY 1 +#define MANA_SEQUENTIAL 2 diff --git a/maplestation_modules/code/__DEFINES/magic/mana_pool_process_bitflags.dm b/maplestation_modules/code/__DEFINES/magic/mana_pool_process_bitflags.dm new file mode 100644 index 000000000000..bc4e688dc610 --- /dev/null +++ b/maplestation_modules/code/__DEFINES/magic/mana_pool_process_bitflags.dm @@ -0,0 +1,2 @@ +#define MANA_POOL_SKIP_NEXT_TRANSFER (1<<0) +#define MANA_POOL_INTRINSIC (1<<1) diff --git a/maplestation_modules/code/datums/components/crafting/recipes.dm b/maplestation_modules/code/datums/components/crafting/recipes.dm index 47525951cee0..7f8c3cdac6cf 100644 --- a/maplestation_modules/code/datums/components/crafting/recipes.dm +++ b/maplestation_modules/code/datums/components/crafting/recipes.dm @@ -28,3 +28,14 @@ tool_behaviors = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WRENCH) time = 2 SECONDS category = CAT_WEAPON_MELEE + +/datum/crafting_recipe/volite_amulet + name = "Volite Amulet" + result = /obj/item/clothing/neck/mana_star + reqs = list( + /obj/item/mana_battery/mana_crystal/cut = 1, + /obj/item/stack/sheet/mineral/gold = 1, // cheap for now + ) + tool_behaviors = list(TOOL_SCREWDRIVER) + time = 5 SECONDS + category = CAT_EQUIPMENT diff --git a/maplestation_modules/code/game/objects/items/devices/scanners/mana_lens.dm b/maplestation_modules/code/game/objects/items/devices/scanners/mana_lens.dm new file mode 100644 index 000000000000..8c3b3d7a5ddc --- /dev/null +++ b/maplestation_modules/code/game/objects/items/devices/scanners/mana_lens.dm @@ -0,0 +1,39 @@ +// as of part one, this is just for the prototype. more will be added with higher report detail/verbosity +/obj/item/mana_lens + name = "Prototype Mana Lens" + icon = 'maplestation_modules/icons/obj/devices.dmi' + icon_state = "mana_lens" + desc = "A prototypical device used to read out the current amount of mana within a subject. The ergonomics are terrible." + item_flags = NOBLUDGEON + throwforce = 3 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + drop_sound = 'maplestation_modules/sound/items/drop/device2.ogg' + pickup_sound = 'maplestation_modules/sound/items/pickup/device.ogg' + +/obj/item/mana_lens/interact_with_atom(atom/movable/interacting_with, mob/living/user) + if (isturf(interacting_with)) + balloon_alert(user, "object has no mana pool!") // turfs should not ever have mana pools, doing this just so it doesn't error + return + if (!interacting_with.mana_pool) + balloon_alert(user, "object has no mana pool!") + return + balloon_alert(user, "mana amount: [interacting_with.mana_pool.amount]") + +/datum/design/proto_mana_lens + name = "Prototype Mana Lens" + desc = "The first prototype of a device capable of reading the prescence of mana." + id = "mana_lens" + build_type = PROTOLATHE + materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2.5, /datum/material/gold = SMALL_MATERIAL_AMOUNT) + build_path = /obj/item/mana_lens + +/datum/techweb_node/mana_base + id = "mana_base" + starting_node = TRUE + display_name = "Early Magical Tech" + description = "The first bits of technology surronding magic." + design_ids = list( + "mana_lens", // more will be added to this + ) diff --git a/maplestation_modules/code/modules/cargo/goodies.dm b/maplestation_modules/code/modules/cargo/goodies.dm index 7544f16471dd..c43b6714319d 100644 --- a/maplestation_modules/code/modules/cargo/goodies.dm +++ b/maplestation_modules/code/modules/cargo/goodies.dm @@ -5,3 +5,35 @@ cost = PAYCHECK_CREW * 4 crate_type = /obj/structure/closet/crate/critter contains = list(/obj/item/toy/plush/peepy) + +/datum/supply_pack/goody/cut_volite + name = "Cut Volite Gemstone" + desc = "An expertly cut volite gemstone, ready to be socketed into an amulet." + cost = PAYCHECK_CREW * 5 + contains = list( + /obj/item/mana_battery/mana_crystal/cut, + ) + +/datum/supply_pack/goody/mana_star + name = "Pre-Assembled Volite Amulet" + desc = "A volite gemstone pre-cut and placed within an amulet, saving you the hassle." + cost = PAYCHECK_CREW * 8 + contains = list( + /obj/item/clothing/neck/mana_star, + ) + +/datum/supply_pack/goody/volite_single_pack + name = "Volite Crystal Single Pack" + desc = "A singular volite crystal, ready for use." // planned to be cut with rework part 2, here for ease of access. + cost = PAYCHECK_CREW * 4 + contains = list( + /obj/item/mana_battery/mana_crystal/standard, + ) + +/datum/supply_pack/goody/small_volite_pack + name = "Small Volite Crystal Single Pack" + desc = "A miniaturized volite crystal." // planned to be cut with rework part 2 + cost = PAYCHECK_CREW * 2 + contains = list( + /obj/item/mana_battery/mana_crystal/small, + ) diff --git a/maplestation_modules/code/modules/cargo/packs.dm b/maplestation_modules/code/modules/cargo/packs.dm index ea7a8f06e3e7..0ae6710eaebf 100644 --- a/maplestation_modules/code/modules/cargo/packs.dm +++ b/maplestation_modules/code/modules/cargo/packs.dm @@ -267,3 +267,39 @@ /obj/item/clothing/mask/breath/ornithid/toucan, /obj/item/clothing/mask/breath/ornithid/bluejay, ) + +/datum/supply_pack/science/volite_shipment + name = "Volite Shipment" + desc = "A bundle containing 5 volite formations." + cost = PAYCHECK_COMMAND * 8 // 800, 4x the cost of a single volite crystal + contains = list( + /obj/item/mana_battery/mana_crystal/standard, + /obj/item/mana_battery/mana_crystal/standard, + /obj/item/mana_battery/mana_crystal/standard, + /obj/item/mana_battery/mana_crystal/standard, + /obj/item/mana_battery/mana_crystal/standard, + ) + +/datum/supply_pack/science/volite_shipment + name = "Small Volite Crystal Shipment" + desc = "A bundle containing 6 miniature volite crystals." + cost = PAYCHECK_COMMAND * 5 + contains = list( + /obj/item/mana_battery/mana_crystal/small, + /obj/item/mana_battery/mana_crystal/small, + /obj/item/mana_battery/mana_crystal/small, + /obj/item/mana_battery/mana_crystal/small, + /obj/item/mana_battery/mana_crystal/small, + /obj/item/mana_battery/mana_crystal/small, + ) + +/datum/supply_pack/science/cut_volite_crystals + name = "Cut Volite Gemstone Pack" + desc = "A bundle containing 4 expertly cut volite crystals, to be slotted in an amulet." + cost = PAYCHECK_COMMAND * 9 + contains = list( + /obj/item/mana_battery/mana_crystal/cut, + /obj/item/mana_battery/mana_crystal/cut, + /obj/item/mana_battery/mana_crystal/cut, + /obj/item/mana_battery/mana_crystal/cut, + ) diff --git a/maplestation_modules/code/modules/client/preferences/spellbook/items/spellbook_item_misc.dm b/maplestation_modules/code/modules/client/preferences/spellbook/items/spellbook_item_misc.dm index f3995afdc294..f31be38a637b 100644 --- a/maplestation_modules/code/modules/client/preferences/spellbook/items/spellbook_item_misc.dm +++ b/maplestation_modules/code/modules/client/preferences/spellbook/items/spellbook_item_misc.dm @@ -13,3 +13,36 @@ GLOBAL_LIST_INIT(spellbook_misc_items, generate_spellbook_items(SPELLBOOK_CATEGO category = SPELLBOOK_CATEGORY_MISC our_action_typepath = /datum/action/cooldown/spell/apply_mutations/mage_hand + +/* /datum/spellbook_item/spell/leyline_charge + name = "Leyline Charge" + description = "Draw mana straight from the leylines themselves." + lore = "The most basic method of regenerating mana on your own. \ + Casting this invocation- while focusing- will allow you to regain mana from the leylines themselves. \ + Do not that this is a finnicky way of regaining mana, and you risk overloading if done improperly." + + category = SPELLBOOK_CATEGORY_MISC + + our_action_typepath = /datum/action/cooldown/spell/leyline_charge */ // disabled because leylines are weird + +/datum/spellbook_item/spell/meditate + name = "Magic Meditation" + description = "Use mental focus to draw mana within yourself" + lore = "The most basic method of regenerating mana on your own. \ + Casting this invocation- while focusing- will allow you to draw mana from the ambient environment. \ + Do note that this will take a while between casts, and you should still find other methods of regeneration." + + category = SPELLBOOK_CATEGORY_MISC + + our_action_typepath = /datum/action/cooldown/spell/meditate + +/datum/spellbook_item/spell/mana_sense + name = "Mana Sense" + description = "Sense other mana pools present" + lore = "Using your magical attunement (or other aptitudes) \ + you can sense if a creature or object has a mana pool present; and what amount of mana the pool has. \ + Do note that this will require a reprieve between casts, and it will take a second to discern the amount of mana a pool has." + + category = SPELLBOOK_CATEGORY_MISC + + our_action_typepath = /datum/action/cooldown/spell/pointed/mana_sense diff --git a/maplestation_modules/code/modules/hydroponics/plant_genes.dm b/maplestation_modules/code/modules/hydroponics/plant_genes.dm new file mode 100644 index 000000000000..10d310a373de --- /dev/null +++ b/maplestation_modules/code/modules/hydroponics/plant_genes.dm @@ -0,0 +1,36 @@ +// file for new plant genes. +/datum/plant_gene/reagent/agnosticine + name = "Agnosticine" + reagent_id = /datum/reagent/toxin/agnosticine + rate = 0.1 +// added to: Odious Puffballs, Carbon Rose + +/datum/plant_gene/reagent/fading_agnosticine + name = "Fading Agnosticine" + reagent_id = /datum/reagent/toxin/agnosticine/fading + rate = 0.05 +// added to: shadowshroom + +/datum/plant_gene/reagent/foggy_agnosticine + name = "Foggy Agnosticine" + reagent_id = /datum/reagent/toxin/agnosticine/foggy + rate = 0.15 +// added to: holy melon, plumb + +/datum/plant_gene/reagent/quintessence + name = "Quintessence" + reagent_id = /datum/reagent/medicine/quintessence + rate = 0.1 +// added to: Tea Astra, Star Cactus + +/datum/plant_gene/reagent/crystalized_quintessence + name = "Crystalized Quintessence" + reagent_id = /datum/reagent/medicine/quintessence/crystalized + rate = 0.1 // higher than its agnosticine equivalent since world peas is a pain in the ass +// added to: world peas + +/datum/plant_gene/reagent/misty_quintessence + name = "Misty Quintessence" + reagent_id = /datum/reagent/medicine/quintessence/misty + rate = 0.15 +// added to: Moonflower, Fire Blossom diff --git a/maplestation_modules/code/modules/magic/HOW_TO_USE.md b/maplestation_modules/code/modules/magic/HOW_TO_USE.md index d43f15bef06b..20fdebfe32b8 100644 --- a/maplestation_modules/code/modules/magic/HOW_TO_USE.md +++ b/maplestation_modules/code/modules/magic/HOW_TO_USE.md @@ -5,7 +5,7 @@ Adding a mana user is very simple. All you have to do is subtype /datum/componen There are a few crucial steps to extending uses_mana. Firstly, you must override get_mana_required() and have it return the exact numerical value in EFFECTIVE mana (we will get to this later) it requires for the action to be cast. The action in this case can be quite literally anything - anything that requires a signal to determine -if it will execute. MAKE SURE TO MULTIPLY IT AGAINST THE USER'S CASTING COST MULT: spell.owner.get_casting_cost_mult() +if it will execute. If you wish your action to be fail if not enough mana is acquired (common behavior), register a signal to a signal handler from some sort of pre-usage check (story_spell_component uses COMSIG_SPELL_BEFORE_CAST, attached to handle_precast, so for spells this is already handled). Most things have a signal for this, you just diff --git a/maplestation_modules/code/modules/magic/README.md b/maplestation_modules/code/modules/magic/README.md index 7e75db9fb46d..f7261b8b79a8 100644 --- a/maplestation_modules/code/modules/magic/README.md +++ b/maplestation_modules/code/modules/magic/README.md @@ -17,8 +17,7 @@ This proc is also called on USE, where it gathers all the available mana (Potent The more a pool's attunements correspond to the values of a user, the lower the casting cost will be, and vice versa. Correspondance is determined by multiplying the attunement to a specific value with the attunement value of the other. Ex. 3\*1 = value of 3, 3\*-2 = value of -6. These attunement values are added together, then converted to a multiplier by proc/get_total_attunement_mult(), and then applied to the raw amount to determine the "effective" amount of mana if it would be applied to the mana user. This same effective amount is also what is actually used. If something has 50 mana but an attunement mult of 0.25, the effective mana is 200, and thus this 50 mana can be applied to a mana user with a requirement of 200. -It's also possible that, in the future, attunements/element will have inherent multipliers attached ot them, in the form of, say, lizards having 80% casting cost for fire magic. -Mobs also have a inherent "castng cost" multiplier, accessable through get_casting_cost_mult(). All instances of mana usage that require or anticipate a mob casting it should take this into account, and multiply the cost by this. +It's also possible that, in the future, attunements/element will have inherent multipliers attached ot them, in the form of, say, lizards having 80% casting cost for fire magic. All instances of mana usage that require or anticipate a mob casting it should take this into account, and multiply the cost by this. mana_users are instances of the mana_user component, which is, again, the component that designates having to use mana somewhere. All instaces of behavior that require mana should implement this component, as they are the only way to actually interact with the mana system and consume/request mana. mana_holders are datums that hold a mana_pool instance. They are primarily irrelevant at the moment - but will become required when mana is refactored into lists of gas_mixture-like datums. diff --git a/maplestation_modules/code/modules/magic/components/uses_mana.dm b/maplestation_modules/code/modules/magic/components/uses_mana.dm index 190e0c646452..c5352b3fd27e 100644 --- a/maplestation_modules/code/modules/magic/components/uses_mana.dm +++ b/maplestation_modules/code/modules/magic/components/uses_mana.dm @@ -10,76 +10,163 @@ */ /// Designates the item it's added to as something that "uses mana". /datum/component/uses_mana + var/datum/callback/get_mana_callback + var/datum/callback/activate_check_failure_callback + var/datum/callback/get_user_callback + var/datum/callback/get_mana_required_callback -/// Should return a list of attunements. Defaults to GLOB.default_attunements. -/datum/component/uses_mana/proc/get_attunement_dispositions() - return GLOB.default_attunements.Copy() + var/list/datum/attunement/attunements + + var/pre_use_check_with_feedback_comsig + var/pre_use_check_comsig + var/post_use_comsig + + var/mana_required + +/datum/component/uses_mana/Initialize( + datum/callback/activate_check_failure_callback, + datum/callback/get_user_callback, + pre_use_check_with_feedback_comsig, + pre_use_check_comsig, + post_use_comsig, + datum/callback/mana_required, + list/datum/attunement/attunements, +) + . = ..() + + if (isnull(pre_use_check_with_feedback_comsig)) + stack_trace("pre_use with feed back null") + return COMPONENT_INCOMPATIBLE + if (isnull(post_use_comsig)) + stack_trace("post_use comsig null") + return COMPONENT_INCOMPATIBLE + + src.activate_check_failure_callback = activate_check_failure_callback + src.get_user_callback = get_user_callback + + if (istype(mana_required)) + src.get_mana_required_callback = mana_required + else if (isnum(mana_required)) + src.mana_required = mana_required + + src.attunements = attunements + src.pre_use_check_with_feedback_comsig = pre_use_check_with_feedback_comsig + src.post_use_comsig = post_use_comsig -/// If we have consistant attunements, this should be used to modify them. -/datum/component/uses_mana/proc/modify_attunements(list/datum/attunement/incoming_attunements) - return + +/datum/component/uses_mana/RegisterWithParent() + . = ..() + + RegisterSignal(parent, pre_use_check_with_feedback_comsig, PROC_REF(can_activate_with_feedback)) + RegisterSignal(parent, post_use_comsig, PROC_REF(react_to_successful_use)) + +/datum/component/uses_mana/UnregisterFromParent() + . = ..() + + UnregisterSignal(parent, pre_use_check_with_feedback_comsig) + UnregisterSignal(parent, post_use_comsig) // TODO: Do I need the vararg? /// Should return the numerical value of mana needed to use whatever it is we're using. Unaffected by attunements. -/datum/component/uses_mana/proc/get_mana_required(atom/caster, ...) - return 0 +/datum/component/uses_mana/proc/get_mana_required(atom/caster, ...) // Get the mana required to cast the spell. + if (!isnull(get_mana_required_callback)) + return get_mana_required_callback?.Invoke(arglist(args)) + + var/required = 0 + + if (!isnull(mana_required)) + required = mana_required + else + return stack_trace("Both the Callback and value for mana required is null!") + return required + +/datum/component/uses_mana/proc/get_mana_to_use() + var/atom/movable/caster = get_parent_user() + var/list/datum/mana_pool/usable_pools = list() + + if (!isnull(caster.mana_pool)) + usable_pools += caster.mana_pool + + for (var/atom/movable/thing as anything in caster.get_all_contents()) + if (!isnull(thing.mana_pool) && HAS_TRAIT(thing, TRAIT_POOL_AVAILABLE_FOR_CAST)) + usable_pools += thing.mana_pool + + return usable_pools -/datum/component/uses_mana/get_available_mana(list/datum/attunement/attunements) - return parent.get_available_mana(attunements) /// Should return TRUE if the total adjusted mana of all mana pools surpasses get_mana_required(). FALSE otherwise. -/datum/component/uses_mana/proc/is_mana_sufficient(list/datum/mana_pool/provided_mana, atom/caster, ...) +/datum/component/uses_mana/proc/is_mana_sufficient(atom/movable/user, ...) var/total_effective_mana = 0 - var/list/datum/attunement/our_attunements = get_attunement_dispositions() + var/list/datum/mana_pool/provided_mana = get_mana_to_use() + var/required_mana = get_mana_required(arglist(args)) + var/atom/caster = user + + for (var/datum/mana_pool/iterated_pool as anything in provided_mana) - total_effective_mana += iterated_pool.get_attuned_amount(our_attunements, caster) + total_effective_mana += iterated_pool.get_attuned_amount(attunements, caster) + if (total_effective_mana > required_mana) + return TRUE + else + return FALSE + +/// The primary proc we will use for draining mana to simulate it being consumed to power our actions. +/datum/component/uses_mana/proc/drain_mana(...) + + var/mob/user = get_user_callback?.Invoke() + + var/mana_consumed = -get_mana_required(arglist(args)) + if (isnull(mana_consumed)) + stack_trace("mana_consumed after get_mana_required is null!") + return - var/mana_required = get_mana_required(arglist(args.Copy(2))) - testing("Checking [mana_required] / [total_effective_mana] mana.") - return mana_required <= total_effective_mana + var/list/datum/mana_pool/available_pools = get_mana_to_use() + + while (mana_consumed <= -0.05) + var/mult + var/attuned_cost + for (var/datum/mana_pool/pool as anything in available_pools) + mult = pool.get_overall_attunement_mults(attunements, user) + attuned_cost = (mana_consumed * mult) + if (pool.amount < attuned_cost) + attuned_cost = (pool.amount) + mana_consumed -= SAFE_DIVIDE(pool.adjust_mana((attuned_cost)), mult) + if (available_pools.Find(pool) == available_pools.len && mana_consumed <= -0.05) // if we're at the end of the list and mana_consumed is not 0 or near 0 (floating points grrr) + stack_trace("cost: [mana_consumed] was not 0 after drain_mana on [src]! This could've been an infinite loop!") + mana_consumed = 0 // lets terminate the loop to be safe /// Should be the raw conditional we use for determining if the thing that "uses mana" can actually /// activate the behavior that "uses mana". -/datum/component/uses_mana/proc/can_activate(atom/caster, ...) - var/list/sufficient_args = list(get_available_mana()) + args - return is_mana_sufficient(arglist(sufficient_args)) +/datum/component/uses_mana/proc/can_activate(...) + SIGNAL_HANDLER + return is_mana_sufficient(arglist(list(get_parent_user()) + args)) /// Wrapper for can_activate(). Should return a bitflag that will be passed down to the signal sender on failure. -/datum/component/uses_mana/proc/can_activate_check(give_feedback = TRUE, atom/caster, ...) - var/list/argss = args.Copy(2) - var/can_activate = can_activate(arglist(argss)) //doesnt return this + can_activate_check_... because returning TRUE/FALSE can gave bitflag implications +/datum/component/uses_mana/proc/can_activate_with_feedback(...) + SIGNAL_HANDLER + var/can_activate + var/list/argss = args.Copy(1) + can_activate = can_activate(arglist(argss)) //doesnt return this + can_activate_check_... because returning TRUE/FALSE can gave bitflag implications + if (!can_activate) - return can_activate_check_failure(arglist(args)) + var/datum/user = get_parent_user() + if (user) + if (ismob(user)) + var/mob/mob_user = user + mob_user.balloon_alert(mob_user, "insufficient mana!") + return can_activate_check_failure(arglist(args.Copy())) + return NONE /// What can_activate_check returns apon failing to activate. -/datum/component/uses_mana/proc/can_activate_check_failure(give_feedback, atom/caster, ...) - PROTECTED_PROC(TRUE) - if (give_feedback) - give_unable_to_activate_feedback(arglist(args.Copy(2))) - return FALSE - -/// If called, should give feedback to the user of the magic, telling them why it failed. -/datum/component/uses_mana/proc/give_unable_to_activate_feedback(atom/caster, ...) - PROTECTED_PROC(TRUE) - return +/datum/component/uses_mana/proc/can_activate_check_failure(...) + return activate_check_failure_callback?.Invoke(arglist(args)) /// Should react to a post-use signal given by the parent, and ideally subtract mana, or something. /datum/component/uses_mana/proc/react_to_successful_use(...) SIGNAL_HANDLER + + drain_mana(arglist(list(get_parent_user()) + args)) + return -/// The primary proc we will use for draining mana to simulate it being consumed to power our actions. -/datum/component/uses_mana/proc/drain_mana(list/datum/mana_pool/pools = get_available_mana(), cost, atom/caster, ...) - if(isnull(cost)) - cost = -1 * get_mana_required(arglist(args.Copy(3))) - - var/list/datum/attunement/our_attunements = get_attunement_dispositions() - for (var/datum/mana_pool/iterated_pool as anything in pools) - var/mult = iterated_pool.get_overall_attunement_mults(our_attunements, caster) - var/attuned_cost = cost * mult - testing("Draining [cost] mana, adjusted to [attuned_cost] by [mult] from [iterated_pool] ([REF(iterated_pool)]) by [caster].") - cost -= SAFE_DIVIDE(iterated_pool.adjust_mana((attuned_cost)), mult) - if (cost == 0) - break - if (cost != 0) - stack_trace("cost: [cost] was not 0 after react_to_successful_use on [src]") +/datum/component/uses_mana/proc/get_parent_user() + return get_user_callback?.Invoke() diff --git a/maplestation_modules/code/modules/magic/magic.dm b/maplestation_modules/code/modules/magic/magic.dm deleted file mode 100644 index 28f45e333199..000000000000 --- a/maplestation_modules/code/modules/magic/magic.dm +++ /dev/null @@ -1,43 +0,0 @@ -/// A datum that simply holds a mana pool, nothing more. Mostly unneccessary for now-but may become needed once mana pools are refactored into -/// gas-mixture like lists of datums. -/datum/mana_holder - var/datum/mana_pool/mana - -/datum/mana_holder/New() - . = ..() - - initialize_mana() - -/datum/mana_holder/Destroy(force, ...) - QDEL_NULL(mana) - - return ..() - -/// Adjust the amount of the mana we hold + their attunements. Will become necessary once mana pools are refactored into gas-mixture like lists of datums. -/datum/mana_holder/proc/adjust_mana(amount, list/incoming_attunements = GLOB.default_attunements) - return mana.adjust_mana(amount, incoming_attunements) - -/// Return the average attunements across all mana within us. -/datum/mana_holder/proc/get_attunements() - return mana.attunements - -/// Getter for our mana. -/datum/mana_holder/proc/get_stored_mana() - return mana - -/// Should only be called during init. Instantiates our mana. -/datum/mana_holder/proc/initialize_mana() - mana = new /datum/mana_pool(generate_initial_capacity(), get_initial_mana_amount(), get_initial_attunements()) - return mana - -/// Generates our initial attunement list. -/datum/mana_holder/proc/get_initial_attunements() - return GLOB.default_attunements.Copy() - -/// Generates the mana amount our pool will start with. -/datum/mana_holder/proc/get_initial_mana_amount() - return generate_initial_capacity() - -/// Generates the maximum mana our pool can hold. -/datum/mana_holder/proc/generate_initial_capacity() - return 0 diff --git a/maplestation_modules/code/modules/magic/magic_subsystem.dm b/maplestation_modules/code/modules/magic/magic_subsystem.dm deleted file mode 100644 index 4c918e9e58f7..000000000000 --- a/maplestation_modules/code/modules/magic/magic_subsystem.dm +++ /dev/null @@ -1,67 +0,0 @@ -PROCESSING_SUBSYSTEM_DEF(magic) - name = "Magic" - flags = SS_BACKGROUND|SS_POST_FIRE_TIMING - - wait = MAGIC_SUBSYSTEM_FIRE_RATE - priority = FIRE_PRIORITY_MAGIC - - /// The intrinsic, underlying lines of transient magic in the universe. - /// Only a list for future changes. V1 will not have deep leyline simulation, only global variables - var/list/datum/mana_holder/leyline/leylines = list() - -/datum/controller/subsystem/processing/magic/Initialize() - . = ..() - - var/amount_of_leylines = get_leyline_amount() - while (amount_of_leylines-- > 0) - new /datum/mana_holder/leyline() //generate initial leylines - - return SS_INIT_SUCCESS - -/// This proc only exists for if we decide to make leylines better simulated. -/datum/controller/subsystem/processing/magic/proc/get_leyline_amount() - return 1 //CHANGE THIS IN OTEHR VERSIONS TO BE BETTER - -/// Starts processing the leyline, and adds it to our list of leylines. -/datum/controller/subsystem/processing/magic/proc/start_processing_leyline(datum/mana_holder/leyline/leyline_to_process) - if (!istype(leyline_to_process)) - CRASH("[leyline_to_process], type of [leyline_to_process?.type] used as arg in start_processing_leyline!") - START_PROCESSING(src, leyline_to_process) - leylines += leyline_to_process - -/// Stops processing the leyline, and removes it from our list of leylines. -/datum/controller/subsystem/processing/magic/proc/stop_processing_leyline(datum/mana_holder/leyline/leyline_to_process) - if (!istype(leyline_to_process)) - CRASH("[leyline_to_process], type of [leyline_to_process?.type] used as arg in stop_processing_leyline!") - STOP_PROCESSING(src, leyline_to_process) - leylines -= leyline_to_process - -/// Returns a list of all our leylines' mana pools. -/datum/controller/subsystem/processing/magic/proc/get_all_leyline_mana() - var/list/datum/mana_pool/mana = list() - for (var/datum/mana_holder/leyline/processing_leyline as anything in leylines) - mana += processing_leyline.get_stored_mana() - - return mana - -/// Returns the raw amount of mana all our leylines have. -/datum/controller/subsystem/processing/magic/proc/get_all_leyline_raw_mana_amount() - var/list/datum/mana_pool/mana = get_all_leyline_mana() - var/amount = 0 - for (var/datum/mana_pool/group as anything in mana) - amount += group.amount - return amount - -/// Returns the attuned amount of mana for all our leylines, using attunements to generate attunement mults. -/datum/controller/subsystem/processing/magic/proc/get_all_leyline_attuned_mana_amount(list/datum/attunement/attunements, atom/caster) - var/list/datum/mana_pool/mana = get_all_leyline_mana() - var/amount = 0 - for (var/datum/mana_pool/group as anything in mana) - amount += group.get_attuned_amount(attunements, caster) - return amount - -/// Adjusts the mana of picked_leyline by amount, with incoming_attunements. -/datum/controller/subsystem/processing/magic/proc/adjust_stored_mana(datum/mana_holder/leyline/picked_leyline, amount, list/incoming_attunements) - if (isnull(incoming_attunements)) - incoming_attunements = picked_leyline.get_attunements() - picked_leyline.adjust_mana(amount, incoming_attunements) diff --git a/maplestation_modules/code/modules/magic/attunement.dm b/maplestation_modules/code/modules/magic/mana/attunements/attunement.dm similarity index 99% rename from maplestation_modules/code/modules/magic/attunement.dm rename to maplestation_modules/code/modules/magic/mana/attunements/attunement.dm index b92dc58e0814..eeaa2782ae03 100644 --- a/maplestation_modules/code/modules/magic/attunement.dm +++ b/maplestation_modules/code/modules/magic/mana/attunements/attunement.dm @@ -21,7 +21,7 @@ GLOBAL_LIST_INIT(default_attunements, create_default_attunement_list()) /// vice versa. /datum/attunement var/name = "Base attunement" - var/desc = "Some fucking dumbass forgot to set desc" + var/desc = "Some coder forgot to set desc" var/list/alignments = list() // no alignments by default diff --git a/maplestation_modules/code/modules/magic/mana/living_mana.dm b/maplestation_modules/code/modules/magic/mana/living_mana.dm new file mode 100644 index 000000000000..5169b609d71b --- /dev/null +++ b/maplestation_modules/code/modules/magic/mana/living_mana.dm @@ -0,0 +1,57 @@ +/mob/living/carbon + has_initial_mana_pool = TRUE + +/mob/living/carbon/get_initial_mana_pool_type() + return /datum/mana_pool/mob/living/carbon + +/mob/living/carbon/human/dummy + has_initial_mana_pool = FALSE + +/datum/mana_pool/mob/living/carbon + maximum_mana_capacity = CARBON_BASE_MANA_CAPACITY + + exponential_decay_divisor = BASE_CARBON_MANA_EXPONENTIAL_DIVISOR + +// carbons have softcap mults, this adds it to the pool. +/mob/living/carbon/initialize_mana_pool() + var/datum/mana_pool/mob/living/carbon/our_pool = ..() + + our_pool.softcap *= get_mana_softcap_mult(mana_pool) + + return our_pool + +/mob/living/carbon/proc/get_mana_softcap_mult(datum/mana_pool/pool) + SHOULD_BE_PURE(TRUE) + + var/mult = 1 + + if (mob_biotypes & MOB_ROBOTIC) + mult *= ROBOTIC_MANA_SOFTCAP_MULT + + return mult + +/mob/living/carbon/proc/get_max_mana_capacity_mult() + SHOULD_BE_PURE(TRUE) + + var/mult = 1 + + return mult + +/mob/living/carbon/proc/safe_adjust_personal_mana(amount_to_adjust) +// proc for adjusting mana without going over the softcap + if(mana_pool) // playing it safe, does nothing if you have no mana pool + if(amount_to_adjust < 0) // if the amount is negative + if(mana_pool.amount > -amount_to_adjust) // not risking negatives + mana_pool.adjust_mana(amount_to_adjust) + else + if(mana_pool.amount < mana_pool.softcap) + mana_pool.adjust_mana(amount_to_adjust) + +/mob/living/carbon/proc/adjust_personal_mana(amount_to_adjust) +// proc for adjusting mana that CAN go over the softcap + if(mana_pool) // playing it safe, does nothing if you have no mana pool + if(amount_to_adjust < 0) // if the amount is negative - you *should* use the above if you know its gonna go into the negatives, though. + if(mana_pool.amount > -amount_to_adjust) // not risking negatives + mana_pool.adjust_mana(amount_to_adjust) + else + mana_pool.adjust_mana(amount_to_adjust) diff --git a/maplestation_modules/code/modules/magic/mana/mana.dm b/maplestation_modules/code/modules/magic/mana/mana.dm index 6d05e1d1398e..5c8e75d68944 100644 --- a/maplestation_modules/code/modules/magic/mana/mana.dm +++ b/maplestation_modules/code/modules/magic/mana/mana.dm @@ -1,71 +1,66 @@ -/* DESIGN NOTES -* This exists because mana will eventually have attunemenents and alignments that will incresae their efficiency in being used -* on spells/by people with corresponding attunements/alignments, vice versa for conflicting. -* -*/ - -/// An abstract representation of collections of mana, as it's impossible to represent each individual mana unit -/datum/mana_pool - var/amount = 0 - /// As attunements on mana is actually a tangible thing, and not just a preference, mana attunements should never go below zero. - var/list/datum/attunement/attunements - var/maximum_mana_capacity - -/datum/mana_pool/New(maximum_mana_capacity = INFINITY, amount = maximum_mana_capacity, attunements = GLOB.default_attunements.Copy()) - . = ..() - - src.maximum_mana_capacity = maximum_mana_capacity - src.amount = amount - src.attunements = attunements - -/datum/mana_pool/Destroy(force, ...) - attunements = null - - return ..() - -#define MANA_POOL_REPLACE_ALL_ATTUNEMENTS (1<<2) -// TODO BIG FUCKING WARNING THIS EQUATION DOSENT WORK AT ALL -// Should be fine as long as nothing actually has any attunements -/// The proc used to modify the mana composition of a mana pool. Should modify attunements in proportion to the ratio -/// between the current amount of mana we have and the mana coming in/being removed, as well as the attunements. -/// Mana pools in general will eventually be refactored to be lists of individual mana pieces with unchanging attunements, -/// so this is not permanent. -/// Returns how much of "amount" was used. -/datum/mana_pool/proc/adjust_mana(amount, list/incoming_attunements = GLOB.default_attunements) - - /*if (src.amount == 0) - CRASH("src.amount was ZERO in [src]'s adjust_quanity") //why would this happen - */ - if (amount == 0) - return amount - - /*var/ratio - if (src.amount == 0) - ratio = MANA_POOL_REPLACE_ALL_ATTUNEMENTS - else - ratio = amount/src.amount*/ - - /*for (var/iterated_attunement as anything in incoming_attunements) - // equation formed in desmos, dosent work - attunements[iterated_attunement] += (((incoming_attunements[iterated_attunement]) - attunements[iterated_attunement]) * (ratio/2)) */ - - var/result = clamp(src.amount + amount, 0, maximum_mana_capacity) - . = result - src.amount // Return the amount that was used - //if (abs(.) > abs(amount)) - // Currently, due to floating point imprecision, leyline recharges always cause this to fire, but honestly its nothing horrible - // Ill fix it later(?) - //stack_trace("[.], amount used, has its absolute value more than [amount]'s during [src]'s adjust_mana") - src.amount = result - -#undef MANA_POOL_REPLACE_ALL_ATTUNEMENTS - -/// Returns an adjusted amount of "effective" mana, affected by the attunements. -/// Will always return a minimum of zero and a maximum of the total amount of mana we can give multiplied by the mults. -/datum/mana_pool/proc/get_attuned_amount(list/datum/attunement/incoming_attunements, atom/caster, amount_to_adjust = src.amount) - var/mult = get_overall_attunement_mults(incoming_attunements, caster) - - return clamp(SAFE_DIVIDE(amount_to_adjust, mult), 0, amount*mult) - -/// Returns the combined attunement mults of all entries in the argument. -/datum/mana_pool/proc/get_overall_attunement_mults(list/attunements, atom/caster) - return get_total_attunement_mult(src.attunements, attunements, caster) +/atom/movable + var/datum/mana_pool/mana_pool + var/has_initial_mana_pool = FALSE // not using flags since this is all modular and flags can be overridden by tg + + var/mana_overload_threshold = BASE_MANA_OVERLOAD_THRESHOLD + var/mana_overload_coefficient = BASE_MANA_OVERLOAD_COEFFICIENT + + var/mana_overloaded = FALSE + +// creates the mana pool for use. +// mostly called on atom_init. +/atom/movable/proc/initialize_mana_pool() + RETURN_TYPE(/datum/mana_pool) + + var/datum/mana_pool/type = get_initial_mana_pool_type() + + var/datum/mana_pool/pool = new type(parent = src) + return pool + +// base proc to return what kind of mana pool this atom will have. +// meant to be overridden with whatever mana_pool subtype you want. +/atom/movable/proc/get_initial_mana_pool_type() + RETURN_TYPE(/datum/mana_pool) + + return /datum/mana_pool + +/// New_pool is nullable +/// standard setter proc. Input the pool you want to add to the atom, and it'll toss out the old +/atom/movable/proc/set_mana_pool(datum/mana_pool/new_pool) + if (!can_have_mana_pool(new_pool)) + return FALSE + + SEND_SIGNAL(src, COMSIG_ATOM_MANA_POOL_CHANGED, mana_pool, new_pool) + + if (mana_pool) + // do stuff like replacement + QDEL_NULL(mana_pool) + set_mana_pool(null) // todo: see if this is necessary + mana_pool = new_pool + + if (isnull(mana_pool)) + if (mana_overloaded) + stop_mana_overload() + +// just retrieves mana_pool and initializes it if it doesn't have one for some reason (and is allowed to have one) +/atom/movable/proc/get_mana_pool_lazy() + + if (!can_have_mana_pool()) + return null + + initialize_mana_pool_if_possible() + + return mana_pool + +// initialize the pool if it doesn't have one, and is allowed to +/atom/movable/proc/initialize_mana_pool_if_possible() + if (isnull(mana_pool) && can_have_mana_pool()) + mana_pool = initialize_mana_pool() + +// arg nullable +// override this with whatever if you want special functionality if something wants to check if it can have a mana_pool +/atom/movable/proc/can_have_mana_pool(datum/mana_pool/new_pool) + SHOULD_CALL_PARENT(TRUE) + SHOULD_BE_PURE(TRUE) + + return TRUE diff --git a/maplestation_modules/code/modules/magic/mana/mana_chemicals.dm b/maplestation_modules/code/modules/magic/mana/mana_chemicals.dm new file mode 100644 index 000000000000..82aec03a21e0 --- /dev/null +++ b/maplestation_modules/code/modules/magic/mana/mana_chemicals.dm @@ -0,0 +1,76 @@ +// this is the containment area for magic related chems, or changes to chems to add interactivity +/datum/reagent/consumable/ethanol/wizz_fizz/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) + . = ..() + //Its a magic drink. it regens mana, if mildly + if(drinker?.mana_pool) + drinker.safe_adjust_personal_mana(1.5) + +/datum/reagent/consumable/ethanol/pod_tesla/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) + . = ..() + //electrical drink, very mild regen, will do better if electrically attuened (when implemented) + if(drinker?.mana_pool) + drinker.safe_adjust_personal_mana(1) +/* TODOS: +* Telepole & Pod Tesla regen mana if electrically attuned/or it has an electric attunement (me when the charge build) +* Sea Breeze (lizard drink) & Tropical Storm (mothic drink) mildly regens mana if water attuned +*/ +/datum/reagent/medicine/quintessence + name = "Quintessence" + description = "Quintessence is mana that has been fixed into a digestible, chemical form." + taste_description = "a unique feeling of raw power" + ph = 5 + color = "#a1fcdc" + var/mana_adjust = 1.5 + +/datum/reagent/medicine/quintessence/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) + . = ..() + //magic chem. regens mana, shouldn't be obtained by reaction, instead from sources like botany. + if(drinker?.mana_pool) + drinker.safe_adjust_personal_mana(mana_adjust) + +/datum/reagent/medicine/quintessence/crystalized + name = "Crystallized Quintessence" + description = "A rarer form of Quintessence, fixed into a liquid crystal form. Increased effects." + taste_description = "a spark of life" + ph = 4 + color = "#9effdd" + mana_adjust = 3 + +/datum/reagent/medicine/quintessence/misty + name = "Misty Quintessence" + description = "A more common, improperly fixed, version of Quintessence. Reduced effects." + taste_description = "potential yet unrealized" + ph = 6 + color = "#b3ead7" + mana_adjust = 0.5 + +/datum/reagent/toxin/agnosticine + name = "Agnosticine" + description = "Agnosticine is Quintessence's natural opposite. It saps mana from those who consume it." + taste_description = "dreams deferred" + ph = 9 + toxpwr = 0 + color = "#5e0323" // the inverse of quintessence + var/mana_adjust = -1.5 + +/datum/reagent/toxin/agnosticine/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) + . = ..() + // also a magic chem. drains mana, shouldn't be obtained by reaction, instead from sources like botany. + if(drinker?.mana_pool) + drinker.safe_adjust_personal_mana(mana_adjust) + +/datum/reagent/toxin/agnosticine/fading + name = "Fading Agnosticine" + description = "Agnosticine congealed to a point it gains a near translucent appearence. Increased effects." + taste_description = "a hollow feeling in your heart" + ph = 10 + color = "#610022" + mana_adjust = -3 + +/datum/reagent/toxin/agnosticine/foggy + name = "Foggy Agnosticine" + description = "An impure, unrealized version of Agnosticine. Reduced effects." + taste_description = "fading wishes" + ph = 8 + color = "#4c1528" + mana_adjust = -0.5 diff --git a/maplestation_modules/code/modules/magic/mana/mana_overload.dm b/maplestation_modules/code/modules/magic/mana/mana_overload.dm new file mode 100644 index 000000000000..77dff34fc21a --- /dev/null +++ b/maplestation_modules/code/modules/magic/mana/mana_overload.dm @@ -0,0 +1,35 @@ +/mob/living/carbon/Initialize(mapload) + if (mob_biotypes & MOB_ROBOTIC) + mana_overload_coefficient *= ROBOTIC_MANA_OVERLOAD_COEFFICIENT_MULT + mana_overload_threshold *= ROBOTIC_MANA_OVERLOAD_THRESHOLD_MULT + + return ..() + +/atom/movable/proc/process_mana_overload(effect_mult, seconds_per_tick) + mana_overloaded = TRUE + +/mob/process_mana_overload(effect_mult, seconds_per_tick) + if (!mana_overloaded) + balloon_alert(src, "mana overload!") + to_chat(src, span_warning("You start feeling fuzzy and tingly all around...")) + + return ..() + +/mob/living/carbon/process_mana_overload(effect_mult, seconds_per_tick) + . = ..() + + var/adjusted_mult = effect_mult * seconds_per_tick + + adjust_disgust(adjusted_mult) + + if (effect_mult > MANA_OVERLOAD_DAMAGE_THRESHOLD) + apply_damage(MANA_OVERLOAD_BASE_DAMAGE * adjusted_mult, damagetype = BRUTE, forced = TRUE, spread_damage = TRUE) + +/atom/movable/proc/stop_mana_overload() + mana_overloaded = FALSE + +/mob/stop_mana_overload() + balloon_alert(src, "mana overload end") + to_chat(src, span_notice("You feel your body returning to normal.")) + + return ..() diff --git a/maplestation_modules/code/modules/magic/mana/mana_pool.dm b/maplestation_modules/code/modules/magic/mana/mana_pool.dm new file mode 100644 index 000000000000..138443c4e653 --- /dev/null +++ b/maplestation_modules/code/modules/magic/mana/mana_pool.dm @@ -0,0 +1,356 @@ +#define MANA_POOL_REPLACE_ALL_ATTUNEMENTS (1<<2) + +/* DESIGN NOTES +* This exists because mana will eventually have attunemenents and alignments that will incresae their efficiency in being used +* on spells/by people with corresponding attunements/alignments, vice versa for conflicting. +* +*/ + +/// An abstract representation of collections of mana, as it's impossible to represent each individual mana unit +/datum/mana_pool + var/atom/movable/parent = null + + // As attunements on mana is actually a tangible thing, and not just a preference, mana attunements should never go below zero. + /// A abstract representation of the attunements of [amount]. This is just an abstraction of the overall bias of all stored mana - in reality, every Vol has its own attunement. + var/list/datum/attunement/attunements + + // In vols + /// The absolute maximum [amount] we can hold. Under no circumstances should [amount] ever exceed this value. + var/maximum_mana_capacity = BASE_MANA_CAPACITY + /// The abstract representation of how many "Vols" this mana pool currently contains. + /// Capped at [maximum_mana_capacity], begins decaying exponentially when above [softcap]. + var/amount = 100 // placeholder. This should be replaced during process. + /// The threshold at which mana begins decaying exponentially. + // TODO: convert to some kind of list for multiple softcaps? + var/softcap = BASE_MANA_SOFTCAP + /// The divisor used in exponential decay when [amount] surpasses [softcap]. Lower = A steeper decay curve. + var/exponential_decay_divisor = BASE_MANA_EXPONENTIAL_DIVISOR + + /// The maximum mana we can transfer per second. [donation_budget_per_tick] is set to this, times seconds_per_tick, every process tick. + var/max_donation_rate_per_second = BASE_MANA_DONATION_RATE + /// The maximum mana we can transfer for this tick. Is used to cap our mana output per tick. Calculated with [max_donation_rate_per_second] * seconds_per_tick. + VAR_PROTECTED/donation_budget_this_tick = 149 // same with amount. this gets replaced on process. + + /// List of (mana_pool -> transfer rate) + var/list/datum/mana_pool/transfer_rates = list() + /// List of (mana_pool -> max mana we will give) + var/list/datum/mana_pool/transfer_caps = list() + /// List of (mana_pool -> mana_pool_process_bitflags). Holds pools we are transferring to. + var/list/datum/mana_pool/transferring_to = list() + /// List of mana_pools transferring to us + var/list/datum/mana_pool/transferring_from = list() + /// The priority method we will use to transfer mana to all that we are trying to transfer into. Uses defines from magic_charge_bitflags.dm + var/transfer_method = MANA_SEQUENTIAL + + /// If true, if no cap is specified, we only go up to the softcap of the target when transferring + var/transfer_default_softcap = TRUE + + /// The natural regen rate, detached from transferrals. Mana generated via this comes from nothing. Has nothing to do with the ethereal species. + var/ethereal_recharge_rate = 0 + /// If we have an ethereal recharge rate,i ths is the attunement set that will be given to the generated mana. + var/list/datum/attunement/attunements_to_generate = list() + + /// The mana pool types we will try to discharge excess mana (from exponential decay) into. Uses defines from magic_charge_bitflags.dm. + var/discharge_destinations = MANA_ALL_LEYLINES + /// The priority method we will use to transfer mana to [discharge_destination] mana pools. Any given type does not guarantee all destinations will receive mana if they are full. + /// Uses defines from magic_charge_bitflags.dm. + var/discharge_method = MANA_SEQUENTIAL + + /// The intrinsic sources of mana we will constantly try to draw from. Uses defines from magic_charge_bitflags.dm. + var/intrinsic_recharge_sources = MANA_ALL_LEYLINES + +/datum/mana_pool/New(atom/parent = null) + . = ..() + donation_budget_this_tick = max_donation_rate_per_second + set_parent(parent) + + update_intrinsic_recharge() + + START_PROCESSING(SSmagic, src) + +/datum/mana_pool/Destroy(force, ...) + attunements = null + attunements_to_generate = null + + QDEL_NULL(transfer_rates) + QDEL_NULL(transfer_caps) + QDEL_NULL(transferring_to) + QDEL_NULL(transferring_from) // we already have a signal registered, so if we qdel we stop transfers + + STOP_PROCESSING(SSmagic, src) + + if (parent.mana_pool != src) + stack_trace("[parent].mana_pool was not [src] when src had parent registered!") + else + parent.mana_pool = null + parent = null + + return ..() + +/datum/mana_pool/proc/set_parent(atom/parent) + src.parent = parent + if(ismob(parent)) + RegisterSignal(parent, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(mana_status_report)) + +/datum/mana_pool/proc/mana_status_report(datum/source, list/status_tab) + SIGNAL_HANDLER + + var/general_amount_estimate + var/sc_very_low = (softcap * 0.1) + var/sc_low = (softcap * 0.3) + var/sc_medium = (softcap * 0.6) + var/sc_high = (softcap * 0.8) + + //determines what the status displays, it'll be a generic/non-obvious value as a design choice + if(amount) + if (amount < sc_very_low) + general_amount_estimate = "VERY LOW" + else if (amount > sc_very_low && amount < sc_low) + general_amount_estimate = "LOW" + else if (amount > sc_low && amount < sc_medium) + general_amount_estimate = "MEDIUM" + else if (amount > sc_medium && amount < sc_high) + general_amount_estimate = "HIGH" + else if (amount > sc_high && amount <= softcap) + general_amount_estimate = "VERY HIGH" + else if (amount > softcap) + general_amount_estimate = "OVERLOADED" + else + general_amount_estimate = "ERROR" + + status_tab += "Mana Count: [general_amount_estimate]" + +/datum/mana_pool/proc/generate_initial_attunements() + RETURN_TYPE(/list/datum/attunement) + + return GLOB.default_attunements.Copy() + +// order of operations is as follows: +// 1. we recharge +// 2. we transfer mana +// 3. we discharge excess mana +/datum/mana_pool/process(seconds_per_tick) + + donation_budget_this_tick = (max_donation_rate_per_second * seconds_per_tick) //TODO: stop float imprecision + + if (ethereal_recharge_rate != 0) + adjust_mana(ethereal_recharge_rate * seconds_per_tick, attunements_to_generate) + if (length(transferring_to) > 0) + switch (transfer_method) + if (MANA_SEQUENTIAL) + for (var/datum/mana_pool/iterated_pool as anything in transferring_to) + if (amount <= 0 || donation_budget_this_tick <= 0) + break + if (transferring_to[iterated_pool] & MANA_POOL_SKIP_NEXT_TRANSFER) + transferring_to[iterated_pool] &= ~MANA_POOL_SKIP_NEXT_TRANSFER + continue + + transfer_mana_to(iterated_pool, seconds_per_tick) + + if (MANA_DISPERSE_EVENLY) + var/mana_to_disperse = (SAFE_DIVIDE(donation_budget_this_tick, length(transferring_to))) + + for (var/datum/mana_pool/iterated_pool as anything in transferring_to) + if (transferring_to[iterated_pool] & MANA_POOL_SKIP_NEXT_TRANSFER) + transferring_to[iterated_pool] &= ~MANA_POOL_SKIP_NEXT_TRANSFER + continue + + transfer_specific_mana(iterated_pool, mana_to_disperse) + // ... + + if (parent) + if (amount > parent.mana_overload_threshold) + var/effect_mult = (((amount / parent.mana_overload_threshold) - 1) * parent.mana_overload_coefficient) + parent.process_mana_overload(effect_mult, seconds_per_tick) + else if (parent.mana_overloaded) + parent.stop_mana_overload() + + if (amount > softcap) // why was this amount < softcap + // exponential decay + // exponentially decays amount when amount surpasses softcap, with [exponential_decay_divisor] being the (inverse) decay factor + // can only decay however much amount we are over softcap + // imperfect as of now (need to test) + var/exponential_decay = (max(-((((NUM_E**((amount - softcap)/exponential_decay_divisor)) + 1)) * seconds_per_tick), (softcap - amount))) + // in desmos: f\left(x\right)=\max\left(\left(\left(-\left(e\right)^{\left(\frac{\left(x-t\right)}{c}\right)}\right)+1\right),\ \left(t-x\right)\right)\ \left\{x\ge t\right\} + // t=50 + // c=150 + if (discharge_destinations) + var/list/datum/mana_pool/pools_to_discharge_into = list() + if (discharge_destinations & MANA_ALL_LEYLINES) + pools_to_discharge_into += get_accessable_leylines() + + switch (discharge_method) + if (MANA_DISPERSE_EVENLY) + var/mana_to_disperse = (exponential_decay / length(pools_to_discharge_into)) + + for (var/datum/mana_pool/iterated_pool as anything in pools_to_discharge_into) + transfer_specific_mana(iterated_pool, -mana_to_disperse, FALSE) + + if (MANA_SEQUENTIAL) + for (var/datum/mana_pool/iterated_pool as anything in pools_to_discharge_into) + exponential_decay -= transfer_specific_mana(iterated_pool, -exponential_decay, FALSE) + if (exponential_decay <= 0) + break + + adjust_mana(exponential_decay) //just to be safe, in case we have any left over or didnt have a discharge destination + +/// Perform a "natural" transfer where we use the default transfer rate, capped by the usual math +/datum/mana_pool/proc/transfer_mana_to(datum/mana_pool/target_pool, seconds_per_tick = 1) + return transfer_specific_mana(target_pool, get_transfer_rate_for(target_pool) * seconds_per_tick) + +/// Returns the amount of mana we want to give in a given tick +/datum/mana_pool/proc/get_transfer_rate_for(datum/mana_pool/target_pool) + var/cached_rate = transfer_rates[target_pool] + return min((cached_rate ? min(cached_rate, donation_budget_this_tick) : donation_budget_this_tick), get_maximum_transfer_for(target_pool)) + +/datum/mana_pool/proc/get_maximum_transfer_for(datum/mana_pool/target_pool) + var/cached_cap = transfer_caps[target_pool] + return (cached_cap || (transfer_default_softcap ? target_pool.softcap : target_pool.maximum_mana_capacity)) + +/datum/mana_pool/proc/transfer_specific_mana(datum/mana_pool/other_pool, amount_to_transfer, decrement_budget = TRUE) + // ensure we dont give more than we hold and dont give more than they CAN hold + var/adjusted_amount = min(min(amount_to_transfer, maximum_mana_capacity), (other_pool.maximum_mana_capacity - other_pool.amount)) + // ^^^^ TODO THIS ISNT THA TGOOD I DONT LIKE IT we should instead have remainders returned on adjust mana and plug it into the OTHER adjust mana + + if (decrement_budget) + donation_budget_this_tick -= amount_to_transfer + + adjust_mana(-adjusted_amount) + return other_pool.adjust_mana(adjusted_amount, attunements) + +/datum/mana_pool/proc/start_transfer(datum/mana_pool/target_pool, force_process = FALSE) + + if (target_pool == src) + stack_trace("start_transfer called where target_pool was src!") + return MANA_POOL_CANNOT_TRANSFER + + if (!can_transfer(target_pool)) + return MANA_POOL_CANNOT_TRANSFER + + if (target_pool in transferring_to) + return MANA_POOL_ALREADY_TRANSFERRING + + target_pool.incoming_transfer_start(src) + + RegisterSignal(target_pool, COMSIG_QDELETING, PROC_REF(stop_transfer)) + + if (force_process) + transferring_to[target_pool] |= MANA_POOL_SKIP_NEXT_TRANSFER + transfer_mana_to(target_pool) // you can potentially get all you need instantly + + return MANA_POOL_TRANSFER_START + +/datum/mana_pool/proc/stop_transfer(datum/mana_pool/target_pool, forced = FALSE) + SIGNAL_HANDLER + + if (!forced && !QDELETED(target_pool) && (transferring_to[target_pool] & MANA_POOL_SKIP_NEXT_TRANSFER)) + return MANA_POOL_TRANSFER_SKIP_ACTIVE // nope! + + transferring_to -= target_pool + target_pool.incoming_transfer_end(src) + + UnregisterSignal(target_pool, COMSIG_QDELETING) + + return MANA_POOL_TRANSFER_STOP + +/datum/mana_pool/proc/incoming_transfer_start(datum/mana_pool/donator) + transferring_from += donator + +/datum/mana_pool/proc/incoming_transfer_end(datum/mana_pool/donator) + transferring_from -= donator + +// TODO BIG FUCKING WARNING THIS EQUATION DOSENT WORK AT ALL +// Should be fine as long as nothing actually has any attunements +/// The proc used to modify the mana composition of a mana pool. Should modify attunements in proportion to the ratio +/// between the current amount of mana we have and the mana coming in/being removed, as well as the attunements. +/// Mana pools in general will eventually be refactored to be lists of individual mana pieces with unchanging attunements, +/// so this is not permanent. +/// Returns how much of "amount" was used. +/datum/mana_pool/proc/adjust_mana(amount, list/incoming_attunements) + if (amount == 0) + return amount + + var/result = clamp(src.amount + amount, 0, maximum_mana_capacity) + . = result - src.amount // Return the amount that was used + src.amount = result + +/// Returns an adjusted amount of "effective" mana, affected by the attunements. +/// Will always return a minimum of zero and a maximum of the total amount of mana we can give multiplied by the mults. +/datum/mana_pool/proc/get_attuned_amount(list/datum/attunement/incoming_attunements, atom/caster, amount_to_adjust = src.amount) + var/mult = get_overall_attunement_mults(incoming_attunements, caster) + + return clamp(SAFE_DIVIDE(amount_to_adjust, mult), 0, amount*mult) + +/// Returns the combined attunement mults of all entries in the argument. +/datum/mana_pool/proc/get_overall_attunement_mults(list/attunements, atom/caster) + return get_total_attunement_mult(src.attunements, attunements, caster) + +/datum/mana_pool/proc/can_transfer(datum/mana_pool/target_pool) + SHOULD_BE_PURE(TRUE) + + return TRUE + +/datum/mana_pool/proc/set_intrinsic_recharge(new_bitflags) + var/old_flags = intrinsic_recharge_sources + intrinsic_recharge_sources |= new_bitflags + update_intrinsic_recharge(old_flags) + +/datum/mana_pool/proc/update_intrinsic_recharge(previous_recharge_sources = NONE) + if (intrinsic_recharge_sources & MANA_ALL_LEYLINES) + for (var/datum/mana_pool/leyline/entry as anything in (get_accessable_leylines() - src)) + + if (entry.start_transfer(src) & MANA_POOL_ALREADY_TRANSFERRING) + continue + + transferring_from[entry] |= MANA_POOL_INTRINSIC + + else if (previous_recharge_sources & MANA_ALL_LEYLINES) + for (var/datum/mana_pool/leyline/entry in transferring_from) + + if (!(transferring_from[entry] & MANA_POOL_INTRINSIC)) + continue + + entry.stop_transfer(src) + + SEND_SIGNAL(src, COMSIG_MANA_POOL_INTRINSIC_RECHARGE_UPDATE, previous_recharge_sources) + +/datum/mana_pool/proc/set_natural_recharge(new_value) + ethereal_recharge_rate = new_value + if ((ethereal_recharge_rate > 0) && isnull(attunements_to_generate)) + attunements_to_generate = get_default_attunements_to_generate() + +/datum/mana_pool/proc/get_default_attunements_to_generate() + RETURN_TYPE(/list/datum/attunement) + + return GLOB.default_attunements.Copy() + +/datum/mana_pool/proc/set_max_mana(new_max, change_amount = FALSE, change_softcap = TRUE) + var/percent = get_percent_to_max() //originally this was a duplicate redefinition- see change_amount + var/softcap_percent = get_percent_of_softcap_to_max() + + if (change_softcap) + softcap_percent = get_percent_of_softcap_to_max() // originally softcap_percent was defined here + softcap = new_max * (softcap_percent / 100) + + if (change_amount) + percent = get_percent_to_max() // this used to be var/percent. why? + amount = new_max * (percent / 100) + + maximum_mana_capacity = new_max + +/datum/mana_pool/proc/get_percent_to_max() + SHOULD_BE_PURE(TRUE) + + return (amount / maximum_mana_capacity) * 100 + +/datum/mana_pool/proc/get_percent_to_softcap() + SHOULD_BE_PURE(TRUE) + + return (amount / softcap) * 100 + +/datum/mana_pool/proc/get_percent_of_softcap_to_max() + SHOULD_BE_PURE(TRUE) + + return (softcap / maximum_mana_capacity) * 100 + +#undef MANA_POOL_REPLACE_ALL_ATTUNEMENTS diff --git a/maplestation_modules/code/modules/magic/mana/misc_mana.dm b/maplestation_modules/code/modules/magic/mana/misc_mana.dm deleted file mode 100644 index 6483bb1eb33f..000000000000 --- a/maplestation_modules/code/modules/magic/mana/misc_mana.dm +++ /dev/null @@ -1,19 +0,0 @@ -/// Should return a list of all mana pools that this datum can access at the given moment. Defaults to returning leylines. -/datum/proc/get_available_mana(list/datum/attunement/attunements = GLOB.default_attunements) - RETURN_TYPE(/list/datum/mana_pool) - return SSmagic.get_all_leyline_mana() - -/// If this mob is casting/using something that costs mana, it should always multiply the cost against this. -/mob/proc/get_casting_cost_mult() - return BASE_STORY_MAGIC_CAST_COST_MULT - -/mob/living/carbon/get_casting_cost_mult() - . = ..() - for(var/obj/item/held_item in held_items) - if (held_item.item_flags & (ABSTRACT|HAND_ITEM)) - continue - // valid catalyst, apply no multiplier - return - - // no catalyst, it costs more. - . *= NO_CATALYST_COST_MULT diff --git a/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities.dm b/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities.dm deleted file mode 100644 index da856f992c8b..000000000000 --- a/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities.dm +++ /dev/null @@ -1,50 +0,0 @@ -GLOBAL_LIST_INIT(leyline_intensities, list( - /datum/leyline_intensity/average = 500, - /datum/leyline_intensity/above_average = 100, - /datum/leyline_intensity/high = 2, - /datum/leyline_intensity/extreme = 1 -)) - -// ^ Only pass integers in since its used for pickweight - -/// "Intensities" that will be applied to leylines. Should influence the average capacity/recharge rate/whatever of the leyline. -/datum/leyline_intensity - var/overall_mult - var/name = "we fucked up" - -/datum/leyline_intensity/none - overall_mult = 0 - name = "None" - -/datum/leyline_intensity/minimal - overall_mult = 0.05 - name = "Minimal" - -/datum/leyline_intensity/extremely_low - overall_mult = 0.1 - name = "Extremely Low" - -/datum/leyline_intensity/low - overall_mult = 0.5 - name = "Low" - -/datum/leyline_intensity/below_average - overall_mult = 0.7 - name = "Below average" - -/datum/leyline_intensity/average - overall_mult = 1 - name = "Average" - -/datum/leyline_intensity/above_average - overall_mult = 1.3 - name = "Above average" - -/datum/leyline_intensity/high - overall_mult = 2 - name = "High" - -/datum/leyline_intensity/extreme - overall_mult = 5 - name = "Extreme" - diff --git a/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_attunements.dm b/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_attunements.dm new file mode 100644 index 000000000000..69ec54c7517f --- /dev/null +++ b/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_attunements.dm @@ -0,0 +1,30 @@ +// works differently than others - uses prob(), so treat this stuff as percents +GLOBAL_LIST_INIT(leyline_attunement_themes, list( + /datum/leyline_variable/attunement_theme/fire_minor = 0.5, +)) + +/proc/get_random_attunement_themes() + RETURN_TYPE(/list/datum/leyline_variable/attunement_theme) + + var/list/datum/leyline_variable/attunement_theme/themes = list() + + for (var/datum/leyline_variable/attunement_theme/theme as anything in GLOB.leyline_attunement_themes) + if (prob(GLOB.leyline_attunement_themes[theme])) + themes += new theme + + return themes + +/// For each entry in GLOB.[leyline_attunement_themes], prob() is ran, and if it succeeds, it adjusts the leyline attunement by [adjust_attunements] +/datum/leyline_variable/attunement_theme + +/datum/leyline_variable/attunement_theme/proc/adjust_attunements(list/datum/attunement/attunements) + return + +/datum/leyline_variable/attunement_theme/fire_minor + name = "Smoldering" + +/datum/leyline_variable/attunement_theme/fire_minor/adjust_attunements(list/datum/attunement/attunements) + attunements[MAGIC_ELEMENT_FIRE] += 0.2 + + + diff --git a/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_intensities.dm b/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_intensities.dm new file mode 100644 index 000000000000..fe4e0b3b093c --- /dev/null +++ b/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_intensities.dm @@ -0,0 +1,54 @@ +GLOBAL_LIST_INIT(leyline_intensities, list( + /datum/leyline_variable/leyline_intensity/none = 200, + /datum/leyline_variable/leyline_intensity/minimal = 8000, + /datum/leyline_variable/leyline_intensity/extremely_low = 11000, + /datum/leyline_variable/leyline_intensity/low = 5000, + /datum/leyline_variable/leyline_intensity/below_average = 1000, + /datum/leyline_variable/leyline_intensity/average = 500, + /datum/leyline_variable/leyline_intensity/above_average = 100, + /datum/leyline_variable/leyline_intensity/high = 2, + /datum/leyline_variable/leyline_intensity/extreme = 1 +)) + +// ^ Only pass integers in since its used for pickweight + +/// "Intensities" that will be applied to leylines. Should influence the average capacity/recharge rate/whatever of the leyline. +/datum/leyline_variable/leyline_intensity + var/overall_mult + +/datum/leyline_variable/leyline_intensity/none + overall_mult = 0 + name = "None" + +/datum/leyline_variable/leyline_intensity/minimal + overall_mult = 0.05 + name = "Minimal" + +/datum/leyline_variable/leyline_intensity/extremely_low + overall_mult = 0.1 + name = "Extremely Low" + +/datum/leyline_variable/leyline_intensity/low + overall_mult = 0.5 + name = "Low" + +/datum/leyline_variable/leyline_intensity/below_average + overall_mult = 0.7 + name = "Below average" + +/datum/leyline_variable/leyline_intensity/average + overall_mult = 1 + name = "Average" + +/datum/leyline_variable/leyline_intensity/above_average + overall_mult = 1.3 + name = "Above average" + +/datum/leyline_variable/leyline_intensity/high + overall_mult = 2 + name = "High" + +/datum/leyline_variable/leyline_intensity/extreme + overall_mult = 5 + name = "Extreme" + diff --git a/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_variable.dm b/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_variable.dm new file mode 100644 index 000000000000..717cc2962601 --- /dev/null +++ b/maplestation_modules/code/modules/magic/mana/sources/leylines/leyline_intensities/leyline_variable.dm @@ -0,0 +1,4 @@ +// A leyline variable is a thing to be generated when a leyline is instantiated and affects many features of said leyline + +/datum/leyline_variable + var/name = "abstract, do not instantiate" diff --git a/maplestation_modules/code/modules/magic/mana/sources/leylines/leylines.dm b/maplestation_modules/code/modules/magic/mana/sources/leylines/leylines.dm index ffb42cfe5f24..9389c5143930 100644 --- a/maplestation_modules/code/modules/magic/mana/sources/leylines/leylines.dm +++ b/maplestation_modules/code/modules/magic/mana/sources/leylines/leylines.dm @@ -1,42 +1,99 @@ -// defines in _module_defines.dm +GLOBAL_LIST_EMPTY_TYPED(all_leylines, /datum/mana_pool/leyline) +// uses pickweight +/proc/generate_initial_leylines() + RETURN_TYPE(/list/datum/mana_pool/leyline) + + var/list/datum/mana_pool/leyline/leylines = list() + + var/leylines_to_generate = get_initial_leyline_amount() + while (leylines_to_generate-- > 0) + leylines += generate_leyline() + + return leylines + +/proc/get_initial_leyline_amount() + var/list/leyline_amount_list = list( + "1" = 5000, + "2" = 500, + "3" = 200, + "4" = 10 + ) + var/leyline_amount = text2num(pick_weight(leyline_amount_list)) + return leyline_amount + +/proc/generate_leyline() + RETURN_TYPE(/datum/mana_pool/leyline) + + return new /datum/mana_pool/leyline() /// The lines of latent energy that run under the universe. Available to all people in the game. Should be high capacity, but slow to recharge. -/datum/mana_holder/leyline - var/datum/leyline_intensity/intensity +/datum/mana_pool/leyline + var/datum/leyline_variable/leyline_intensity/intensity + var/list/datum/leyline_variable/attunement_theme/themes - var/recharge_rate + maximum_mana_capacity = LEYLINE_BASE_CAPACITY + + ethereal_recharge_rate = LEYLINE_BASE_RECHARGE + max_donation_rate_per_second = BASE_LEYLINE_DONATION_RATE + + transfer_method = MANA_DISPERSE_EVENLY + + discharge_destinations = NONE + +/datum/mana_pool/leyline/New() + GLOB.all_leylines += src -/datum/mana_holder/leyline/New() intensity = generate_initial_intensity() + themes = generate_initial_themes() + + for (var/datum/leyline_variable/attunement_theme/theme as anything in themes) + theme.adjust_attunements(attunements_to_generate) - . = ..() + maximum_mana_capacity *= (intensity.overall_mult) + softcap = maximum_mana_capacity - recharge_rate = generate_recharge_rate() + ethereal_recharge_rate *= (intensity.overall_mult) + max_donation_rate_per_second *= (intensity.overall_mult) - SSmagic.start_processing_leyline(src) + amount = maximum_mana_capacity -/datum/mana_holder/leyline/Destroy(force, ...) - SSmagic.stop_processing_leyline(src) + return ..() + +/datum/mana_pool/leyline/generate_initial_attunements() + return attunements_to_generate.Copy() + +/datum/mana_pool/leyline/proc/generate_initial_intensity() + var/picked_intensity = pick_weight(GLOB.leyline_intensities) + return new picked_intensity + +/datum/mana_pool/leyline/proc/generate_initial_themes() + var/list/datum/leyline_variable/attunement_theme/themes = get_random_attunement_themes() + + return themes +/datum/mana_pool/leyline/Destroy(force, ...) QDEL_NULL(intensity) + QDEL_LIST(themes) - return ..() + GLOB.all_leylines -= src -/datum/mana_holder/leyline/process(seconds_per_tick) - adjust_mana(recharge_rate * seconds_per_tick) //recharge + return ..() /// GETTERS / SETTERS -// TODO: CHANGE THIS LATER. I want this shit to be RANDOM. Im just bad at MATH. -/datum/mana_holder/leyline/generate_initial_capacity() - return LEYLINE_BASE_CAPACITY * intensity.overall_mult +/datum/proc/get_accessable_leylines() + RETURN_TYPE(/list/datum/mana_pool/leyline) -/datum/mana_holder/leyline/proc/generate_recharge_rate() - return LEYLINE_BASE_RECHARGE * intensity.overall_mult + var/list/datum/mana_pool/leyline/accessable_leylines = list() -/datum/mana_holder/leyline/proc/get_recharge_rate() - return recharge_rate + for (var/datum/mana_pool/leyline/entry as anything in GLOB.all_leylines) + if (entry.can_entity_access(src)) + accessable_leylines += entry -/datum/mana_holder/leyline/proc/generate_initial_intensity() - var/datum/leyline_intensity/picked_intensity = pick_weight(GLOB.leyline_intensities) - return new picked_intensity + return accessable_leylines + +/datum/proc/can_access_leyline(datum/mana_pool/leyline/leyline_in_question) + return TRUE + +/datum/mana_pool/leyline/proc/can_entity_access(datum/entity) + return entity.can_access_leyline(src) diff --git a/maplestation_modules/code/modules/magic/mana/sources/mana_batteries.dm b/maplestation_modules/code/modules/magic/mana/sources/mana_batteries.dm index 199d2ece5f94..47f9999e7708 100644 --- a/maplestation_modules/code/modules/magic/mana/sources/mana_batteries.dm +++ b/maplestation_modules/code/modules/magic/mana/sources/mana_batteries.dm @@ -1,4 +1,175 @@ -/*/obj/item/mana_crystal +/datum/mana_pool/mana_battery + amount = 0 + +/datum/mana_pool/mana_battery/can_transfer(datum/mana_pool/target_pool) + if (QDELETED(target_pool.parent)) + return FALSE + var/obj/item/mana_battery/battery = parent + + if (battery.loc == target_pool.parent.loc) + return TRUE + + if (get_dist(battery, target_pool.parent) > battery.max_allowed_transfer_distance) + return FALSE + return ..() + +/obj/item/mana_battery + name = "generic mana battery" + has_initial_mana_pool = TRUE + var/max_allowed_transfer_distance = MANA_BATTERY_MAX_TRANSFER_DISTANCE + +/obj/item/mana_battery/get_initial_mana_pool_type() + return /datum/mana_pool/mana_battery/mana_crystal + +// when we hit ourself with left click, we draw mana FROM the battery. +/obj/item/mana_battery/attack_self(mob/user, modifiers) + . = ..() + + if (.) + return TRUE + + if (!user.mana_pool) + balloon_alert(user, "no mana pool!") + return FALSE + + var/already_transferring = (user in mana_pool.transferring_to) + if (already_transferring) + balloon_alert(user, "canceled draw") + mana_pool.stop_transfer(user.mana_pool) + else + if(!user.is_holding(src)) + balloon_alert(user, "too far!") + return + var/mana_to_draw = tgui_input_number(user, "How much mana do you want to draw from the battery? Soft Cap (You will lose mana when above this!): [user.mana_pool.softcap]", "Draw Mana", max_value = mana_pool.maximum_mana_capacity) + if(!mana_to_draw || QDELETED(user) || QDELETED(src) || !user.is_holding(src)) + return + var/drawn_mana = mana_to_draw + balloon_alert(user, "drawing mana....") + mana_pool.transfer_specific_mana(user.mana_pool, drawn_mana, decrement_budget = TRUE) +// when we hit ourself with right click, however, we send mana TO the battery. +/obj/item/mana_battery/attack_self_secondary(mob/user, modifiers) + . = ..() + if (.) + return TRUE + + if (!user.mana_pool) + balloon_alert(user, "no mana pool!") + return FALSE + var/already_transferring = (user in mana_pool.transferring_to) + if (already_transferring) + balloon_alert(user, "canceled send") + user.mana_pool.stop_transfer(mana_pool) + else + if(!user.is_holding(src)) + balloon_alert(user, "too far!") + return + var/mana_to_send = tgui_input_number(user, "How much mana do you want to send to the battery? Max Capacity: [mana_pool.maximum_mana_capacity]", "Send Mana", max_value = mana_pool.maximum_mana_capacity) + if(!mana_to_send || QDELETED(user) || QDELETED(src) || !user.is_holding(src)) + return + var/sent_mana = mana_to_send + balloon_alert(user, "sending mana....") + user.mana_pool.transfer_specific_mana(mana_pool, sent_mana, decrement_budget = TRUE) +/obj/item/mana_battery/mana_crystal name = MAGIC_MATERIAL_NAME + " crystal" + desc = "Crystalized mana." //placeholder desc + icon = 'maplestation_modules/icons/obj/magic/crystals.dmi' //placeholder + +// Do not use, basetype +/datum/mana_pool/mana_battery/mana_crystal + + maximum_mana_capacity = MANA_CRYSTAL_BASE_MANA_CAPACITY + softcap = MANA_CRYSTAL_BASE_MANA_CAPACITY + + exponential_decay_divisor = MANA_CRYSTAL_BASE_DECAY_DIVISOR + + max_donation_rate_per_second = BASE_MANA_CRYSTAL_DONATION_RATE + +/datum/mana_pool/mana_battery/mana_crystal/New(atom/parent, amount) + . = ..() + softcap = maximum_mana_capacity + +/obj/item/mana_battery/mana_crystal/standard + name = "Stabilized Volite Crystal" + desc = "A stabilized Volite Crystal, one of the few objects capable of stably storing mana without binding." + icon_state = "standard" + +/obj/item/mana_battery/mana_crystal/standard/get_initial_mana_pool_type() + return /datum/mana_pool/mana_battery/mana_crystal/standard + +/datum/mana_pool/mana_battery/mana_crystal/standard // basically, just, bog standard, none of the variables need to be changed + +/obj/item/mana_battery/mana_crystal/small + name = "Small Volite Crystal" + desc = "A miniaturized Volite crystal, formed using the run-off of cutting larger ones. Able to hold mana still, although not as much as a proper formation." + icon_state = "small" + w_class = WEIGHT_CLASS_SMALL + +/obj/item/mana_battery/mana_crystal/small/get_initial_mana_pool_type() + return /datum/mana_pool/mana_battery/mana_crystal/small + +/obj/item/mana_battery/mana_crystal/cut + name = "Cut Volite Crystal" + desc = "A cut and shaped Volite Crystal, using a standardized square cut. It lacks power until it is slotted into a proper amulet." + icon_state = "cut" + +/obj/item/mana_battery/mana_crystal/cut/get_initial_mana_pool_type() + return /datum/mana_pool/mana_battery/mana_crystal/small + +/datum/mana_pool/mana_battery/mana_crystal/small/ + // half the size of the normal crystal + maximum_mana_capacity = (MANA_CRYSTAL_BASE_MANA_CAPACITY / 2) + softcap = (MANA_CRYSTAL_BASE_MANA_CAPACITY / 2) + +/datum/mana_pool/mana_star + // a special type of mana battery that regenerates passively- but cannot be given mana + maximum_mana_capacity = 400 // 400 by default + softcap = 400 + amount = 0 + ethereal_recharge_rate = 2 // forgot this was a thing LMFAO + +/obj/item/clothing/neck/mana_star + name = "Volite Amulet" + desc = "A cut volite crystal placed within a gilded amulet. It naturally draws and fixes mana for your use." + has_initial_mana_pool = TRUE + worn_icon = 'maplestation_modules/icons/mob/clothing/neck.dmi' + worn_icon_state = "volite_amulet" + icon = 'maplestation_modules/icons/obj/magic/crystals.dmi' + icon_state = "amulet" + +/obj/item/clothing/neck/mana_star/get_initial_mana_pool_type() + return /datum/mana_pool/mana_star + +/obj/item/clothing/neck/mana_star/attack_self(mob/user, modifiers) // you can only draw by default. + . = ..() + + if (.) + return TRUE + + if (!user.mana_pool) + balloon_alert(user, "no mana pool!") + return FALSE + + var/already_transferring = (user in mana_pool.transferring_to) + if (already_transferring) + balloon_alert(user, "canceled draw") + mana_pool.stop_transfer(user.mana_pool) + else + if(!user.is_holding(src)) + balloon_alert(user, "too far!") + return + var/mana_to_draw = tgui_input_number(user, "How much mana do you want to draw from the star? Soft Cap (You will lose mana when above this!): [user.mana_pool.softcap]", "Draw Mana", max_value = mana_pool.maximum_mana_capacity) + if(!mana_to_draw || QDELETED(user) || QDELETED(src) || !user.is_holding(src)) + return + var/drawn_mana = mana_to_draw + balloon_alert(user, "drawing mana....") + mana_pool.transfer_specific_mana(user.mana_pool, drawn_mana, decrement_budget = TRUE) + +/obj/item/mana_battery/mana_crystal/small/focus //really only exists for debug. + name = "Focused Small Volite Crystal" + desc = "A focused variant of the standard small volite crystal. You can draw mana from this while casting." + icon_state = "small" + +/obj/item/mana_battery/mana_crystal/small/focus/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_POOL_AVAILABLE_FOR_CAST, INNATE_TRAIT) -*/ diff --git a/maplestation_modules/code/modules/magic/mana/sources/transmutation.dm b/maplestation_modules/code/modules/magic/mana/sources/transmutation.dm deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/maplestation_modules/code/modules/magic/spell.dm b/maplestation_modules/code/modules/magic/spell.dm new file mode 100644 index 000000000000..cc33bca467b4 --- /dev/null +++ b/maplestation_modules/code/modules/magic/spell.dm @@ -0,0 +1,9 @@ +/datum/action/proc/get_owner() + return owner + +/datum/action/cooldown/spell/proc/spell_cannot_activate() + return . | SPELL_CANCEL_CAST + +/datum/action/cooldown/spell/pointed/spell_cannot_activate() + unset_click_ability(owner) + return ..() diff --git a/maplestation_modules/code/modules/magic/story_spells/acid_touch.dm b/maplestation_modules/code/modules/magic/story_spells/acid_touch.dm index 65931b59462a..f18d19db2f08 100644 --- a/maplestation_modules/code/modules/magic/story_spells/acid_touch.dm +++ b/maplestation_modules/code/modules/magic/story_spells/acid_touch.dm @@ -1,26 +1,5 @@ -/datum/component/uses_mana/story_spell/touch/acid_touch - /// Attunement modifier for Earth attunement - var/acid_touch_attunement_amount = 0.5 - /// Base mana cost - var/acid_touch_cost = 50 - /// Multiplier applied to cost when casting on turfs - var/turf_cost_multiplier = 0.25 - /// Multiplier applied to cost when casting on objects - var/obj_cost_multiplier = 0.5 - -/datum/component/uses_mana/story_spell/touch/acid_touch/get_attunement_dispositions() - . = ..() - .[/datum/attunement/earth] += acid_touch_attunement_amount - -/datum/component/uses_mana/story_spell/touch/acid_touch/get_mana_required(atom/caster, atom/cast_on, ...) - var/datum/action/cooldown/spell/touch/acid_touch/spell = parent - var/final_cost = acid_touch_cost - final_cost *= ..() // default multiplier - if(isturf(cast_on)) - final_cost *= max(1, spell.turf_modifier * turf_cost_multiplier) // so it's not a skeleton key - if(isobj(cast_on)) - final_cost *= max(1, spell.obj_modifier * obj_cost_multiplier) // to make it harder to destroy items - return final_cost +#define ACID_TOUCH_ATTUNEMENT_EARTH 0.5 +#define ACID_TOUCH_MANA_BASECOST 50 /datum/action/cooldown/spell/touch/acid_touch name = "Acid Touch" @@ -34,6 +13,7 @@ invocation = "Ac rid!" invocation_type = INVOCATION_SHOUT spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC + var/acid_touch_cost = ACID_TOUCH_MANA_BASECOST hand_path = /obj/item/melee/touch_attack/acid_touch can_cast_on_self = TRUE @@ -47,10 +27,32 @@ var/obj_modifier = 2.5 /// Modifier to power and volume applied to aciding turfs var/turf_modifier = 10 + /// Multiplier applied to cost when casting on turfs + var/turf_cost_multiplier = 0.25 + /// Multiplier applied to cost when casting on objects + var/obj_cost_multiplier = 0.5 /datum/action/cooldown/spell/touch/acid_touch/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/touch/acid_touch) + + + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_EARTH] += ACID_TOUCH_ATTUNEMENT_EARTH + + AddComponent(/datum/component/uses_mana/touch_spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + mana_required = CALLBACK(src, PROC_REF(get_mana_required)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + attunements = attunements, \ + ) + +/datum/action/cooldown/spell/touch/acid_touch/proc/get_mana_required(atom/caster, atom/cast_on, ...) + var/final_cost = acid_touch_cost + if(isturf(cast_on)) + final_cost *= max(1, turf_modifier * turf_cost_multiplier) // so it's not a skeleton key + if(isobj(cast_on)) + final_cost *= max(1, obj_modifier * obj_cost_multiplier) // to make it harder to destroy items + return final_cost /datum/action/cooldown/spell/touch/acid_touch/is_valid_target(atom/cast_on) return TRUE @@ -80,3 +82,6 @@ icon_state = "duffelcurse" inhand_icon_state = "duffelcurse" color = COLOR_PALE_GREEN_GRAY + +#undef ACID_TOUCH_ATTUNEMENT_EARTH +#undef ACID_TOUCH_MANA_BASECOST diff --git a/maplestation_modules/code/modules/magic/story_spells/airhike.dm b/maplestation_modules/code/modules/magic/story_spells/airhike.dm index 913706aa68e9..bc4b583e8b98 100644 --- a/maplestation_modules/code/modules/magic/story_spells/airhike.dm +++ b/maplestation_modules/code/modules/magic/story_spells/airhike.dm @@ -1,19 +1,5 @@ -/datum/component/uses_mana/story_spell/airhike - var/attunement_amount = 0.5 - var/airhike_cost = 30 - -/datum/component/uses_mana/story_spell/airhike/get_attunement_dispositions() - . = ..() - .[MAGIC_ELEMENT_WIND] += attunement_amount - -/datum/component/uses_mana/story_spell/airhike/get_mana_required(atom/caster, atom/cast_on, ...) - return ..() * airhike_cost - -//If there isn't enough mana and the Rclick check passes so it won't mess up any future Normal casts -/datum/component/uses_mana/story_spell/airhike/can_activate_check_failure(give_feedback, ...) - var/datum/action/cooldown/spell/airhike/airhike_spell = parent - airhike_spell.zup = FALSE - return ..() +#define AIRHIKE_ATTUNEMENT_WIND 0.5 +#define AIRHIKE_MANA_COST 30 /datum/action/cooldown/spell/airhike name = "Air Hike" @@ -32,10 +18,23 @@ var/jumpspeed = 2 var/zup = FALSE + var/mana_cost = AIRHIKE_MANA_COST + /datum/action/cooldown/spell/airhike/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/airhike) + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_WIND] += AIRHIKE_ATTUNEMENT_WIND + + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = mana_cost, \ + attunements = attunements, \ + ) +/datum/action/cooldown/spell/airhike/spell_cannot_activate() + zup = FALSE + return ..() /datum/action/cooldown/spell/airhike/is_valid_target(atom/cast_on) return iscarbon(cast_on) @@ -80,3 +79,6 @@ to_chat(usr, span_warning("Something prevents you from dashing upwards!")) return FALSE return ..() + +#undef AIRHIKE_ATTUNEMENT_WIND +#undef AIRHIKE_MANA_COST diff --git a/maplestation_modules/code/modules/magic/story_spells/components/pointed_component.dm b/maplestation_modules/code/modules/magic/story_spells/components/pointed_component.dm deleted file mode 100644 index 45b5573479e4..000000000000 --- a/maplestation_modules/code/modules/magic/story_spells/components/pointed_component.dm +++ /dev/null @@ -1,13 +0,0 @@ -/datum/component/uses_mana/story_spell/pointed - -/datum/component/uses_mana/story_spell/pointed/Initialize(...) - . = ..() - - if (!istype(parent, /datum/action/cooldown/spell/pointed)) - return . | COMPONENT_INCOMPATIBLE - -/datum/component/uses_mana/story_spell/pointed/can_activate_check_failure(give_feedback, atom/cast_on) - . = ..() - var/datum/action/cooldown/spell/spell_parent = parent - - spell_parent.unset_click_ability(spell_parent.owner) diff --git a/maplestation_modules/code/modules/magic/story_spells/components/story_spell_component.dm b/maplestation_modules/code/modules/magic/story_spells/components/story_spell_component.dm index 29021b9703d5..f27d85249b9e 100644 --- a/maplestation_modules/code/modules/magic/story_spells/components/story_spell_component.dm +++ b/maplestation_modules/code/modules/magic/story_spells/components/story_spell_component.dm @@ -1,32 +1,48 @@ -/// The base component to be applied to all spells that interact with the mana system. -/datum/component/uses_mana/story_spell +// this is basically only used to cut down on boilerplate. +// all that remains is stuff that has to be on the spell itself to work (checking for mana cost, attunements, some of the callbacks) +// i'll provide a sample block so this file can be a "tutorial" IG +/datum/component/uses_mana/spell can_transfer = FALSE -/datum/component/uses_mana/story_spell/Initialize(...) +/datum/component/uses_mana/spell/Initialize( + datum/callback/activate_check_failure_callback, + datum/callback/get_user_callback, + pre_use_check_with_feedback_comsig = COMSIG_SPELL_BEFORE_CAST, + pre_use_check_comsig, + post_use_comsig = COMSIG_SPELL_AFTER_CAST, + datum/callback/mana_required, + list/datum/attunement/attunements + ) + . = ..() if (!istype(parent, /datum/action/cooldown/spell)) return . | COMPONENT_INCOMPATIBLE -/datum/component/uses_mana/story_spell/RegisterWithParent() - . = ..() +/* sample tutorial block. plus explanations. - RegisterSignal(parent, COMSIG_SPELL_BEFORE_CAST, PROC_REF(handle_precast)) - RegisterSignal(parent, COMSIG_SPELL_CAST, PROC_REF(handle_cast)) - RegisterSignal(parent, COMSIG_SPELL_AFTER_CAST, PROC_REF(react_to_successful_use)) - -/datum/component/uses_mana/story_spell/UnregisterFromParent() +/datum/action/cooldown/spell/ourspell/New(Target, original) . = ..() - UnregisterSignal(parent, COMSIG_SPELL_BEFORE_CAST) - UnregisterSignal(parent, COMSIG_SPELL_CAST) - UnregisterSignal(parent, COMSIG_SPELL_AFTER_CAST) + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_X] += SPELL_X_ATTUNEMENT // define the numeric value you want for the attunement ideally at the top, it is the value you want the spell's cost to be multiplied (done using decimals since its supposed to be a discount) by when the user is correctly attunned.. + replace the "X" with what element you want, such as "light". Replace "SPELL" with the name of your spell. -/datum/component/uses_mana/story_spell/give_unable_to_activate_feedback(atom/caster, atom/cast_on, ...) - caster.balloon_alert(caster, "insufficient mana!") + AddComponent(/datum/component/uses_mana/spell, \ // the two main signals are pre-defined in uses_mana/spell so you don't have to copy paste it. everything here needs to be in the init since its on the spell itself. + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ // this is defined on datum/action/cooldown/spell and just sends the signal used to cancel casting. must be in this define. + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ // "get_owner" is a proc on datum/action/ and must be defined in "the block" + mana_required = mana_cost, \ // mana_cost is defined on the spell datum action, set it to a variable at the top ideally. if you want fancier stuff, see the next line: + mana_required = CALLBACK(src, PROC_REF(get_mana_consumed)), \ // obviously only ever set mana required ONCE, but this is an example of what you want to do if you want to do a variable cost system, define the proc for this on the spell itself. + attunements = attunements, \ // see above. you can just copy paste this to every instance, if you don't want attunements just don't add this block or the two lines outside the add component + ) -// SIGNAL HANDLERS + heres a sample callback for get mana required, taken from healing touch. + /datum/action/cooldown/spell/touch/healing_touch/proc/get_mana_consumed(atom/caster, atom/cast_on, ...) + return (brute_heal + burn_heal + tox_heal + oxy_heal + pain_heal * 3) \ // instead this will calculate your cost based on how much it heals. whenever it wants to check the mana cost value, it'll defer to this proc. + * mana_cost +*/ +// this should be the only thing left lmfao, the rest is going to be held till the end /** * Actions done before the actual cast is called. * This is the last chance to cancel the spell from being cast. @@ -38,14 +54,36 @@ * - SPELL_NO_FEEDBACK will prevent the spell from calling [proc/spell_feedback] on cast. (invocation), sounds) * - SPELL_NO_IMMEDIATE_COOLDOWN will prevent the spell from starting its cooldown between cast and before after_cast. */ -/datum/component/uses_mana/story_spell/proc/handle_precast(datum/action/cooldown/spell/source, atom/cast_on) - SIGNAL_HANDLER +/* /datum/component/uses_mana/spell/RegisterWithParent() + . = ..() + + RegisterSignal(parent, COMSIG_SPELL_BEFORE_CAST, PROC_REF(handle_precast)) + RegisterSignal(parent, COMSIG_SPELL_CAST, PROC_REF(handle_cast)) + RegisterSignal(parent, COMSIG_SPELL_AFTER_CAST, PROC_REF(react_to_successful_use)) */ - return can_activate_check(TRUE, source.owner, cast_on) +/* /datum/component/uses_mana/spell/UnregisterFromParent() + . = ..() + + UnregisterSignal(parent, COMSIG_SPELL_BEFORE_CAST) + UnregisterSignal(parent, COMSIG_SPELL_CAST) + UnregisterSignal(parent, COMSIG_SPELL_AFTER_CAST) */ -/datum/component/uses_mana/story_spell/can_activate_check_failure(give_feedback, atom/caster, atom/cast_on, ...) +/* /datum/component/uses_mana/spell/give_unable_to_activate_feedback(atom/cast_on) . = ..() - return . | SPELL_CANCEL_CAST + var/datum/action/cooldown/spell/spell_parent = parent + + spell_parent.owner.balloon_alert(spell_parent.owner, "insufficient mana!") */ // should be redundant, holding onto till pr completion + +// SIGNAL HANDLERS + + +/* /datum/component/uses_mana/spell/proc/handle_precast(atom/cast_on) + SIGNAL_HANDLER + return can_activate_with_feedback() */ // todo get this up to date + //can_activate_with_feedback(TRUE, parent_spell.owner, cast_on) + //var/datum/action/cooldown/spell/parent_spell = parent + + /** * Actions done as the main effect of the spell. @@ -53,15 +91,12 @@ * For spells without a click intercept, [cast_on] will be the owner. * For click spells, [cast_on] is whatever the owner clicked on in casting the spell. */ -/datum/component/uses_mana/story_spell/proc/handle_cast(datum/action/cooldown/spell/source, atom/cast_on) +/* /datum/component/uses_mana/spell/proc/handle_cast(atom/cast_on) SIGNAL_HANDLER return -/datum/component/uses_mana/story_spell/react_to_successful_use(datum/action/cooldown/spell/source, atom/cast_on) - drain_mana(null, null, source.owner, cast_on) - -/datum/component/uses_mana/story_spell/get_mana_required(atom/caster, atom/cast_on, ...) +/datum/component/uses_mana/spell/proc/get_mana_required_spell(atom/caster, atom/cast_on, ...) if(ismob(caster)) var/mob/caster_mob = caster return caster_mob.get_casting_cost_mult() - return 1 + return 1 */ diff --git a/maplestation_modules/code/modules/magic/story_spells/components/touch_component.dm b/maplestation_modules/code/modules/magic/story_spells/components/touch_component.dm index b65d8ccc71d8..08faa2785892 100644 --- a/maplestation_modules/code/modules/magic/story_spells/components/touch_component.dm +++ b/maplestation_modules/code/modules/magic/story_spells/components/touch_component.dm @@ -1,51 +1,29 @@ #define COMSIG_SPELL_TOUCH_CAN_HIT "spell_touch_can_hit" - /** * A preset component for touch spells that use mana * * These spells require mana to activate (channel into your hand) * but does not expend mana until you actually touch someone with it. + * Addendum: under normal situations cooldown spells like this would get the /spell subtype + * touch spells are inherently snowflakey, so it gets a snowflakey component. */ -/datum/component/uses_mana/story_spell/touch +/datum/component/uses_mana/touch_spell can_transfer = FALSE -/datum/component/uses_mana/story_spell/touch/Initialize(...) - if (!istype(parent, /datum/action/cooldown/spell/touch)) - return COMPONENT_INCOMPATIBLE - - return ..() - -/datum/component/uses_mana/story_spell/touch/RegisterWithParent() - RegisterSignal(parent, COMSIG_SPELL_BEFORE_CAST, PROC_REF(handle_precast)) - RegisterSignal(parent, COMSIG_SPELL_TOUCH_CAN_HIT, PROC_REF(can_touch)) - RegisterSignal(parent, COMSIG_SPELL_TOUCH_HAND_HIT, PROC_REF(handle_touch)) - -/datum/component/uses_mana/story_spell/touch/UnregisterFromParent() - UnregisterSignal(parent, COMSIG_SPELL_BEFORE_CAST) - UnregisterSignal(parent, COMSIG_SPELL_TOUCH_CAN_HIT) - UnregisterSignal(parent, COMSIG_SPELL_TOUCH_HAND_HIT) - -/datum/component/uses_mana/story_spell/touch/proc/can_touch( - datum/action/cooldown/spell/touch/source, - atom/victim, - mob/living/carbon/caster, +/datum/component/uses_mana/touch_spell/Initialize( + datum/callback/activate_check_failure_callback, + datum/callback/get_user_callback, + pre_use_check_with_feedback_comsig = COMSIG_SPELL_BEFORE_CAST, + pre_use_check_comsig, + post_use_comsig = COMSIG_SPELL_TOUCH_HAND_HIT, + datum/callback/mana_required, + list/datum/attunement/attunements, ) - SIGNAL_HANDLER - - if(source.attached_hand) - return NONE // de-activating, so don't block it - - return can_activate_check(TRUE, caster, victim) -/datum/component/uses_mana/story_spell/touch/proc/handle_touch( - datum/action/cooldown/spell/touch/source, - atom/victim, - mob/living/carbon/caster, - obj/item/melee/touch_attack/hand, -) - SIGNAL_HANDLER + ..() - react_to_successful_use(source, victim) + if (!istype(parent, /datum/action/cooldown/spell/touch)) + return COMPONENT_INCOMPATIBLE // Override to send a signal we can react to /datum/action/cooldown/spell/touch/can_hit_with_hand(atom/victim, mob/caster) diff --git a/maplestation_modules/code/modules/magic/story_spells/convect.dm b/maplestation_modules/code/modules/magic/story_spells/convect.dm index 4a8fa181dfb7..b9e114a6dd12 100644 --- a/maplestation_modules/code/modules/magic/story_spells/convect.dm +++ b/maplestation_modules/code/modules/magic/story_spells/convect.dm @@ -1,32 +1,5 @@ -/datum/component/uses_mana/story_spell/pointed/convect - -/datum/component/uses_mana/story_spell/pointed/convect/Initialize(...) - . = ..() - - if (!istype(parent, /datum/action/cooldown/spell/pointed/convect)) - return . | COMPONENT_INCOMPATIBLE - #define CONVECT_HEAT_ATTUNEMENT 0.5 #define CONVECT_ICE_ATTUNEMENT 0.5 -/datum/component/uses_mana/story_spell/pointed/convect/get_attunement_dispositions() - . = ..() - var/datum/action/cooldown/spell/pointed/convect/convect_spell = parent - if (convect_spell.temperature_for_cast == 0) - return - if (convect_spell.temperature_for_cast > 0) - .[MAGIC_ELEMENT_FIRE] += CONVECT_HEAT_ATTUNEMENT - .[MAGIC_ELEMENT_ICE] -= CONVECT_HEAT_ATTUNEMENT - return - else - .[MAGIC_ELEMENT_ICE] += CONVECT_ICE_ATTUNEMENT - .[MAGIC_ELEMENT_FIRE] -= CONVECT_ICE_ATTUNEMENT - -#undef CONVECT_HEAT_ATTUNEMENT -#undef CONVECT_ICE_ATTUNEMENT - -/datum/component/uses_mana/story_spell/pointed/convect/get_mana_required(atom/caster, atom/cast_on, ...) - var/datum/action/cooldown/spell/pointed/convect/convect_spell = parent - return ..() * abs(convect_spell.temperature_for_cast) * CONVECT_MANA_COST_PER_KELVIN /datum/action/cooldown/spell/pointed/convect name = "Convect" @@ -42,6 +15,8 @@ unset_after_click = FALSE spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC + var/list/datum/attunement/attunements + var/temperature_for_cast = 25 //Maximum temperature change able to be chosen when casting var/maximum_temperature_delta = 50 @@ -53,7 +28,33 @@ /datum/action/cooldown/spell/pointed/convect/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/pointed/convect) + update_attunement_dispositions() + + AddComponent(/datum/component/uses_mana/spell, \ + mana_required = CALLBACK(src, PROC_REF(get_mana_consumed)), \ + attunements = src.attunements, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + ) + +/datum/action/cooldown/spell/pointed/convect/proc/update_attunement_dispositions() + if (isnull(attunements)) + attunements = GLOB.default_attunements.Copy() + + if (temperature_for_cast == 0) + return attunements + if (temperature_for_cast > 0) + attunements[MAGIC_ELEMENT_FIRE] += CONVECT_HEAT_ATTUNEMENT + attunements[MAGIC_ELEMENT_ICE] -= CONVECT_HEAT_ATTUNEMENT + else + attunements[MAGIC_ELEMENT_ICE] += CONVECT_ICE_ATTUNEMENT + attunements[MAGIC_ELEMENT_FIRE] -= CONVECT_ICE_ATTUNEMENT + + return attunements + +/datum/action/cooldown/spell/pointed/convect/proc/get_mana_consumed() + return ((abs(temperature_for_cast) * CONVECT_MANA_COST_PER_KELVIN)) + // todo: methodize the casting cost mult part /datum/action/cooldown/spell/pointed/convect/is_valid_target(atom/cast_on) return TRUE //cant call suepr cause i want to be able to use this on myself @@ -72,6 +73,7 @@ return FALSE temperature_for_cast = temperature owner.balloon_alert(owner, "casting temperature set to [temperature]K") + update_attunement_dispositions() /datum/action/cooldown/spell/pointed/convect/on_activation(mob/on_who) . = ..() @@ -132,3 +134,6 @@ var/just_convected_text = span_warning("You [heat_or_cool] [cast_on] by [temperature_for_cast]K.") owner.balloon_alert(owner, just_convected_text) to_chat(owner, just_convected_text) + +#undef CONVECT_HEAT_ATTUNEMENT +#undef CONVECT_ICE_ATTUNEMENT diff --git a/maplestation_modules/code/modules/magic/story_spells/finger_flame.dm b/maplestation_modules/code/modules/magic/story_spells/finger_flame.dm index 24f84d64a038..b393f68acaf7 100644 --- a/maplestation_modules/code/modules/magic/story_spells/finger_flame.dm +++ b/maplestation_modules/code/modules/magic/story_spells/finger_flame.dm @@ -1,40 +1,6 @@ +#define FINGERFLAME_MANA_COST 5 // very cheap, it's just a lighter +#define FINGERFLAME_ATTUNEMENT_FIRE 0.2 // flame users make this EVEN cheaper // This doesn't use the touch spell component because we use mana on activation rather than touch. -/datum/component/uses_mana/story_spell/finger_flame - var/flame_cost = 5 // very cheap, it's just a lighter - var/flame_attunement = 0.2 // flame users make this EVEN cheaper - - /// You get some seconds of freecasting to prevent spam. - COOLDOWN_DECLARE(free_use_cooldown) - -/datum/component/uses_mana/story_spell/finger_flame/RegisterWithParent() - RegisterSignal(parent, COMSIG_SPELL_BEFORE_CAST, PROC_REF(handle_precast)) - RegisterSignal(parent, COMSIG_SPELL_CAST, PROC_REF(handle_cast)) - -/datum/component/uses_mana/story_spell/finger_flame/UnregisterFromParent() - UnregisterSignal(parent, COMSIG_SPELL_BEFORE_CAST) - UnregisterSignal(parent, COMSIG_SPELL_CAST) - -/datum/component/uses_mana/story_spell/finger_flame/get_attunement_dispositions() - . = ..() - .[/datum/attunement/fire] = flame_attunement - -/datum/component/uses_mana/story_spell/finger_flame/get_mana_required(atom/caster, atom/cast_on, ...) - return COOLDOWN_FINISHED(src, free_use_cooldown) ? (..() * flame_cost) : 0 - -/datum/component/uses_mana/story_spell/finger_flame/handle_precast(datum/action/cooldown/spell/touch/finger_flame/source, atom/cast_on) - if(source.attached_hand) - return NONE - return ..() - -/datum/component/uses_mana/story_spell/finger_flame/handle_cast(datum/action/cooldown/spell/source, atom/cast_on) - // this drains mana "on cast", and not on "touch spell hit" or "on after cast", unlike the touch spell component. - // whichs means it uses mana when the flame / hand is CREATED instead of used - react_to_successful_use(source, cast_on) - -/datum/component/uses_mana/story_spell/finger_flame/react_to_successful_use(datum/action/cooldown/spell/source, atom/cast_on) - . = ..() - COOLDOWN_START(src, free_use_cooldown, 4 SECONDS) - // All this spell does is give you a lighter on demand. /datum/action/cooldown/spell/touch/finger_flame name = "Free Finger Flame" @@ -51,10 +17,19 @@ draw_message = null drop_message = null can_cast_on_self = TRUE // self burn + var/mana_cost = FINGERFLAME_MANA_COST + // You get some seconds of freecasting to prevent spam. + COOLDOWN_DECLARE(free_use_cooldown) // I was considering giving this the same "trigger on snap emote" effect that the arm implant has, // but considering this has a tangible cost (mana) while the arm implant is free, I decided against it. +/datum/action/cooldown/spell/touch/finger_flame/cast(...) + // this drains mana "on cast", and not on "touch spell hit" or "on after cast", unlike the touch spell component. + // whichs means it uses mana when the flame / hand is CREATED instead of used + ..() + COOLDOWN_START(src, free_use_cooldown, 4 SECONDS) + /datum/action/cooldown/spell/touch/finger_flame/can_cast_spell(feedback) return ..() && !HAS_TRAIT(owner, TRAIT_EMOTEMUTE) // checked as if it were an emote invocation spell @@ -103,9 +78,23 @@ /datum/action/cooldown/spell/touch/finger_flame/mana/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/finger_flame) + + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_FIRE] += FINGERFLAME_ATTUNEMENT_FIRE + + AddComponent(/datum/component/uses_mana/, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + post_use_comsig = COMSIG_SPELL_CAST, \ + pre_use_check_with_feedback_comsig = COMSIG_SPELL_BEFORE_CAST, \ + mana_required = CALLBACK(src, PROC_REF(get_mana_consumed)), \ + attunements = attunements, \ + ) desc += " Costs mana to conjure, but is free to maintain." +/datum/action/cooldown/spell/touch/finger_flame/proc/get_mana_consumed(atom/caster, datum/spell, atom/cast_on, ...) + return COOLDOWN_FINISHED(src, free_use_cooldown) ? (mana_cost) : 0 + /datum/action/cooldown/spell/touch/finger_flame/lizard name = "Muster Flame" desc = "Muster all you can to breathe a small mote of fire - just strong enough to light a cigarette. \ @@ -248,3 +237,6 @@ . = ..() var/datum/action/cooldown/spell/touch/finger_flame/lizard/fire_breath = locate() in C.actions qdel(fire_breath) + +#undef FINGERFLAME_MANA_COST +#undef FINGERFLAME_ATTUNEMENT_FIRE diff --git a/maplestation_modules/code/modules/magic/story_spells/flare.dm b/maplestation_modules/code/modules/magic/story_spells/flare.dm index 53d5c0329593..81495deda932 100644 --- a/maplestation_modules/code/modules/magic/story_spells/flare.dm +++ b/maplestation_modules/code/modules/magic/story_spells/flare.dm @@ -1,13 +1,5 @@ -/datum/component/uses_mana/story_spell/conjure_item/flare - var/attunement_amount = 0.5 - -/datum/component/uses_mana/story_spell/conjure_item/flare/get_attunement_dispositions() - . = ..() - .[MAGIC_ELEMENT_LIGHT] += attunement_amount - -/datum/component/uses_mana/story_spell/conjure_item/flare/get_mana_required(atom/caster, atom/cast_on, ...) - var/datum/action/cooldown/spell/conjure_item/flare/flare_spell = parent - return ..() * flare_spell.flare_cost +#define FLARE_LIGHT_ATTUNEMENT 0.5 +#define FLARE_BASE_MANA_COST 30 /datum/action/cooldown/spell/conjure_item/flare name = "Flare" @@ -24,13 +16,13 @@ /// What color the flare created appears to be var/flare_color /// What the mana cost is, affected by Lesser variant. - var/flare_cost = 30 + var/mana_cost = FLARE_BASE_MANA_COST //Variant that conjures a weaker version /datum/spellbook_item/spell/conjure_item/flare/apply_params(datum/action/cooldown/spell/conjure_item/flare/our_spell, lesser) if (lesser) our_spell.item_type = /obj/item/flashlight/glowstick/magic/lesser - our_spell.flare_cost = 10 + our_spell.mana_cost = 10 our_spell.cooldown_time = 2 MINUTES our_spell.name = "Lesser Flare" our_spell.desc = "Conjure lumens into a glob to be held or thrown to light an area. Right-click the spell icon to set the light color. This weaker version burns up quicker and has a considerable cooldown between conjures." @@ -45,7 +37,15 @@ /datum/action/cooldown/spell/conjure_item/flare/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/conjure_item/flare) + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_LIGHT] += FLARE_LIGHT_ATTUNEMENT + + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = mana_cost, \ + attunements = attunements, \ + ) /obj/item/flashlight/glowstick/magic name = "self sustaining flare" @@ -109,3 +109,6 @@ var/new_color new_color = input(user, "Choose a new color for the flare.", "Light Color", new_color) as color|null return new_color + +#undef FLARE_LIGHT_ATTUNEMENT +#undef FLARE_BASE_MANA_COST diff --git a/maplestation_modules/code/modules/magic/story_spells/freeze_person.dm b/maplestation_modules/code/modules/magic/story_spells/freeze_person.dm index af7f0a2385ca..93c3b20f7c53 100644 --- a/maplestation_modules/code/modules/magic/story_spells/freeze_person.dm +++ b/maplestation_modules/code/modules/magic/story_spells/freeze_person.dm @@ -1,13 +1,5 @@ -/datum/component/uses_mana/story_spell/pointed/freeze_person - var/freeze_person_attunement = 0.5 - var/freeze_person_cost = 50 - -/datum/component/uses_mana/story_spell/pointed/freeze_person/get_attunement_dispositions() - . = ..() - .[/datum/attunement/ice] = freeze_person_attunement - -/datum/component/uses_mana/story_spell/pointed/freeze_person/get_mana_required(atom/caster, atom/cast_on, ...) - return ..() * freeze_person_cost +#define FREEZE_PERSON_ATTUNEMENT_ICE 0.5 +#define FREEZE_PERSON_MANA_COST 50 /datum/action/cooldown/spell/pointed/freeze_person name = "Freeze Person" @@ -22,6 +14,7 @@ invocation = "Als Eisz'it!" invocation_type = INVOCATION_SHOUT school = SCHOOL_CONJURATION + var/mana_cost = FREEZE_PERSON_MANA_COST active_msg = "You prepare to freeze someone." deactive_msg = "You stop preparing to freeze someone." @@ -31,7 +24,15 @@ /datum/action/cooldown/spell/pointed/freeze_person/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/pointed/freeze_person) + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_ICE] += FREEZE_PERSON_ATTUNEMENT_ICE + + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = mana_cost, \ + attunements = attunements, \ + ) /datum/action/cooldown/spell/pointed/freeze_person/is_valid_target(atom/cast_on) if(!isliving(cast_on)) @@ -123,3 +124,6 @@ owner.move_force = initial(owner.move_force) owner.pull_force = initial(owner.pull_force) return ..() + +#undef FREEZE_PERSON_ATTUNEMENT_ICE +#undef FREEZE_PERSON_MANA_COST diff --git a/maplestation_modules/code/modules/magic/story_spells/healing_touch.dm b/maplestation_modules/code/modules/magic/story_spells/healing_touch.dm index 1b422cdcb802..9dba0af95d5e 100644 --- a/maplestation_modules/code/modules/magic/story_spells/healing_touch.dm +++ b/maplestation_modules/code/modules/magic/story_spells/healing_touch.dm @@ -4,19 +4,9 @@ #define HEAL_HANDLED (1<<0) #define HEAL_CANCELLED (1<<1) -/datum/component/uses_mana/story_spell/touch/healing_touch - var/healing_touch_attunement_amount = 0.5 - var/healing_touch_cost_per_healed = 1.5 +#define HEALING_TOUCH_ATTUNEMENT_LIFE 0.5 +#define HEALING_TOUCH_COST_PER_HEALED 1.5 -/datum/component/uses_mana/story_spell/touch/healing_touch/get_attunement_dispositions() - . = ..() - .[/datum/attunement/life] += healing_touch_attunement_amount - -/datum/component/uses_mana/story_spell/touch/healing_touch/get_mana_required(atom/caster, atom/cast_on, ...) - var/datum/action/cooldown/spell/touch/healing_touch/touch_spell = parent - return ..() \ - * (touch_spell.brute_heal + touch_spell.burn_heal + touch_spell.tox_heal + touch_spell.oxy_heal + touch_spell.pain_heal * 3) \ - * healing_touch_cost_per_healed // Touch based healing spell, very simple. Only works on organic mobs or anything that hooks to the comsig. /datum/action/cooldown/spell/touch/healing_touch @@ -30,6 +20,7 @@ cooldown_time = 1 MINUTES invocation_type = INVOCATION_NONE spell_requirements = NONE + var/mana_cost = HEALING_TOUCH_COST_PER_HEALED invocation = "Sana manu!" invocation_type = INVOCATION_WHISPER @@ -49,7 +40,19 @@ /datum/action/cooldown/spell/touch/healing_touch/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/touch/healing_touch) + + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_LIFE] += HEALING_TOUCH_ATTUNEMENT_LIFE + + AddComponent(/datum/component/uses_mana/touch_spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + mana_required = CALLBACK(src, PROC_REF(get_mana_consumed)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + attunements = attunements, \ + ) +/datum/action/cooldown/spell/touch/healing_touch/proc/get_mana_consumed(atom/caster, datum/spell, atom/cast_on) + return (brute_heal + burn_heal + tox_heal + oxy_heal + pain_heal * 3) \ + * mana_cost /datum/action/cooldown/spell/touch/healing_touch/is_valid_target(atom/cast_on) if(SEND_SIGNAL(cast_on, COMSIG_SPELL_HEALING_TOUCH_IS_VALID, src) & CAN_BE_HEALED) @@ -182,3 +185,5 @@ #undef COMSIG_SPELL_HEALING_TOUCH_CAST #undef HEAL_HANDLED #undef HEAL_CANCELLED +#undef HEALING_TOUCH_ATTUNEMENT_LIFE +#undef HEALING_TOUCH_COST_PER_HEALED diff --git a/maplestation_modules/code/modules/magic/story_spells/ice_blast.dm b/maplestation_modules/code/modules/magic/story_spells/ice_blast.dm index a3a5ac8d852e..1a1f50d77848 100644 --- a/maplestation_modules/code/modules/magic/story_spells/ice_blast.dm +++ b/maplestation_modules/code/modules/magic/story_spells/ice_blast.dm @@ -1,13 +1,5 @@ -/datum/component/uses_mana/story_spell/pointed/ice_blast - var/ice_blast_attunement = 0.5 - var/ice_blast_cost = 25 - -/datum/component/uses_mana/story_spell/pointed/ice_blast/get_attunement_dispositions() - . = ..() - .[/datum/attunement/ice] = ice_blast_attunement - -/datum/component/uses_mana/story_spell/pointed/ice_blast/get_mana_required(atom/caster, atom/cast_on, ...) - return ..() * ice_blast_cost +#define ICE_BLAST_ATTUNEMENT_ICE 0.5 +#define ICE_BLAST_MANA_COST 25 /datum/action/cooldown/spell/pointed/projectile/ice_blast name = "Ice blast" @@ -22,6 +14,7 @@ invocation = "Frig'dus humer'm!" //this one sucks, ireally wis hi had something better invocation_type = INVOCATION_SHOUT school = SCHOOL_CONJURATION + var/mana_cost = ICE_BLAST_MANA_COST active_msg = "You prepare to throw an ice blast." deactive_msg = "You stop preparing to throw an ice blast." @@ -32,7 +25,15 @@ /datum/action/cooldown/spell/pointed/projectile/ice_blast/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/pointed/ice_blast) + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_ICE] += ICE_BLAST_ATTUNEMENT_ICE + + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = mana_cost, \ + attunements = attunements, \ + ) /// Special ice made so that I can replace it's Initialize's MakeSlippery call to have a different property. /turf/open/misc/funny_ice diff --git a/maplestation_modules/code/modules/magic/story_spells/ice_knife.dm b/maplestation_modules/code/modules/magic/story_spells/ice_knife.dm index 91bda5f0156e..e85f5758eb1a 100644 --- a/maplestation_modules/code/modules/magic/story_spells/ice_knife.dm +++ b/maplestation_modules/code/modules/magic/story_spells/ice_knife.dm @@ -1,20 +1,14 @@ -/datum/component/uses_mana/story_spell/conjure_item/ice_knife - var/ice_knife_attunement = 0.5 - -/datum/component/uses_mana/story_spell/conjure_item/ice_knife/get_attunement_dispositions() - . = ..() - .[/datum/attunement/ice] = ice_knife_attunement - -/datum/component/uses_mana/story_spell/conjure_item/ice_knife/get_mana_required(atom/caster, atom/cast_on, ...) - var/datum/action/cooldown/spell/conjure_item/ice_knife/ice_knife_spell = parent - return ..() * ice_knife_spell.ice_knife_cost +#define ICE_KNIFE_ATTUNEMENT_ICE 0.5 +#define ICE_KNIFE_MANA_COST 30 +#define ICE_ARMBLADE_MANA_COST 45 /datum/action/cooldown/spell/conjure_item/ice_knife name = "Ice knife" desc = "Summon an ice knife made from the moisture in the air." button_icon = 'maplestation_modules/icons/mob/actions/actions_cantrips.dmi' button_icon_state = "ice_knife" - + /// What the mana cost is, affected by Armblade variant. + var/mana_cost = ICE_KNIFE_MANA_COST item_type = /obj/item/knife/combat/ice delete_old = TRUE @@ -25,8 +19,19 @@ spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC - /// What the mana cost is, affected by Armblade variant. - var/ice_knife_cost = 30 + +/datum/action/cooldown/spell/conjure_item/ice_knife/New(Target, original) + . = ..() + + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_ICE] += ICE_KNIFE_ATTUNEMENT_ICE + + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = mana_cost, \ + attunements = attunements, \ + ) /obj/item/knife/combat/ice name = "ice knife" @@ -67,7 +72,7 @@ /datum/spellbook_item/spell/ice_knife/apply_params(datum/action/cooldown/spell/conjure_item/ice_knife/our_spell, ice_blade) if (ice_blade) our_spell.item_type = /obj/item/melee/arm_blade/ice_armblade - our_spell.ice_knife_cost = 45 + our_spell.mana_cost = ICE_ARMBLADE_MANA_COST our_spell.name = "Ice Armblade" our_spell.desc = "Construct a blade around your arm, in exchange of harming it in the process." return @@ -136,3 +141,7 @@ return var/spell_hand = (mymob.get_held_index_of_item(src) % 2) ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM mymob.apply_damage(damage, BRUTE, spell_hand) + +#undef ICE_KNIFE_ATTUNEMENT_ICE +#undef ICE_KNIFE_MANA_COST +#undef ICE_ARMBLADE_MANA_COST diff --git a/maplestation_modules/code/modules/magic/story_spells/illusion.dm b/maplestation_modules/code/modules/magic/story_spells/illusion.dm index 2c5c96169510..add158d7d3d5 100644 --- a/maplestation_modules/code/modules/magic/story_spells/illusion.dm +++ b/maplestation_modules/code/modules/magic/story_spells/illusion.dm @@ -1,13 +1,5 @@ -/datum/component/uses_mana/story_spell/pointed/illusion - var/illusion_attunement = 0.5 - var/illusion_cost = 25 - -/datum/component/uses_mana/story_spell/pointed/illusion/get_attunement_dispositions() - . = ..() - .[/datum/attunement/light] = illusion_attunement - -/datum/component/uses_mana/story_spell/pointed/illusion/get_mana_required(atom/caster, atom/cast_on, ...) - return ..() * illusion_cost +#define ILLUSION_ATTUNEMENT_LIGHT 0.5 +#define ILLUSION_MANA_COST 25 /datum/action/cooldown/spell/pointed/illusion name = "Illusion" @@ -18,6 +10,7 @@ cooldown_time = 2 MINUTES spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC + var/mana_cost = ILLUSION_MANA_COST school = SCHOOL_CONJURATION @@ -33,7 +26,16 @@ /datum/action/cooldown/spell/pointed/illusion/New(Target) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/pointed/illusion) + + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_LIGHT] += ILLUSION_ATTUNEMENT_LIGHT + + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = mana_cost, \ + attunements = attunements, \ + ) /datum/action/cooldown/spell/pointed/illusion/Remove(mob/living/remove_from) . = ..() @@ -170,3 +172,6 @@ pull_force = INFINITY sentience_type = SENTIENCE_BOSS // I wanted to make these illusion react to emotes (wave to wave, frown to swears, etc) but maybe later + +#undef ILLUSION_ATTUNEMENT_LIGHT +#undef ILLUSION_MANA_COST diff --git a/maplestation_modules/code/modules/magic/story_spells/mage_hand.dm b/maplestation_modules/code/modules/magic/story_spells/mage_hand.dm index 97d84106869c..3554df0936d2 100644 --- a/maplestation_modules/code/modules/magic/story_spells/mage_hand.dm +++ b/maplestation_modules/code/modules/magic/story_spells/mage_hand.dm @@ -1,8 +1,4 @@ -/datum/component/uses_mana/story_spell/mage_hand - var/mage_hand_cost = 20 - -/datum/component/uses_mana/story_spell/mage_hand/get_mana_required(atom/caster, atom/cast_on, ...) - return ..() * mage_hand_cost +#define MAGE_HAND_MANA_COST 20 // Yeah, it's just a spell that gives you telekinesis for a short period, sue me /datum/action/cooldown/spell/apply_mutations/mage_hand @@ -14,6 +10,7 @@ cooldown_time = 20 SECONDS spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC + var/mana_cost = MAGE_HAND_MANA_COST school = SCHOOL_CONJURATION // or SCHOOL_TRANSLOCATION, or even SCHOOL_PSYCHIC antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_MIND @@ -22,7 +19,11 @@ /datum/action/cooldown/spell/apply_mutations/mage_hand/New(Target) . = ..() mutation_duration = cooldown_time * 0.5 - AddComponent(/datum/component/uses_mana/story_spell/mage_hand) + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = mana_cost, \ + ) /datum/action/cooldown/spell/apply_mutations/mage_hand/Grant(mob/grant_to) . = ..() @@ -68,3 +69,5 @@ /datum/mutation/human/telekinesis/mage_hand/get_visual_indicator() return + +#undef MAGE_HAND_MANA_COST diff --git a/maplestation_modules/code/modules/magic/story_spells/mana_charge.dm b/maplestation_modules/code/modules/magic/story_spells/mana_charge.dm new file mode 100644 index 000000000000..693616165df5 --- /dev/null +++ b/maplestation_modules/code/modules/magic/story_spells/mana_charge.dm @@ -0,0 +1,68 @@ +// spell that recharges your mana by drawing from leylines, temporarily inaccessible because leyline access is weird, and they're underpowered rn +/datum/action/cooldown/spell/leyline_charge + name = "Leyline Charge" + desc = "Regenerate some of your mana by channeling straight from the leylines themselves." + button_icon = 'icons/effects/effects.dmi' + button_icon_state = "plasmasoul" + sound = 'sound/magic/staff_healing.ogg' + + school = SCHOOL_UNSET // no idea where this would go tbh + cooldown_time = 2 MINUTES + invocation_type = INVOCATION_NONE + spell_requirements = NONE + + invocation = "AS'P'RE" + invocation_type = INVOCATION_WHISPER + var/channel_time = 7 SECONDS + + +/datum/action/cooldown/spell/leyline_charge/before_cast(mob/living/cast_on) + . = ..() + if (!cast_on.mana_pool) + cast_on.balloon_alert(cast_on, "no mana pool!") + return + if(!do_after(cast_on, channel_time)) // don't want this casted mid combat + return . | SPELL_CANCEL_CAST + +/datum/action/cooldown/spell/leyline_charge/cast(mob/living/cast_on) + . = ..() + var/randy_value = rand(0,25) // generate a random number, which will be- + var/mana_to_gain = randy_value + 20 // added to the base amount, to get a semi-inconsistent regen amount + var/list/datum/mana_pool/leyline/accessable_leylines = list(get_accessable_leylines()) + if(!accessable_leylines.len) + cast_on.balloon_alert(cast_on, "no accessable leylines!") + return + var/datum/mana_pool/leyline/random_leyline = accessable_leylines[rand(0, accessable_leylines.len)] // get a random leyline + random_leyline.transfer_specific_mana(cast_on.mana_pool, mana_to_gain, decrement_budget = TRUE) + +// recover mana on your own. longer recharge. more is planned, hopefully +/datum/action/cooldown/spell/meditate + name = "Meditation" + desc = "Regenerate some of your mana by focusing within yourself. Takes a period of time to cast." + button_icon = 'icons/effects/effects.dmi' + button_icon_state = "plasmasoul" + sound = 'sound/magic/staff_healing.ogg' + + school = SCHOOL_UNSET // no idea where this would go tbh + cooldown_time = 4 MINUTES + invocation_type = INVOCATION_NONE + spell_requirements = NONE + + invocation = "Focus...." + invocation_type = INVOCATION_WHISPER + var/channel_time = 12 SECONDS + +/datum/action/cooldown/spell/meditate/before_cast(mob/living/cast_on) + . = ..() + if (!cast_on.mana_pool) + cast_on.balloon_alert(cast_on, "no mana pool!") + return + to_chat(cast_on, span_alert("You begin focusing your mind on manipulating ambient mana.")) + if(!do_after(cast_on, channel_time)) // don't want this casted mid combat + return . | SPELL_CANCEL_CAST + +/datum/action/cooldown/spell/meditate/cast(mob/living/cast_on) + . = ..() + var/randy_value = rand(0,25) + var/mana_to_gain = randy_value + 40 + cast_on.mana_pool.adjust_mana(mana_to_gain) diff --git a/maplestation_modules/code/modules/magic/story_spells/mana_sense.dm b/maplestation_modules/code/modules/magic/story_spells/mana_sense.dm new file mode 100644 index 000000000000..73ab4cb94b62 --- /dev/null +++ b/maplestation_modules/code/modules/magic/story_spells/mana_sense.dm @@ -0,0 +1,64 @@ +// mana sense, a roundstart spell that lets you sense mana on a target, on a cooldown +/datum/action/cooldown/spell/pointed/mana_sense + name = "Mana Sense" + desc = "Attempt to read the mana contained with in a creature or object \ + requires no mana to use, but will take you a few seconds to readjust after use. \ " + button_icon = 'icons/effects/effects.dmi' + button_icon_state = "quantum_sparks" + sound = 'sound/effects/magic.ogg' + + cooldown_time = 25 SECONDS + spell_requirements = NONE + + school = SCHOOL_UNSET + antimagic_flags = MAGIC_RESISTANCE + + cast_range = 4 + +/datum/action/cooldown/spell/pointed/mana_sense/is_valid_target(atom/movable/cast_on) + if (isturf(cast_on)) + return FALSE + if (!cast_on.mana_pool) + return FALSE + return TRUE + +/datum/action/cooldown/spell/pointed/mana_sense/before_cast(atom/movable/cast_on) + . = ..() + if(. & SPELL_CANCEL_CAST) + return . + var/mob/caster = usr || owner + if(DOING_INTERACTION(caster, REF(src))) + return . | SPELL_CANCEL_CAST + + if(!is_valid_target(cast_on)) + caster.balloon_alert(caster, "invalid target!") + return . | SPELL_CANCEL_CAST + + if(!do_after( // this was copied from soothe, feeling like keeping this here so lenses and mana sense both have reasons to use both + user = caster, + delay = 1 SECONDS, + target = cast_on, + timed_action_flags = IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM|IGNORE_SLOWDOWNS, + extra_checks = CALLBACK(src, PROC_REF(block_cast), caster, cast_on), \ + interaction_key = REF(src), \ + )) + . |= SPELL_CANCEL_CAST + + return . + +/datum/action/cooldown/spell/pointed/mana_sense/proc/block_cast(mob/living/caster, mob/living/cast_on) + if(QDELETED(src) || QDELETED(caster) || QDELETED(cast_on)) + return FALSE + if(!is_valid_target(cast_on)) + caster.balloon_alert(caster, "invalid target!") + return FALSE + if(get_dist(cast_on, caster) > cast_range) + caster.balloon_alert(caster, "out of range!") + return FALSE + + return TRUE + +/datum/action/cooldown/spell/pointed/mana_sense/cast(atom/movable/cast_on) + . = ..() + var/mob/caster = usr || owner + caster.balloon_alert(caster, "mana amount: [cast_on.mana_pool.amount]") diff --git a/maplestation_modules/code/modules/magic/story_spells/shock_touch.dm b/maplestation_modules/code/modules/magic/story_spells/shock_touch.dm index 7e8230f785d3..fa9248b6a7f9 100644 --- a/maplestation_modules/code/modules/magic/story_spells/shock_touch.dm +++ b/maplestation_modules/code/modules/magic/story_spells/shock_touch.dm @@ -1,13 +1,5 @@ -/datum/component/uses_mana/story_spell/touch/shock_touch - var/shock_touch_attunement_amount = 0.5 - var/shock_touch_cost = 50 - -/datum/component/uses_mana/story_spell/touch/shock_touch/get_attunement_dispositions() - . = ..() - .[/datum/attunement/electric] += shock_touch_attunement_amount - -/datum/component/uses_mana/story_spell/touch/shock_touch/get_mana_required(atom/caster, atom/cast_on, ...) - return ..() * shock_touch_cost +#define SHOCK_TOUCH_ATTUNEMENT_ELEC 0.5 +#define SHOCK_TOUCH_MANA_COST 50 // Magical shock touch can just subtype normal shock touch relatievly painlessly /datum/action/cooldown/spell/touch/shock/magical @@ -19,9 +11,21 @@ spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC antimagic_flags = MAGIC_RESISTANCE + var/shock_touch_cost = SHOCK_TOUCH_MANA_COST + /datum/action/cooldown/spell/touch/shock/magical/New(Target, original) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/touch/shock_touch) + + + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_ELECTRIC] += SHOCK_TOUCH_ATTUNEMENT_ELEC + + AddComponent(/datum/component/uses_mana/touch_spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + mana_required = shock_touch_cost, \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + attunements = attunements, \ + ) // Shock mutation needs to address people with magic shock touch /datum/mutation/human/shock @@ -35,8 +39,7 @@ return ..() to_chat(owner, span_notice("Your hands feel like they're buzzing with electricity.")) - var/datum/component/uses_mana/story_spell/touch/shock_touch/touch = magic_shock.GetComponent(/datum/component/uses_mana/story_spell/touch/shock_touch) - touch?.shock_touch_cost = 0 + magic_shock?.shock_touch_cost = 0 power_path = null /datum/mutation/human/shock/on_losing(mob/living/carbon/human/owner) @@ -49,5 +52,8 @@ return to_chat(owner, span_warning("Your hands feel numb once more.")) - var/datum/component/uses_mana/story_spell/touch/shock_touch/touch = magic_shock.GetComponent(/datum/component/uses_mana/story_spell/touch/shock_touch) - touch?.shock_touch_cost = initial(touch.shock_touch_cost) + + magic_shock?.shock_touch_cost = initial(magic_shock.shock_touch_cost) + +#undef SHOCK_TOUCH_ATTUNEMENT_ELEC +#undef SHOCK_TOUCH_MANA_COST diff --git a/maplestation_modules/code/modules/magic/story_spells/soothe.dm b/maplestation_modules/code/modules/magic/story_spells/soothe.dm index 4948f735661c..c6d18edc92e2 100644 --- a/maplestation_modules/code/modules/magic/story_spells/soothe.dm +++ b/maplestation_modules/code/modules/magic/story_spells/soothe.dm @@ -1,16 +1,5 @@ -/datum/component/uses_mana/story_spell/pointed/soothe - var/soothe_attunement_amount = 0.5 - var/soothe_cost = 20 - -/datum/component/uses_mana/story_spell/pointed/soothe/get_attunement_dispositions() - . = ..() - .[/datum/attunement/life] += soothe_attunement_amount - -/datum/component/uses_mana/story_spell/pointed/soothe/get_mana_required(atom/caster, mob/living/cast_on, ...) - var/final_cost = ..() * soothe_cost - if(!isnull(cast_on.mind)) - final_cost *= 2 // costs more on other players because pacifism is kind of annoying... - return final_cost +#define SOOTHE_ATTUNEMENT_LIFE 0.5 +#define SOOTHE_MANA_COST 20 // Calm Emotions / Soothe, basically just applied pacifism after a short do_after. Can be resisted. /datum/action/cooldown/spell/pointed/soothe_target @@ -24,6 +13,7 @@ cooldown_time = 2 MINUTES spell_requirements = NONE + var/mana_cost = SOOTHE_MANA_COST school = SCHOOL_PSYCHIC antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_MIND @@ -37,7 +27,23 @@ /datum/action/cooldown/spell/pointed/soothe_target/New(Target) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/pointed/soothe) + + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[MAGIC_ELEMENT_LIFE] += SOOTHE_ATTUNEMENT_LIFE + + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = CALLBACK(src, PROC_REF(get_mana_consumed)), \ + attunements = attunements, \ + ) + +/datum/action/cooldown/spell/pointed/soothe_target/proc/get_mana_consumed(atom/caster, datum/spell, atom/cast_on) + var/final_cost = mana_cost + var/mob/living/living_cast_on = cast_on + if(!isnull(living_cast_on.mind)) + final_cost *= 2 // costs more on other players because pacifism is kind of annoying... + return final_cost /datum/action/cooldown/spell/pointed/soothe_target/is_valid_target(atom/cast_on) return isliving(cast_on) && (cast_on != owner) @@ -224,3 +230,6 @@ /datum/mood_event/soothed description = "To err is human..." mood_change = 25 + +#undef SOOTHE_ATTUNEMENT_LIFE +#undef SOOTHE_MANA_COST diff --git a/maplestation_modules/code/modules/magic/story_spells/thaumatergic_sense.dm b/maplestation_modules/code/modules/magic/story_spells/thaumatergic_sense.dm index 041be9c51aa8..6b433027f61c 100644 --- a/maplestation_modules/code/modules/magic/story_spells/thaumatergic_sense.dm +++ b/maplestation_modules/code/modules/magic/story_spells/thaumatergic_sense.dm @@ -1,4 +1,4 @@ -/*// doesnt use or catalyse magic, just senses +/* // doesnt use or catalyse magic, just senses /datum/action/cooldown/spell/thaumatergic_sense /// How much we can discern mana pools from one another, and their contents. /// 0: Default. We return the average mana and attunements of all pools. @@ -36,7 +36,7 @@ var/list/datum/mana_pool/mana_pools = owner.get_available_mana() if (mana_pools.len == 0) - if (feedback) + if (feedback) // error: "feedback" undefined to_chat(owner, span_warning("You sense no accessable magic in range...")) return FALSE var/mana = get_raw_mana_of_pools(mana_pools) @@ -46,7 +46,7 @@ var/list/datum/attunement/average_attunements = list() for (var/datum/mana_pool/pool as anything in mana_pools) for (var/datum/attunement/iterated_attunement as anything in pool.attunements) - average_attunements[iterated_attunement] += rand(pool.attunements[iterated_attunement]*attunement_precision, pool.attunements[iterated_attunement]*SAVE_DIVIDE(1, attunement_precision)) + average_attunements[iterated_attunement] += rand(pool.attunements[iterated_attunement]*attunement_precision, pool.attunements[iterated_attunement]*SAFE_DIVIDE(1, attunement_precision)) var/average_attunements_string = "" for (var/datum/attunement/iterated_attunement as anything in average_attunements) average_attunements[iterated_attunement] /= mana_pools.len @@ -71,9 +71,7 @@ . = list() - var/should_differentiate_pools_at_all = (pool_discernment != THAUMATERGIC_SENSE_POOL_DISCERNMENT_LEVEL_ZERO) + var/should_differentiate_pools_at_all = (pool_discernment != THAUMATERGIC_SENSE_POOL_DISCERNMENT_LEVEL_ZERO) // pool discernment undefined on datum .["should_differentiate_pools_at_all"] = should_differentiate_pools_at_all - - - */ + diff --git a/maplestation_modules/code/modules/magic/story_spells/water_control.dm b/maplestation_modules/code/modules/magic/story_spells/water_control.dm index 6400d4b01229..3e71ecc098bc 100644 --- a/maplestation_modules/code/modules/magic/story_spells/water_control.dm +++ b/maplestation_modules/code/modules/magic/story_spells/water_control.dm @@ -1,19 +1,5 @@ -/datum/component/uses_mana/story_spell/pointed/soft_and_wet - var/wet_attunement_amount = 0.5 - var/wet_cost_per_unit = 0.4 - -/datum/component/uses_mana/story_spell/pointed/soft_and_wet/get_attunement_dispositions() - . = ..() - .[/datum/attunement/water] += wet_attunement_amount - -/datum/component/uses_mana/story_spell/pointed/soft_and_wet/get_mana_required(atom/caster, atom/cast_on, ...) - var/datum/action/cooldown/spell/pointed/soft_and_wet/spell = parent - var/turf/open/cast_turf = get_turf(cast_on) - if(SEND_SIGNAL(cast_turf, COMSIG_TURF_IS_WET) || spell.wetness_pool.total_volume >= spell.wetness_pool.maximum_volume) - return ..() * spell.wetness_pool.maximum_volume * wet_cost_per_unit - - // Supplying water makes it cheaper, technically. - return ..() * spell.wetness_pool.total_volume * wet_cost_per_unit +#define WET_ATTUNEMENT_WATER 0.5 +#define WET_MANA_COST_PER_UNIT 0.4 /datum/action/cooldown/spell/pointed/soft_and_wet name = "Water Control" @@ -26,6 +12,7 @@ cooldown_time = 10 SECONDS spell_requirements = NONE + var/wet_cost_per_unit = WET_MANA_COST_PER_UNIT school = SCHOOL_TRANSMUTATION @@ -45,10 +32,27 @@ /datum/action/cooldown/spell/pointed/soft_and_wet/New(Target) . = ..() - AddComponent(/datum/component/uses_mana/story_spell/pointed/soft_and_wet) + + var/list/datum/attunement/attunements = GLOB.default_attunements.Copy() + attunements[/datum/attunement/water] += WET_ATTUNEMENT_WATER + + AddComponent(/datum/component/uses_mana/spell, \ + activate_check_failure_callback = CALLBACK(src, PROC_REF(spell_cannot_activate)), \ + get_user_callback = CALLBACK(src, PROC_REF(get_owner)), \ + mana_required = CALLBACK(src, PROC_REF(get_mana_consumed)), \ + attunements = attunements, \ + ) wetness_pool = new(water_units_applied * ((1 + 2 * aoe_range) ** 2)) wetness_pool.add_reagent(water_type, INFINITY) +/datum/action/cooldown/spell/pointed/soft_and_wet/proc/get_mana_consumed(atom/caster, datum/spell, atom/cast_on) + var/turf/open/cast_turf = get_turf(cast_on) + if(SEND_SIGNAL(cast_turf, COMSIG_TURF_IS_WET) || wetness_pool.total_volume >= wetness_pool.maximum_volume) + return wetness_pool.maximum_volume * wet_cost_per_unit + + // Supplying water makes it cheaper, technically. + return wetness_pool.total_volume * wet_cost_per_unit + /datum/action/cooldown/spell/pointed/soft_and_wet/Destroy() QDEL_NULL(wetness_pool) return ..() @@ -167,3 +171,6 @@ . = ..() if(istype(used)) color = mix_color_from_reagents(used.reagent_list) + +#undef WET_ATTUNEMENT_WATER +#undef WET_MANA_COST_PER_UNIT diff --git a/maplestation_modules/code/modules/magic/subsystems/magic_subsystem.dm b/maplestation_modules/code/modules/magic/subsystems/magic_subsystem.dm new file mode 100644 index 000000000000..41867473f0cf --- /dev/null +++ b/maplestation_modules/code/modules/magic/subsystems/magic_subsystem.dm @@ -0,0 +1,13 @@ +PROCESSING_SUBSYSTEM_DEF(magic) + name = "Magic" + flags = SS_BACKGROUND|SS_POST_FIRE_TIMING + + wait = MAGIC_SUBSYSTEM_FIRE_RATE + priority = FIRE_PRIORITY_MAGIC + +/datum/controller/subsystem/processing/magic/Initialize() + . = ..() + + generate_initial_leylines() + + return SS_INIT_SUCCESS diff --git a/maplestation_modules/icons/mob/clothing/neck.dmi b/maplestation_modules/icons/mob/clothing/neck.dmi new file mode 100644 index 000000000000..e38868489cc2 Binary files /dev/null and b/maplestation_modules/icons/mob/clothing/neck.dmi differ diff --git a/maplestation_modules/icons/obj/devices.dmi b/maplestation_modules/icons/obj/devices.dmi index f40e0fd6e346..5e6c1ac234fc 100644 Binary files a/maplestation_modules/icons/obj/devices.dmi and b/maplestation_modules/icons/obj/devices.dmi differ diff --git a/maplestation_modules/icons/obj/magic/crystals.dmi b/maplestation_modules/icons/obj/magic/crystals.dmi new file mode 100644 index 000000000000..60a82f0428fe Binary files /dev/null and b/maplestation_modules/icons/obj/magic/crystals.dmi differ