From 429a3bbf048daec952af8547b5f4652edbc0f6da Mon Sep 17 00:00:00 2001 From: Y0SH1M4S73R Date: Fri, 16 May 2025 22:49:23 -0400 Subject: [PATCH] Adds the craftable MOD soul shard core. (#91023) ## About The Pull Request If you examine a soul shard closely, you can learn to craft the MOD soul shard core. It takes the same basic ingredients as other MOD cores, but uses a soul shard. It is powered by your own soul, which drains your sanity to provide charge (if mood is disabled in the config, it's practically equivalent to an infinite core). Crafting one ejects any shades in the shard (because that's the simplest behavior that doesn't require a substantial refactor to crafting or modsuit code). Depending on the kind of shard used, certain roles get negatively impacted by using a modsuit with such a core. - Cult soul shards wrack non-magical roles (anyone that isn't a cultist, heretic, heretic minion, or wizard) with horrible agony (a -20 moodlet that expires 10 seconds after turning the suit off) - Holy soul shards wrack cultists and heretics with horrible agony, while wizards are put at unease (-3 mood) - Wizard soul shards have no negative impact on anyone - If someone were to VV a soul shard to have the heretic theme (only used by rusted harvesters), it would be functionally identical to the cult shard Soulless mobs (liches, people who lost with the cursed russian revolver, and people who died from casting Soul Tap too many times) have no soul for the core to draw power from. Also gives the wizard modsuit a soul core. ## Why It's Good For The Game Provides a neat but caveat-rich way to utilize soul shards, especially for players who have no way to obtain construct shells absent a cult or wizard to create them. --- code/datums/components/crafting/robot.dm | 15 ++ .../greyscale_configs/greyscale_items.dm | 5 + .../greyscale/json_configs/mod_core_soul.json | 15 ++ .../antagonists/wizard/equipment/soulstone.dm | 42 +++++ code/modules/mod/mod_core.dm | 165 ++++++++++++++++++ code/modules/mod/mod_types.dm | 2 +- .../obj/clothing/modsuit/mod_construction.dmi | Bin 5423 -> 5796 bytes 7 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 code/datums/greyscale/json_configs/mod_core_soul.json diff --git a/code/datums/components/crafting/robot.dm b/code/datums/components/crafting/robot.dm index 8a6a014d5a4..94c1aa979d1 100644 --- a/code/datums/components/crafting/robot.dm +++ b/code/datums/components/crafting/robot.dm @@ -201,3 +201,18 @@ /obj/item/reagent_containers/syringe = 1, ) category = CAT_ROBOT + +/datum/crafting_recipe/mod_core_soul + name = "MOD core (Soul)" + result = /obj/item/mod/core/soul + tool_behaviors = list(TOOL_SCREWDRIVER) + time = 10 SECONDS + reqs = list( + /obj/item/stack/cable_coil = 5, + /obj/item/stack/rods = 2, + /obj/item/stack/sheet/glass = 1, + /obj/item/soulstone = 1, + ) + parts = list(/obj/item/soulstone = 1) + category = CAT_ROBOT + crafting_flags = parent_type::crafting_flags | CRAFT_MUST_BE_LEARNED diff --git a/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm b/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm index 62f30855abb..1c7fd0523b0 100644 --- a/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm +++ b/code/datums/greyscale/config_types/greyscale_configs/greyscale_items.dm @@ -355,3 +355,8 @@ name = "Piggy Bank" icon_file = 'icons/obj/fluff/general.dmi' json_config = 'code/datums/greyscale/json_configs/piggy_bank.json' + +/datum/greyscale_config/mod_core_soul + name = "MOD Soul Core" + icon_file = 'icons/obj/clothing/modsuit/mod_construction.dmi' + json_config = 'code/datums/greyscale/json_configs/mod_core_soul.json' diff --git a/code/datums/greyscale/json_configs/mod_core_soul.json b/code/datums/greyscale/json_configs/mod_core_soul.json new file mode 100644 index 00000000000..fbdd965ac6c --- /dev/null +++ b/code/datums/greyscale/json_configs/mod_core_soul.json @@ -0,0 +1,15 @@ +{ + "mod-core-soul": [ + { + "type": "icon_state", + "icon_state": "mod-core-soul-base", + "blend_mode": "overlay" + }, + { + "type": "icon_state", + "icon_state": "mod-core-soul-shard", + "blend_mode": "overlay", + "color_ids": [ 1 ] + } + ] +} diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 6c31a2fcab5..a828415a1d9 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -152,6 +152,48 @@ if(spent) . += span_cult("This shard is spent; it is now just a creepy rock.") +/obj/item/soulstone/examine_more(mob/user) + . = ..() + if(!isliving(user) || isnull(user.mind)) + return + if(!user.mind.has_crafting_recipe(/datum/crafting_recipe/mod_core_soul)) + . += span_notice("You know... there might be alternate uses for something like this.") + +/obj/item/soulstone/Topic(href, list/href_list) + . = ..() + + if(href_list["learn_soul_core_recipe"]) + learn_soul_core_recipe(usr) + +/obj/item/soulstone/proc/learn_soul_core_recipe(mob/user) + if(user.mind?.has_crafting_recipe(/datum/crafting_recipe/mod_core_soul)) + return + if(!soul_core_learning_check(user)) + return + var/list/remarks = list( + "You begin brainstorming...", + "Are constructs powered by souls?", + "Then wouldn't that mean...", + "Can that energy be turned into electricity?", + "You have an idea...", + ) + for(var/remark in remarks) + to_chat(user, span_notice("[remark]")) + if(!do_after( + user, + 5 SECONDS, + timed_action_flags = IGNORE_USER_LOC_CHANGE | IGNORE_HELD_ITEM, + extra_checks = CALLBACK(src, PROC_REF(soul_core_learning_check), user), + interaction_key = "soul_core_learn", + max_interact_count = 1 + )) + return + user.mind?.teach_crafting_recipe(/datum/crafting_recipe/mod_core_soul) + to_chat(user, span_notice("You learned to craft [/obj/item/mod/core/soul::name].")) + +/obj/item/soulstone/proc/soul_core_learning_check(mob/user) + return user.is_holding(src) || (user.loc == loc) || (isturf(loc) && user.Adjacent(loc)) + /obj/item/soulstone/Destroy() //Stops the shade from being qdel'd immediately and their ghost being sent back to the arrival shuttle. for(var/mob/living/basic/shade/shade in src) INVOKE_ASYNC(shade, TYPE_PROC_REF(/mob/living, death)) diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm index 01e0902b6b0..657b6838394 100644 --- a/code/modules/mod/mod_core.dm +++ b/code/modules/mod/mod_core.dm @@ -509,3 +509,168 @@ var/flower_boots = new chosen_type(get_turf(mod.wearer)) animate(flower_boots, alpha = 0, 1 SECONDS) QDEL_IN(flower_boots, 1 SECONDS) + +/obj/item/mod/core/soul + name = "MOD soul shard core" + desc = "A soul shard haphazardly jammed into a hand-crafted MOD core frame." + icon_state = "mod-core-soul" + icon_state_preview = "mod-core-soul-preview" + var/base_desc + var/theme = THEME_CULT + greyscale_config = /datum/greyscale_config/mod_core_soul + greyscale_colors = "#ff0000" + +/obj/item/mod/core/soul/Initialize(mapload) + . = ..() + base_desc = desc + update_appearance(UPDATE_DESC) + +/obj/item/mod/core/soul/update_desc(updates) + . = ..() + desc = base_desc + switch(theme) + if(THEME_CULT) + desc += " You can feel unholy energies trying to tear something away from you." + if(THEME_HOLY) + desc += " It emanates a divine aura that defies souls tainted by darker forces." + if(THEME_WIZARD) + desc += " Yet another foray by the Wizard Federation into the dangerous field of soul magic." + if(THEME_HERETIC) + desc += " The surface of the shard shines with glimpses of things which never were, yet have always been." + else + desc += " Or at least, that's what it should be. Somebody must have set a variable incorrectly." + +/obj/item/mod/core/soul/update_greyscale() + switch(theme) + if(THEME_CULT) + greyscale_colors = "#ff0000" + if(THEME_HOLY) + greyscale_colors = "#0000ff" + if(THEME_WIZARD) + greyscale_colors = "#ff00ff" + if(THEME_HERETIC) + greyscale_colors = "#00ff00" + return ..() + +/obj/item/mod/core/soul/CheckParts(list/parts_list, datum/crafting_recipe/current_recipe) + var/obj/item/soulstone/stone = locate() in parts_list + set_theme(stone.theme) + for(var/mob/living/basic/shade/shade in stone) + shade.forceMove(get_turf(src)) + shade.visible_message(span_warning("[shade] is ejected from [stone] as it is inserted into [src]!"), span_warning("You are ejected from [stone] as it is inserted into [src]!")) + parts_list -= stone + qdel(stone) + return ..() + +/obj/item/mod/core/soul/proc/set_theme(new_theme) + theme = new_theme + update_appearance(UPDATE_DESC) + update_greyscale() + +/obj/item/mod/core/soul/charge_source() + return CONFIG_GET(flag/disable_human_mood) ? src : mod.wearer?.mob_mood + +/obj/item/mod/core/soul/max_charge_amount() + return CONFIG_GET(flag/disable_human_mood) ? INFINITY : SANITY_MAXIMUM + +/obj/item/mod/core/soul/charge_amount() + var/mob/living/wearer = mod.wearer + if(!wearer) + return 0 + if(HAS_TRAIT(wearer, TRAIT_NO_SOUL)) + return 0 // Can't draw from something that isn't there. + if(CONFIG_GET(flag/disable_human_mood)) + return INFINITY + var/datum/mood/source = charge_source() + return source?.sanity + +/obj/item/mod/core/soul/check_charge(amount) + if(CONFIG_GET(flag/disable_human_mood)) + return !!mod.wearer + return charge_amount() >= amount * 10 / STANDARD_CELL_CHARGE + +/obj/item/mod/core/soul/subtract_charge(amount) + var/mob/living/wearer = mod.wearer + if(CONFIG_GET(flag/disable_human_mood)) + return !!wearer + var/datum/mood/source = charge_source() + source.adjust_sanity(-amount * 10 / STANDARD_CELL_CHARGE) + var/backlash_type = get_backlash_type(wearer) + if(backlash_type) + wearer.add_mood_event("soul_core", backlash_type) + else + wearer.add_mood_event("soul_core", /datum/mood_event/soul_core_warning) + return TRUE + +/obj/item/mod/core/soul/get_chargebar_string() + var/mob/living/wearer = mod.wearer + if(wearer || HAS_TRAIT(wearer, TRAIT_NO_SOUL)) + return "No power source detected." + if(CONFIG_GET(flag/disable_human_mood)) + return "Infinite" + return "[round(charge_amount() / max_charge_amount() * 100, 0.1)]%" + +/obj/item/mod/core/soul/get_chargebar_color() + switch(theme) + if(THEME_CULT) + return "red" + if(THEME_HOLY) + return "blue" + if(THEME_WIZARD) + return "purple" + if(THEME_HERETIC) + return "green" + +/obj/item/mod/core/soul/proc/get_backlash_type(mob/living/checked) + switch(theme) + if(THEME_CULT) + if(!(IS_CULTIST(checked) || IS_HERETIC(checked) || HAS_MIND_TRAIT(checked, TRAIT_MAGICALLY_GIFTED))) + return /datum/mood_event/soul_core_torment + if(THEME_HERETIC) + if(!(IS_CULTIST(checked) || IS_HERETIC(checked) || HAS_MIND_TRAIT(checked, TRAIT_MAGICALLY_GIFTED))) + return /datum/mood_event/soul_core_torment/heretic + if(THEME_HOLY) + if(IS_CULTIST(checked) || IS_HERETIC(checked)) + return /datum/mood_event/soul_core_torment + if(IS_WIZARD(checked)) + return /datum/mood_event/soul_core_discomfort + +/obj/item/mod/core/soul/get_charge_icon_state() + switch(round(charge_amount() / max_charge_amount(), 0.01)) + if(0.75 to INFINITY) + return "high" + if(0.5 to 0.75) + return "mid" + if(0.25 to 0.5) + return "low" + if(0.02 to 0.25) + return "very_low" + + return "empty" + +/obj/item/mod/core/soul/vv_edit_var(vname, vval) + . = ..() + if(vname == NAMEOF(src, theme)) + update_appearance(UPDATE_DESC) + update_greyscale() + +/datum/mood_event/soul_core_torment + description = "IT BURNS!! IT BURNS!! THE DEEPEST DEPTHS OF MY BEING!! IT BURNS!!" + mood_change = -20 + timeout = 10 SECONDS + +/datum/mood_event/soul_core_torment/heretic + description = "GET OUT OF MY HEAD GET OUT OF MY HEAD GET OUT OF MY HEAD!!" + +/datum/mood_event/soul_core_discomfort + description = "I'm no fan of these divine powers breathing down my neck." + mood_change = -3 + timeout = 10 SECONDS + +/datum/mood_event/soul_core_warning + description = "I can feel my modsuit siphoning my energy. I'd better keep my spirits high." + mood_change = 0 + timeout = 10 SECONDS + +/obj/item/mod/core/soul/wizard + theme = THEME_WIZARD diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm index 08f104a95a0..1b442ed9619 100644 --- a/code/modules/mod/mod_types.dm +++ b/code/modules/mod/mod_types.dm @@ -390,7 +390,7 @@ /obj/item/mod/control/pre_equipped/enchanted theme = /datum/mod_theme/enchanted starting_frequency = null - applied_core = /obj/item/mod/core/infinite + applied_core = /obj/item/mod/core/soul/wizard applied_modules = list( /obj/item/mod/module/storage/large_capacity, /obj/item/mod/module/energy_shield/wizard, diff --git a/icons/obj/clothing/modsuit/mod_construction.dmi b/icons/obj/clothing/modsuit/mod_construction.dmi index 1080b89650772e4847282d77958ff597c900c7e0..b2b5c1f5ceb906d750822923df76467b80cbdebc 100644 GIT binary patch delta 5426 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 Q|2YSov#>LNd&Vp7--2k(<^TWy