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.
This commit is contained in:
Y0SH1M4S73R
2025-05-16 22:49:23 -04:00
committed by GitHub
parent 27f9159919
commit 429a3bbf04
7 changed files with 243 additions and 1 deletions
+15
View File
@@ -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
@@ -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'
@@ -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 ]
}
]
}
@@ -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 <a href='byond://?src=[REF(src)];learn_soul_core_recipe=1'>alternate uses</a> 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 <i>powered</i> 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))
+165
View File
@@ -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. <i>Somebody</i> 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
+1 -1
View File
@@ -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,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB