Festival Sect Expansion: New Tunes, New Rites, Cogitandi Fidis (#80032)
## About The Pull Request ### Cogitandi Fidis Creates a holy violin that gives an analysis of songs played on it. This lets you check how long the song is for final effects, to make sure they trigger without the hassle of trying a rite with a song until you get one that works. 20 Favor, looks neat too! ### Portable Song Tuning Rite to empower an instrument with the ability to invoke specifically song tuning rites, 5 charges before you need to go back to a proper altar. Helps a chaplain stay on the move, since songs are interruptable and sometimes an altar can be incredibly far. 10 Favor for 5 charges. ### Illuminating Solo  New free invocation that lets you light up the way with your music! The finishing effect applies a weaker glow to all listeners for a minute. ### Other Misc Changes The threshold for getting a final effect on a song is now lower, 220 -> 170. As a reminder, this threshold is lines * tempo. I think the threshold was simply too high ## Why It's Good For The Game Changes were made from https://tgstation13.org/phpBB/viewtopic.php?f=10&t=35381 statistics and feedback about sects. I think, overall, Festival Sect is in a very good place and doesn't need a whole lot but a few rites that help with a quality of life would really cut down on the amount of time a chappy is spending doing nothing but running allllll the way back to the chapel to load up some new songs ## Changelog 🆑 add: Festival Sect has 3 new rites: Cogitandi Fidis, Portable Song Tuning, and Illuminating Solo. balance: lowers threshold for triggering a final effect. Consult your Cogitandi Fidis for more information /🆑
@@ -1,6 +1,6 @@
|
||||
// /datum/song signals
|
||||
|
||||
///sent to the instrument when a song starts playing
|
||||
///sent to the instrument when a song starts playing: (datum/starting_song, atom/player)
|
||||
#define COMSIG_INSTRUMENT_START "instrument_start"
|
||||
///sent to the instrument when a song stops playing
|
||||
#define COMSIG_INSTRUMENT_END "instrument_end"
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#define ALIGNMENT_NEUT "neutral"
|
||||
#define ALIGNMENT_EVIL "evil"
|
||||
|
||||
///how many lines multiplied by tempo should at least be higher than this. Makes people have to choose a long enough song to get the final effect.
|
||||
#define FESTIVAL_SONG_LONG_ENOUGH 170
|
||||
|
||||
/// the probability, when not overridden by sects, for a bible's bless effect to trigger on a smack
|
||||
#define DEFAULT_SMACK_CHANCE 60
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
*/
|
||||
/datum/component/religious_tool
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
dupe_mode = COMPONENT_DUPE_HIGHLANDER
|
||||
/// Enables access to the global sect directly
|
||||
var/datum/religion_sect/easy_access_sect
|
||||
/// Prevents double selecting sects
|
||||
@@ -14,19 +14,32 @@
|
||||
/// The rite currently being invoked
|
||||
var/datum/religion_rites/performing_rite
|
||||
///Sets the type for catalyst
|
||||
var/catalyst_type = /obj/item/book/bible
|
||||
var/obj/item/catalyst_type = /obj/item/book/bible
|
||||
///Enables overide of COMPONENT_NO_AFTERATTACK, not recommended as it means you can potentially cause damage to the item using the catalyst.
|
||||
var/force_catalyst_afterattack = FALSE
|
||||
///Callback provided to the tool for after a sect is chosen
|
||||
var/datum/callback/after_sect_select_cb
|
||||
///Optional argument. If a positive value, each invocation will lower charges, and the component will delete without any more charges
|
||||
var/charges
|
||||
///If a typecache is provided, only types of rites in the cache can be invoked.
|
||||
var/list/rite_types_allowlist
|
||||
|
||||
/datum/component/religious_tool/Initialize(_flags = ALL, _force_catalyst_afterattack = FALSE, _after_sect_select_cb, override_catalyst_type)
|
||||
/datum/component/religious_tool/Initialize(
|
||||
operation_flags = ALL,
|
||||
force_catalyst_afterattack = FALSE,
|
||||
after_sect_select_cb = null,
|
||||
catalyst_type = /obj/item/book/bible,
|
||||
charges = -1,
|
||||
rite_types_allowlist = null,
|
||||
)
|
||||
. = ..()
|
||||
SetGlobalToLocal() //attempt to connect on start in case one already exists!
|
||||
operation_flags = _flags
|
||||
force_catalyst_afterattack = _force_catalyst_afterattack
|
||||
after_sect_select_cb = _after_sect_select_cb
|
||||
if(override_catalyst_type)
|
||||
catalyst_type = override_catalyst_type
|
||||
src.operation_flags = operation_flags
|
||||
src.force_catalyst_afterattack = force_catalyst_afterattack
|
||||
src.after_sect_select_cb = after_sect_select_cb
|
||||
src.catalyst_type = catalyst_type
|
||||
src.charges = charges
|
||||
src.rite_types_allowlist = rite_types_allowlist
|
||||
RegisterSignal(SSdcs, COMSIG_RELIGIOUS_SECT_CHANGED, PROC_REF(SetGlobalToLocal))
|
||||
RegisterSignal(SSdcs, COMSIG_RELIGIOUS_SECT_RESET, PROC_REF(on_sect_reset))
|
||||
|
||||
@@ -150,6 +163,9 @@
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You are not holy, and therefore cannot perform rites.")
|
||||
return
|
||||
if(rite_types_allowlist && !is_path_in_list(path, rite_types_allowlist))
|
||||
to_chat(user, span_warning("This cannot perform that kind of rite."))
|
||||
return
|
||||
if(performing_rite)
|
||||
to_chat(user, "<span class='notice'>There is a rite currently being performed here already.")
|
||||
return
|
||||
@@ -159,13 +175,17 @@
|
||||
performing_rite = new path(parent)
|
||||
if(!performing_rite.perform_rite(user, parent))
|
||||
QDEL_NULL(performing_rite)
|
||||
return
|
||||
performing_rite.invoke_effect(user, parent)
|
||||
easy_access_sect.adjust_favor(-performing_rite.favor_cost)
|
||||
if(performing_rite.auto_delete)
|
||||
QDEL_NULL(performing_rite)
|
||||
else
|
||||
performing_rite.invoke_effect(user, parent)
|
||||
easy_access_sect.adjust_favor(-performing_rite.favor_cost)
|
||||
if(performing_rite.auto_delete)
|
||||
QDEL_NULL(performing_rite)
|
||||
else
|
||||
performing_rite = null
|
||||
performing_rite = null
|
||||
if(charges)
|
||||
charges--
|
||||
if(!charges)
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* Generates a list of available sects to the user. Intended to support custom-availability sects.
|
||||
@@ -231,7 +251,7 @@
|
||||
|
||||
if(!can_i_see)
|
||||
return
|
||||
examine_list += span_notice("Use a bible to interact with this.")
|
||||
examine_list += span_notice("Use a [catalyst_type::name] to interact with this.")
|
||||
if(isnull(easy_access_sect))
|
||||
if(operation_flags & RELIGION_TOOL_SECTSELECT)
|
||||
examine_list += span_notice("This looks like it can be used to select a sect.")
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
///how many lines multiplied by tempo should at least be higher than this.
|
||||
#define LONG_ENOUGH_SONG 220
|
||||
|
||||
///Smooth tunes component! Applied to musicians to give the songs they play special effects, according to a rite!
|
||||
///Comes with BARTICLES!!!
|
||||
/datum/component/smooth_tunes
|
||||
@@ -47,7 +44,7 @@
|
||||
if(istype(starting_song.parent, /obj/structure/musician))
|
||||
return //TODO: make stationary instruments work with no hiccups
|
||||
|
||||
if(starting_song.lines.len * starting_song.tempo > LONG_ENOUGH_SONG)
|
||||
if(starting_song.lines.len * starting_song.tempo > FESTIVAL_SONG_LONG_ENOUGH)
|
||||
viable_for_final_effect = TRUE
|
||||
else
|
||||
to_chat(parent, span_warning("This song is too short, so it won't include the song finishing effect."))
|
||||
@@ -56,6 +53,8 @@
|
||||
if(linked_songtuner_rite.song_start_message)
|
||||
starting_song.parent.visible_message(linked_songtuner_rite.song_start_message)
|
||||
|
||||
linked_songtuner_rite.performer_start_effect(parent, starting_song)
|
||||
|
||||
///prevent more songs from being blessed concurrently, mob signal
|
||||
UnregisterSignal(parent, COMSIG_ATOM_STARTING_INSTRUMENT)
|
||||
///and hook into the instrument this time, preventing other weird exploity stuff.
|
||||
@@ -112,5 +111,3 @@
|
||||
linked_songtuner_rite.song_effect(listener, parent)
|
||||
else
|
||||
stop_singing()
|
||||
|
||||
#undef LONG_ENOUGH_SONG
|
||||
|
||||
@@ -34,3 +34,22 @@
|
||||
|
||||
/datum/status_effect/song/antimagic/get_examine_text()
|
||||
return span_notice("[owner.p_They()] seem[owner.p_s()] to be covered in a dull, grey aura.")
|
||||
|
||||
/datum/status_effect/song/light
|
||||
id = "light_song"
|
||||
status_type = STATUS_EFFECT_REFRESH
|
||||
duration = 1 MINUTES
|
||||
aura_desc = "bright"
|
||||
/// lighting object that makes owner glow
|
||||
var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj
|
||||
|
||||
/datum/status_effect/song/light/on_apply()
|
||||
mob_light_obj = owner.mob_light(3, color = LIGHT_COLOR_DIM_YELLOW)
|
||||
playsound(owner, 'sound/weapons/fwoosh.ogg', 75, FALSE)
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/song/light/on_remove()
|
||||
QDEL_NULL(mob_light_obj)
|
||||
|
||||
/datum/status_effect/song/light_song/get_examine_text()
|
||||
return span_notice("[owner.p_They()] seem[owner.p_s()] to be covered in a glowing aura.")
|
||||
|
||||
@@ -81,3 +81,18 @@
|
||||
"sleepy_9" = 2, //sleepy theme specific
|
||||
"sleepy_10" = 2, //sleepy theme specific
|
||||
)
|
||||
|
||||
/particles/musical_notes/light
|
||||
icon = 'icons/effects/particles/notes/note_light.dmi'
|
||||
icon_state = list(
|
||||
"power_1" = 1,
|
||||
"power_2" = 1,
|
||||
"power_3" = 1,
|
||||
"power_4" = 1,
|
||||
"power_5" = 1,
|
||||
"power_6" = 1,
|
||||
"power_7" = 1,
|
||||
"power_8" = 1,
|
||||
"power_9" = 2, //light theme specific
|
||||
"power_10" = 2, //light theme specific
|
||||
)
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
RegisterSignal(src, COMSIG_INSTRUMENT_END, PROC_REF(update_icon_for_playing_music))
|
||||
|
||||
// Called by a component signal to update musical note VFX for songs playing while worn.
|
||||
/obj/item/instrument/piano_synth/headphones/proc/update_icon_for_playing_music()
|
||||
/obj/item/instrument/piano_synth/headphones/proc/update_icon_for_playing_music(datum/source, datum/starting_song, atom/player)
|
||||
SIGNAL_HANDLER
|
||||
update_appearance()
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
/obj/item/circuit_component/synth/proc/start_playing(datum/port/input/port)
|
||||
synth.song.start_playing(src)
|
||||
|
||||
/obj/item/circuit_component/synth/proc/on_song_start()
|
||||
/obj/item/circuit_component/synth/proc/on_song_start(datum/source, datum/starting_song, atom/player)
|
||||
SIGNAL_HANDLER
|
||||
is_playing.set_output(TRUE)
|
||||
started_playing.set_output(COMPONENT_SIGNAL)
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
//we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak.
|
||||
//wrap the rest of the stuff to ensure stop_playing() is called.
|
||||
do_hearcheck()
|
||||
SEND_SIGNAL(parent, COMSIG_INSTRUMENT_START, src)
|
||||
SEND_SIGNAL(parent, COMSIG_INSTRUMENT_START, src, user)
|
||||
SEND_SIGNAL(user, COMSIG_ATOM_STARTING_INSTRUMENT, src)
|
||||
elapsed_delay = 0
|
||||
delay_by = 0
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/obj/item/instrument/violin/festival
|
||||
name = "Cogitandi Fidis"
|
||||
desc = "A violin that holds a special interest in the songs played from its strings."
|
||||
icon_state = "holy_violin"
|
||||
inhand_icon_state = "holy_violin"
|
||||
|
||||
/obj/item/instrument/violin/festival/Initialize(mapload)
|
||||
. = ..()
|
||||
RegisterSignal(src, COMSIG_INSTRUMENT_START, PROC_REF(on_instrument_start))
|
||||
|
||||
/// signal fired when the festival instrument starts to play.
|
||||
/obj/item/instrument/violin/festival/proc/on_instrument_start(datum/source, datum/song/starting_song, atom/player)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!starting_song || !isliving(player))
|
||||
return
|
||||
analyze_song(starting_song, player)
|
||||
|
||||
///Reports some relevant information when the song begins playing.
|
||||
/obj/item/instrument/violin/festival/proc/analyze_song(datum/song/song, mob/living/playing_song)
|
||||
var/list/analysis = list()
|
||||
//check tempo and lines
|
||||
var/song_length = song.lines.len * song.tempo
|
||||
analysis += span_revenbignotice("[src] speaks to you...")
|
||||
analysis += span_revennotice("\"This song has <b>[song.lines.len]</b> lines and a tempo of <b>[song.tempo]</b>.\"")
|
||||
analysis += span_revennotice("\"Multiplying these together gives a song length of <b>[song_length]</b>.\"")
|
||||
analysis += span_revennotice("\"To get a bonus effect from [GLOB.deity] upon finishing a performance, you need a song length of <b>[FESTIVAL_SONG_LONG_ENOUGH]</b>.\"")
|
||||
|
||||
to_chat(playing_song, analysis.Join("\n"))
|
||||
@@ -1,9 +1,64 @@
|
||||
/datum/religion_rites/holy_violin
|
||||
name = "Cogitandi Fidis"
|
||||
desc = "Creates a holy violin that can analyze songs played from it."
|
||||
ritual_length = 6 SECONDS
|
||||
ritual_invocations = list("A servant of jubilee is needed ...")
|
||||
invoke_msg = "... A great mind for musical matters!"
|
||||
favor_cost = 20 //you only need one
|
||||
|
||||
/datum/religion_rites/holy_violin/invoke_effect(mob/living/user, atom/religious_tool)
|
||||
. = ..()
|
||||
var/turf/tool_turf = get_turf(religious_tool)
|
||||
var/obj/item/instrument/violin/fidis = new /obj/item/instrument/violin/festival(get_turf(religious_tool))
|
||||
fidis.visible_message(span_notice("[fidis] appears!"))
|
||||
playsound(tool_turf, 'sound/effects/pray.ogg', 50, TRUE)
|
||||
|
||||
/datum/religion_rites/portable_song_tuning
|
||||
name = "Portable Song Tuning"
|
||||
desc = "Empowers an instrument on the table to work as a portable altar for tuning songs. Will need to be recharged after 5 rites."
|
||||
ritual_length = 6 SECONDS
|
||||
ritual_invocations = list("Allow me to bring your holy inspirations ...")
|
||||
invoke_msg = "... And send them with the winds my tunes ride with!"
|
||||
favor_cost = 10
|
||||
///instrument to empower
|
||||
var/obj/item/instrument/instrument_target
|
||||
|
||||
/datum/religion_rites/portable_song_tuning/perform_rite(mob/living/user, atom/religious_tool)
|
||||
for(var/obj/item/instrument/could_empower in get_turf(religious_tool))
|
||||
instrument_target = could_empower
|
||||
return ..()
|
||||
to_chat(user, span_warning("You need to place an instrument on [religious_tool] to do this!"))
|
||||
return FALSE
|
||||
|
||||
/datum/religion_rites/portable_song_tuning/invoke_effect(mob/living/user, atom/movable/religious_tool)
|
||||
..()
|
||||
var/obj/item/instrument/empower_target = instrument_target
|
||||
var/turf/tool_turf = get_turf(religious_tool)
|
||||
instrument_target = null
|
||||
if(QDELETED(empower_target) || !(tool_turf == empower_target.loc)) //check if the instrument is still there
|
||||
to_chat(user, span_warning("Your target left the altar!"))
|
||||
return FALSE
|
||||
empower_target.visible_message(span_notice("[empower_target] glows for a moment."))
|
||||
playsound(tool_turf, 'sound/effects/pray.ogg', 50, TRUE)
|
||||
var/list/allowed_rites_from_bible = subtypesof(/datum/religion_rites/song_tuner)
|
||||
empower_target.AddComponent( \
|
||||
/datum/component/religious_tool, \
|
||||
operation_flags = RELIGION_TOOL_INVOKE, \
|
||||
force_catalyst_afterattack = FALSE, \
|
||||
after_sect_select_cb = null, \
|
||||
catalyst_type = /obj/item/book/bible, \
|
||||
charges = 5, \
|
||||
rite_types_allowlist = allowed_rites_from_bible, \
|
||||
)
|
||||
return TRUE
|
||||
|
||||
///prototype for rites that tune a song.
|
||||
/datum/religion_rites/song_tuner
|
||||
name = "Tune Song"
|
||||
desc = "this is a prototype."
|
||||
ritual_length = 10 SECONDS
|
||||
favor_cost = 10
|
||||
auto_delete = FALSE
|
||||
///if repeats count as continuations instead of a song's end, TRUE
|
||||
var/repeats_okay = TRUE
|
||||
///personal message sent to the chaplain as feedback for their chosen song
|
||||
@@ -20,6 +75,16 @@
|
||||
to_chat(user, span_notice(song_invocation_message))
|
||||
user.AddComponent(/datum/component/smooth_tunes, src, repeats_okay, particles_path, glow_color)
|
||||
|
||||
/**
|
||||
* Song effect applied when the performer starts playing.
|
||||
*
|
||||
* Arguments:
|
||||
* * performer - A human starting the song
|
||||
* * song_source - parent of the smooth_tunes component. This is limited to the compatible items of said component, which currently includes mobs and objects so we'll have to type appropriately.
|
||||
*/
|
||||
/datum/religion_rites/song_tuner/proc/performer_start_effect(mob/living/carbon/human/performer, atom/song_source)
|
||||
return
|
||||
|
||||
/**
|
||||
* Perform the song effect.
|
||||
*
|
||||
@@ -60,6 +125,28 @@
|
||||
/datum/religion_rites/song_tuner/evangelism/finish_effect(mob/living/carbon/human/listener, atom/song_source)
|
||||
listener.add_mood_event("blessing", /datum/mood_event/blessing)
|
||||
|
||||
/datum/religion_rites/song_tuner/light
|
||||
name = "Illuminating Solo"
|
||||
desc = "Sing a bright song, lighting up the area around you. At the end of the song, you'll give some illumination to listeners."
|
||||
particles_path = /particles/musical_notes/light
|
||||
song_invocation_message = "You've prepared a bright song!"
|
||||
song_start_message = span_notice("This music simply glows!")
|
||||
glow_color = "#fcff44"
|
||||
repeats_okay = FALSE
|
||||
favor_cost = 0
|
||||
/// lighting object that makes chaplain glow
|
||||
var/obj/effect/dummy/lighting_obj/moblight/performer_light_obj
|
||||
|
||||
/datum/religion_rites/song_tuner/light/performer_start_effect(mob/living/carbon/human/performer, atom/song_source)
|
||||
performer_light_obj = performer.mob_light(8, color = LIGHT_COLOR_DIM_YELLOW)
|
||||
|
||||
/datum/religion_rites/song_tuner/light/Destroy()
|
||||
QDEL_NULL(performer_light_obj)
|
||||
. = ..()
|
||||
|
||||
/datum/religion_rites/song_tuner/light/finish_effect(mob/living/carbon/human/listener, atom/song_source)
|
||||
listener.apply_status_effect(/datum/status_effect/song/light)
|
||||
|
||||
/datum/religion_rites/song_tuner/nullwave
|
||||
name = "Nullwave Vibrato"
|
||||
desc = "Sing a dull song, protecting those who listen from magic."
|
||||
|
||||
@@ -508,7 +508,10 @@
|
||||
alignment = ALIGNMENT_GOOD
|
||||
candle_overlay = FALSE
|
||||
rites_list = list(
|
||||
/datum/religion_rites/holy_violin,
|
||||
/datum/religion_rites/portable_song_tuning,
|
||||
/datum/religion_rites/song_tuner/evangelism,
|
||||
/datum/religion_rites/song_tuner/light,
|
||||
/datum/religion_rites/song_tuner/nullwave,
|
||||
/datum/religion_rites/song_tuner/pain,
|
||||
/datum/religion_rites/song_tuner/lullaby,
|
||||
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
@@ -5382,6 +5382,7 @@
|
||||
#include "code\modules\religion\rites.dm"
|
||||
#include "code\modules\religion\burdened\burdened_trauma.dm"
|
||||
#include "code\modules\religion\burdened\psyker.dm"
|
||||
#include "code\modules\religion\festival\festival_violin.dm"
|
||||
#include "code\modules\religion\festival\instrument_rites.dm"
|
||||
#include "code\modules\religion\honorbound\honorbound_rites.dm"
|
||||
#include "code\modules\religion\honorbound\honorbound_trauma.dm"
|
||||
|
||||