From 5a500257e83988b693cb66ae21d4e2dbb830ad45 Mon Sep 17 00:00:00 2001 From: Rhials <28870487+Rhials@users.noreply.github.com> Date: Fri, 2 May 2025 20:43:01 -0400 Subject: [PATCH] Improves the Dice Servant role candidacy process, and some other code tweaks (#90952) ## About The Pull Request This improves the code surrounding the Dice Servant, as well as the summoning behavior, to make it WAY more intuitive. "But what IS a Dice Servant?" well... If you roll a 16 on a Die of Fate, you get a unique "Dice Servant" spell that lets you summon a butler from wherever they may be. The issue is that the role itself is polled for a mere 5 seconds, and if nobody signs up for it, the role is never offered again and the caster gets no servant. Now, the spell will attempt to gather candidates, and if it fails, it will continue trying to do so upon your next cast. This also makes it so the spell can be applied to players through VV or other unimplemented means. The code has been cleaned up a bit, and the "dice servant" spell has been converted into a generic "servant" role. The Dice Servant, however, retains the old role name (and now plays a service bell "ding!" when you summon your butler!) There are some other, very minor changes like reducing the smoke from each summon, or adding an apostrophe to the incantation (I think that's all). ## Why It's Good For The Game Rolling a Die of Fate (RARE), and landing a 16 (1/20), and having someone scoop up the ghost role in 5 seconds or less (I hope pop is above 50) has made this role a very uncommon sight. I saw it for the first time about a week ago. It's been in the game for a very very long time. It's a cool, unique role that should be much more accessible and intuitive than it currently is. The code improvements also lay a great groundwork for other mob summon spells to be made off of. ## Changelog :cl: Rhials code: The Dice Servant role will now re-poll itself until a candidate is found. /:cl: --- code/game/objects/items/dice.dm | 68 +---------- .../spells/spell_types/summon/servant.dm | 106 ++++++++++++++++++ tgstation.dme | 1 + 3 files changed, 109 insertions(+), 66 deletions(-) create mode 100644 code/modules/spells/spell_types/summon/servant.dm diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm index 59d264e0210..0cb90bdfa41 100644 --- a/code/game/objects/items/dice.dm +++ b/code/game/objects/items/dice.dm @@ -419,25 +419,9 @@ new /obj/item/book/granter/action/spell/random(drop_location()) if(16) //Servant & Servant Summon - selected_turf.visible_message(span_userdanger("A Dice Servant appears in a cloud of smoke!")) - var/mob/living/carbon/human/human_servant = new(drop_location()) - do_smoke(0, holder = src, location = drop_location()) - - var/mob/chosen_one = SSpolling.poll_ghosts_for_target("Do you want to play as [span_danger("[user.real_name]'s")] [span_notice("Servant")]?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 5 SECONDS, checked_target = human_servant, alert_pic = user, role_name_text = "dice servant") - if(chosen_one) - message_admins("[ADMIN_LOOKUPFLW(chosen_one)] was spawned as Dice Servant") - human_servant.PossessByPlayer(chosen_one.key) - - human_servant.equipOutfit(/datum/outfit/butler) - var/datum/mind/servant_mind = new /datum/mind() - var/datum/antagonist/magic_servant/servant_antagonist = new - servant_mind.transfer_to(human_servant) - servant_antagonist.setup_master(user) - servant_mind.add_antag_datum(servant_antagonist) - - var/datum/action/cooldown/spell/summon_mob/summon_servant = new(user.mind || user, human_servant) + selected_turf.visible_message(span_userdanger("[user] ripples with newfound magical power!")) + var/datum/action/cooldown/spell/summon_mob/dice/summon_servant = new(user.mind || user) summon_servant.Grant(user) - if(17) //Tator Kit selected_turf.visible_message(span_userdanger("A suspicious box appears!")) @@ -459,52 +443,4 @@ selected_turf.visible_message(span_userdanger("Magic flows out of [src] and into [user]!")) user.mind.make_wizard() -/datum/outfit/butler - name = "Butler" - uniform = /obj/item/clothing/under/suit/black_really - neck = /obj/item/clothing/neck/tie/red/tied - shoes = /obj/item/clothing/shoes/laceup - head = /obj/item/clothing/head/hats/bowler - glasses = /obj/item/clothing/glasses/monocle - gloves = /obj/item/clothing/gloves/color/white - -/datum/action/cooldown/spell/summon_mob - name = "Summon Servant" - desc = "This spell can be used to call your servant, whenever you need it." - button_icon_state = "summons" - - school = SCHOOL_CONJURATION - cooldown_time = 10 SECONDS - - invocation = "JE VES?" - invocation_type = INVOCATION_WHISPER - spell_requirements = NONE - spell_max_level = 0 //cannot be improved - - smoke_type = /datum/effect_system/fluid_spread/smoke - smoke_amt = 2 - - var/datum/weakref/summon_weakref - -/datum/action/cooldown/spell/summon_mob/New(Target, mob/living/summoned_mob) - . = ..() - if(summoned_mob) - summon_weakref = WEAKREF(summoned_mob) - -/datum/action/cooldown/spell/summon_mob/cast(atom/cast_on) - . = ..() - var/mob/living/to_summon = summon_weakref?.resolve() - if(QDELETED(to_summon)) - to_chat(cast_on, span_warning("You can't seem to summon your servant - it seems they've vanished from reality, or never existed in the first place...")) - return - - do_teleport( - to_summon, - get_turf(cast_on), - precision = 1, - asoundin = 'sound/effects/magic/wand_teleport.ogg', - asoundout = 'sound/effects/magic/wand_teleport.ogg', - channel = TELEPORT_CHANNEL_MAGIC, - ) - #undef MIN_SIDES_ALERT diff --git a/code/modules/spells/spell_types/summon/servant.dm b/code/modules/spells/spell_types/summon/servant.dm new file mode 100644 index 00000000000..63f30e913a2 --- /dev/null +++ b/code/modules/spells/spell_types/summon/servant.dm @@ -0,0 +1,106 @@ + +/datum/action/cooldown/spell/summon_mob + name = "Summon Servant" + desc = "This spell can be used to call your servant, whenever you need it." + button_icon_state = "summons" + + school = SCHOOL_CONJURATION + cooldown_time = 10 SECONDS + + invocation = "JE'VES?" + + invocation_type = INVOCATION_WHISPER + spell_requirements = NONE + spell_max_level = 0 //cannot be improved + + smoke_type = /datum/effect_system/fluid_spread/smoke + smoke_amt = 0 + + ///Weakref to the summonable mob. + var/datum/weakref/summon_weakref + ///Are we already summoning a mob? + var/summoning_servant = FALSE + ///Have we selected a mob to be our servant? + var/selected_summon = FALSE + ///What do we call our servant? + var/servant_title = "Servant" + +/datum/action/cooldown/spell/summon_mob/Grant(mob/grant_to) + . = ..() + owner.balloon_alert(owner, "conjuring a new [servant_title]...") + find_servant() + +/datum/action/cooldown/spell/summon_mob/before_cast(mob/living/invoker, feedback) + . = ..() + if(!selected_summon) + if(summoning_servant) + owner.balloon_alert(owner, "still conjuring!") + return SPELL_CANCEL_CAST + owner.balloon_alert(owner, "conjuring [servant_title]...") + find_servant() + return SPELL_CANCEL_CAST + + var/mob/living/to_summon = summon_weakref?.resolve() + + if(QDELETED(to_summon)) + to_chat(owner, span_warning("You can't seem to summon your [servant_title] - it seems they've vanished from reality, or never existed in the first place...")) + return SPELL_CANCEL_CAST + +/datum/action/cooldown/spell/summon_mob/cast() + . = ..() + + var/mob/living/to_summon = summon_weakref?.resolve() + + to_summon.visible_message(span_alert("[to_summon] suddenly vanishes into thin air!"), span_alert("You have been summoned to serve!"), span_hear("You hear something teleport away from nearby, off to serve...")) + + do_teleport( + to_summon, + get_turf(owner), + precision = 1, + asoundin = 'sound/effects/magic/wand_teleport.ogg', + asoundout = 'sound/effects/magic/wand_teleport.ogg', + channel = TELEPORT_CHANNEL_MAGIC, + ) + +/datum/action/cooldown/spell/summon_mob/proc/find_servant() + summoning_servant = TRUE //If we find a candidate, this stays true and locks in the summoned servant. + var/list/candidate_list = SSpolling.poll_ghost_candidates("Do you want to play as [span_danger("[owner.real_name]'s")] [span_notice("[servant_title]")]?", check_jobban = ROLE_WIZARD, role = ROLE_WIZARD, poll_time = 15 SECONDS, alert_pic = owner, role_name_text = "[servant_title]") + if(!length(candidate_list)) + summoning_servant = FALSE + return + + var/mob/chosen_one = pick(candidate_list) + if(!chosen_one) //Just in case! + summoning_servant = FALSE + return + + message_admins("[ADMIN_LOOKUPFLW(chosen_one)] was spawned as a Magical Servant ([servant_title])") + var/turf/spawn_location = get_turf(owner) + spawn_location.visible_message(span_userdanger("A Magical [servant_title] appears in a cloud of smoke!")) + var/mob/living/carbon/human/human_servant = new(spawn_location) + human_servant.equipOutfit(/datum/outfit/butler) + do_smoke(0, holder = src, location = spawn_location) + human_servant.PossessByPlayer(chosen_one.key) + summon_weakref = WEAKREF(human_servant) + + var/datum/mind/servant_mind = new /datum/mind() + var/datum/antagonist/magic_servant/servant_antagonist = new + + servant_mind.transfer_to(human_servant) + servant_antagonist.setup_master(owner) + servant_mind.add_antag_datum(servant_antagonist) + selected_summon = TRUE + +/datum/action/cooldown/spell/summon_mob/dice + name = "Summon Dice Servant" + sound = 'sound/machines/microwave/microwave-end.ogg' + servant_title = "Dice Servant" + +/datum/outfit/butler + name = "Butler" + uniform = /obj/item/clothing/under/suit/black_really + neck = /obj/item/clothing/neck/tie/red/tied + shoes = /obj/item/clothing/shoes/laceup + head = /obj/item/clothing/head/hats/bowler + glasses = /obj/item/clothing/glasses/monocle + gloves = /obj/item/clothing/gloves/color/white diff --git a/tgstation.dme b/tgstation.dme index 8b8e7b2b427..ce6faac8792 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -6182,6 +6182,7 @@ #include "code\modules\spells\spell_types\shapeshift\polar_bear.dm" #include "code\modules\spells\spell_types\shapeshift\shapechange.dm" #include "code\modules\spells\spell_types\shapeshift\wolf.dm" +#include "code\modules\spells\spell_types\summon\servant.dm" #include "code\modules\spells\spell_types\teleport\_teleport.dm" #include "code\modules\spells\spell_types\teleport\blink.dm" #include "code\modules\spells\spell_types\teleport\teleport.dm"