diff --git a/code/__DEFINES/dcs/signals/signals_music.dm b/code/__DEFINES/dcs/signals/signals_music.dm index 69c4fc5b12e..2f15f4e6322 100644 --- a/code/__DEFINES/dcs/signals/signals_music.dm +++ b/code/__DEFINES/dcs/signals/signals_music.dm @@ -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" diff --git a/code/__DEFINES/religion.dm b/code/__DEFINES/religion.dm index 0db0961c12d..edbde8bfcf9 100644 --- a/code/__DEFINES/religion.dm +++ b/code/__DEFINES/religion.dm @@ -9,6 +9,15 @@ #define ALIGNMENT_NEUT "neutral" #define ALIGNMENT_EVIL "evil" +<<<<<<< HEAD +======= +///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 + +>>>>>>> 4eb17420b ([MIRROR] Festival Sect Expansion: New Tunes, New Rites, Cogitandi Fidis [MDB IGNORE] (#25404)) //## which weapons should we use? // unused but for clarity diff --git a/code/datums/components/religious_tool.dm b/code/datums/components/religious_tool.dm index 4c0646b3a55..8491e489bed 100644 --- a/code/datums/components/religious_tool.dm +++ b/code/datums/components/religious_tool.dm @@ -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, "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, "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.") diff --git a/code/datums/components/smooth_tunes.dm b/code/datums/components/smooth_tunes.dm index c3025dbb54c..1442d29547b 100644 --- a/code/datums/components/smooth_tunes.dm +++ b/code/datums/components/smooth_tunes.dm @@ -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 diff --git a/code/datums/status_effects/song_effects.dm b/code/datums/status_effects/song_effects.dm index 0cca48d22b6..066ac457a9f 100644 --- a/code/datums/status_effects/song_effects.dm +++ b/code/datums/status_effects/song_effects.dm @@ -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.") diff --git a/code/game/objects/effects/particles/note_particles.dm b/code/game/objects/effects/particles/note_particles.dm index a959bf4537a..cb39685d812 100644 --- a/code/game/objects/effects/particles/note_particles.dm +++ b/code/game/objects/effects/particles/note_particles.dm @@ -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 + ) diff --git a/code/modules/instruments/piano_synth.dm b/code/modules/instruments/piano_synth.dm index a93ca0b7f51..71d0d96ef56 100644 --- a/code/modules/instruments/piano_synth.dm +++ b/code/modules/instruments/piano_synth.dm @@ -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) diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm index ccd85ffda5f..68039df2146 100644 --- a/code/modules/instruments/songs/_song.dm +++ b/code/modules/instruments/songs/_song.dm @@ -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 diff --git a/code/modules/religion/festival/festival_violin.dm b/code/modules/religion/festival/festival_violin.dm new file mode 100644 index 00000000000..82431352685 --- /dev/null +++ b/code/modules/religion/festival/festival_violin.dm @@ -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 [song.lines.len] lines and a tempo of [song.tempo].\"") + analysis += span_revennotice("\"Multiplying these together gives a song length of [song_length].\"") + analysis += span_revennotice("\"To get a bonus effect from [GLOB.deity] upon finishing a performance, you need a song length of [FESTIVAL_SONG_LONG_ENOUGH].\"") + + to_chat(playing_song, analysis.Join("\n")) diff --git a/code/modules/religion/festival/instrument_rites.dm b/code/modules/religion/festival/instrument_rites.dm index a1c94c92425..d8537f5845e 100644 --- a/code/modules/religion/festival/instrument_rites.dm +++ b/code/modules/religion/festival/instrument_rites.dm @@ -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." diff --git a/code/modules/religion/religion_sects.dm b/code/modules/religion/religion_sects.dm index 8b96c210511..5b70a462ee8 100644 --- a/code/modules/religion/religion_sects.dm +++ b/code/modules/religion/religion_sects.dm @@ -431,7 +431,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, diff --git a/icons/effects/particles/notes/note_light.dmi b/icons/effects/particles/notes/note_light.dmi new file mode 100644 index 00000000000..3474edebc23 Binary files /dev/null and b/icons/effects/particles/notes/note_light.dmi differ diff --git a/icons/mob/inhands/equipment/instruments_lefthand.dmi b/icons/mob/inhands/equipment/instruments_lefthand.dmi index edd78927aa6..2fb076293d5 100644 Binary files a/icons/mob/inhands/equipment/instruments_lefthand.dmi and b/icons/mob/inhands/equipment/instruments_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/instruments_righthand.dmi b/icons/mob/inhands/equipment/instruments_righthand.dmi index 7cc8568b908..f989cec435d 100644 Binary files a/icons/mob/inhands/equipment/instruments_righthand.dmi and b/icons/mob/inhands/equipment/instruments_righthand.dmi differ diff --git a/icons/obj/art/musician.dmi b/icons/obj/art/musician.dmi index 6f98eb0d7b0..df48e54e6c6 100644 Binary files a/icons/obj/art/musician.dmi and b/icons/obj/art/musician.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 83556138c36..04a06d03a5c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -5468,6 +5468,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"