diff --git a/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm b/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm
index a00c71b1e94..89383c9e77c 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm
@@ -126,6 +126,11 @@
/obj/structure/bed,
/turf/simulated/floor/mineral/titanium/purple,
/area/ruin/space/powered)
+"Q" = (
+/obj/structure/table/wood,
+/obj/item/blank_tarot_card,
+/turf/simulated/floor/mineral/titanium/purple,
+/area/ruin/space/powered)
(1,1,1) = {"
a
@@ -277,7 +282,7 @@ b
j
r
w
-n
+Q
j
b
a
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm
index d1f0175e1f4..8434558c253 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm
@@ -167,6 +167,7 @@
/area/ruin/space/unpowered)
"aF" = (
/obj/structure/table/wood,
+/obj/item/blank_tarot_card,
/turf/simulated/floor/wood{
icon_state = "wood-broken6"
},
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 4f185e0f70a..d34fb486829 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -18,6 +18,8 @@
#define STATUS_EFFECT_SHADOW_MEND /datum/status_effect/shadow_mend //Quick, powerful heal that deals damage afterwards. Heals 15 brute/burn every second for 3 seconds.
#define STATUS_EFFECT_VOID_PRICE /datum/status_effect/void_price //The price of healing yourself with void energy. Deals 3 brute damage every 3 seconds for 30 seconds.
+#define STATUS_EFFECT_SHADOW_MEND_DEVIL /datum/status_effect/shadow_mend/devil //Tarot version, hurts others over self
+
#define STATUS_EFFECT_HIPPOCRATIC_OATH /datum/status_effect/hippocraticOath //Gives you an aura of healing as well as regrowing the Rod of Asclepius if lost
#define STATUS_EFFECT_REGENERATIVE_CORE /datum/status_effect/regenerative_core
@@ -37,6 +39,8 @@
#define STATUS_EFFECT_BLOODDRUNK /datum/status_effect/blooddrunk //Stun immunity and greatly reduced damage taken
+#define STATUS_EFFECT_BLOODDRUNK_CHARIOT /datum/status_effect/blooddrunk/chariot //adds pacifism
+
#define STATUS_EFFECT_DASH /datum/status_effect/dash // Grants the ability to dash, expiring after a few secodns
/// Rapid burn/brute/oxy/blood healing from the cling ability
@@ -62,6 +66,12 @@
#define STATUS_EFFECT_BEARSERKER_RAGE /datum/status_effect/bearserker_rage
+#define STATUS_EFFECT_XRAY /datum/status_effect/xray // Xray vision for 2 minutes
+
+#define STATUS_EFFECT_BADASS /datum/status_effect/badass // Badass trait for 2 minutes.
+
+#define STATUS_EFFECT_REVERSED_SUN /datum/status_effect/reversed_sun // Weaker eternal darkness, nightvision, but nearsight
+
/////////////
// DEBUFFS //
/////////////
@@ -114,6 +124,8 @@
#define STATUS_EFFECT_PEPPERSPRAYED /datum/status_effect/pepper_spray
+#define STATUS_EFFECT_REVERSED_HIGH_PRIESTESS /datum/status_effect/reversed_high_priestess //Bubblegum will chase the person hit by the effect, grabbing people at random. This can and WILL include the caster.
+
//#define STATUS_EFFECT_NECROPOLIS_CURSE /datum/status_effect/necropolis_curse
//#define CURSE_BLINDING 1 //makes the edges of the target's screen obscured
//#define CURSE_SPAWNING 2 //spawns creatures that attack the target only
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index 646b40e4d26..38f94e4ce89 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -62,6 +62,11 @@
id = "shadow_mend"
duration = 3 SECONDS
alert_type = /atom/movable/screen/alert/status_effect/shadow_mend
+ /// If this is true, the status effect will try to apply the debuff to others, rather than the user
+ var/devil = FALSE
+
+/datum/status_effect/shadow_mend/devil
+ devil = TRUE
/atom/movable/screen/alert/status_effect/shadow_mend
name = "Shadow Mend"
@@ -78,10 +83,25 @@
owner.adjustFireLoss(-15)
/datum/status_effect/shadow_mend/on_remove()
- owner.visible_message("The violet light around [owner] glows black!", "The tendrils around you cinch tightly and reap their toll...")
- playsound(owner, 'sound/magic/teleport_diss.ogg', 50, 1)
- owner.apply_status_effect(STATUS_EFFECT_VOID_PRICE)
+ if(!devil)
+ owner.visible_message("The violet light around [owner] glows black!", "The tendrils around you cinch tightly and reap their toll...")
+ playsound(owner, 'sound/magic/teleport_diss.ogg', 50, TRUE)
+ owner.apply_status_effect(STATUS_EFFECT_VOID_PRICE)
+ return
+ var/found_someone = FALSE
+
+ for(var/mob/living/L in oview(9, owner))
+ found_someone = TRUE
+ playsound(owner, 'sound/magic/teleport_diss.ogg', 50, TRUE)
+ L.Beam(owner, "grabber_beam", time = 1 SECONDS, maxdistance = 9)
+ L.apply_status_effect(STATUS_EFFECT_VOID_PRICE)
+ if(found_someone)
+ owner.visible_message("The violet light around [owner] glows black... and shoots off to those around [owner.p_them()]!", "The tendrils around you cinch tightly... but then unwravel and fly at others!")
+ else
+ owner.visible_message("The violet light around [owner] glows black!", "The tendrils around you cinch tightly and reap their toll...")
+ playsound(owner, 'sound/magic/teleport_diss.ogg', 50, TRUE)
+ owner.apply_status_effect(STATUS_EFFECT_VOID_PRICE)
/datum/status_effect/void_price
id = "void_price"
@@ -110,6 +130,13 @@
tick_interval = 0
alert_type = /atom/movable/screen/alert/status_effect/blooddrunk
var/blooddrunk_damage_mod_remove = 4 // Damage is multiplied by this at the end of the status effect. Modify this one, it changes the _add
+ /// If this is the chariot subtype, which grants pacifism while the effect is active.
+ var/chariot = FALSE
+
+/datum/status_effect/blooddrunk/chariot
+ duration = 10 SECONDS
+ chariot = TRUE
+ blooddrunk_damage_mod_remove = 6
/atom/movable/screen/alert/status_effect/blooddrunk
name = "Blood-Drunk"
@@ -120,6 +147,8 @@
. = ..()
if(.)
ADD_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, "blooddrunk")
+ if(chariot)
+ ADD_TRAIT(owner, TRAIT_PACIFISM, "blooddrunk")
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
var/blooddrunk_damage_mod_add = 1 / blooddrunk_damage_mod_remove // Damage is multiplied by this at the start of the status effect. Don't modify this one directly.
@@ -144,6 +173,7 @@
H.physiology.stamina_mod *= blooddrunk_damage_mod_remove
add_attack_logs(owner, owner, "lost blood-drunk stun immunity", ATKLOG_ALL)
REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, "blooddrunk")
+ REMOVE_TRAIT(owner, TRAIT_PACIFISM, "blooddrunk")
if(islist(owner.stun_absorption) && owner.stun_absorption["blooddrunk"])
owner.remove_stun_absorption("blooddrunk")
@@ -183,7 +213,7 @@
H.physiology.stamina_mod *= 0.5
H.physiology.stun_mod *= 0.5
var/datum/antagonist/vampire/V = owner.mind.has_antag_datum(/datum/antagonist/vampire)
- if(V.get_ability(/datum/vampire_passive/blood_swell_upgrade))
+ if(V?.get_ability(/datum/vampire_passive/blood_swell_upgrade))
bonus_damage_applied = TRUE
H.physiology.melee_bonus += 10
H.dna.species.punchstunthreshold += 8 //higher chance to stun but not 100%
@@ -776,3 +806,70 @@
var/mob/living/carbon/human/H = owner
H.physiology.stamina_mod /= 0.75
add_attack_logs(owner, owner, "lost bearserker rage resistances", ATKLOG_ALL)
+
+/datum/status_effect/xray
+ id = "xray"
+ alert_type = null
+ status_type = STATUS_EFFECT_REFRESH
+ duration = 2 MINUTES
+ tick_interval = 0
+
+/datum/status_effect/xray/on_apply()
+ . = ..()
+ ADD_TRAIT(owner, TRAIT_XRAY_VISION, "XRAY_BUFF")
+ ADD_TRAIT(owner, TRAIT_NIGHT_VISION, "XRAY_BUFF")
+ owner.update_sight()
+
+/datum/status_effect/xray/on_remove()
+ . = ..()
+ REMOVE_TRAIT(owner, TRAIT_XRAY_VISION, "XRAY_BUFF")
+ REMOVE_TRAIT(owner, TRAIT_NIGHT_VISION, "XRAY_BUFF")
+ owner.update_sight()
+
+/datum/status_effect/badass
+ id = "badass"
+ alert_type = null
+ status_type = STATUS_EFFECT_REFRESH
+ duration = 2 MINUTES
+ tick_interval = 0
+
+/datum/status_effect/badass/on_apply()
+ . = ..()
+ ADD_TRAIT(owner, TRAIT_BADASS, "BADDASS_BUFF")
+
+/datum/status_effect/badass/on_remove()
+ . = ..()
+ REMOVE_TRAIT(owner, TRAIT_BADASS, "BADDASS_BUFF")
+
+/datum/status_effect/reversed_sun
+ id = "reversed_sun"
+ alert_type = null
+ status_type = STATUS_EFFECT_REFRESH
+ duration = 1 MINUTES
+ tick_interval = 0.2 SECONDS
+
+/datum/status_effect/reversed_sun/on_apply()
+ . = ..()
+ owner.become_nearsighted("REVERSED_SUN")
+ ADD_TRAIT(owner, TRAIT_NIGHT_VISION, "REVERSED_SUN")
+ owner.update_sight()
+ owner.set_light(7, -5, "#ddd6cf")
+
+/datum/status_effect/reversed_sun/on_remove()
+ . = ..()
+ owner.remove_light()
+ owner.cure_nearsighted("REVERSED_SUN")
+ REMOVE_TRAIT(owner, TRAIT_NIGHT_VISION, "REVERSED_SUN")
+ owner.update_sight()
+
+/datum/status_effect/reversed_sun/tick()
+ for(var/atom/movable/AM in oview(8, owner))
+ if(isliving(AM))
+ var/mob/living/L = AM
+ if(L.affects_vampire(owner))
+ L.adjust_bodytemperature(-1.5 * TEMPERATURE_DAMAGE_COEFFICIENT)
+ continue
+ if(istype(AM, /obj/item/projectile))
+ var/obj/item/projectile/P = AM
+ if(P.flag == ENERGY || P.flag == LASER)
+ P.damage *= 0.85
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index 9edf7eb42ad..e65da3535cd 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -1322,3 +1322,29 @@
desc = "Real winners quit before they reach the ultimate prize."
#undef DEFAULT_MAX_CURSE_COUNT
+
+/datum/status_effect/reversed_high_priestess
+ id = "reversed_high_priestess"
+ duration = 1 MINUTES
+ status_type = STATUS_EFFECT_REFRESH
+ tick_interval = 6 SECONDS
+ alert_type = /atom/movable/screen/alert/status_effect/bubblegum_curse
+
+/datum/status_effect/reversed_high_priestess/tick()
+ . = ..()
+ new /obj/effect/bubblegum_warning(get_turf(owner))
+
+/obj/effect/bubblegum_warning
+ name = "bloody rift"
+ desc = "You feel like even being *near* this is a bad idea"
+ icon = 'icons/obj/biomass.dmi'
+ icon_state = "rift"
+ color = "red"
+
+/obj/effect/bubblegum_warning/Initialize()
+ . = ..()
+ addtimer(CALLBACK(src, PROC_REF(slap_someone)), 2.5 SECONDS) //A chance to run away
+
+/obj/effect/bubblegum_warning/proc/slap_someone()
+ new /obj/effect/abstract/bubblegum_rend_helper(get_turf(src), null, 10)
+ qdel(src)
diff --git a/code/datums/uplink_items/uplink_traitor.dm b/code/datums/uplink_items/uplink_traitor.dm
index db79ccb1a6c..ea331744b39 100644
--- a/code/datums/uplink_items/uplink_traitor.dm
+++ b/code/datums/uplink_items/uplink_traitor.dm
@@ -305,6 +305,17 @@
excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
job = list("Head of Personnel", "Quartermaster", "Cargo Technician", "Librarian", "Coroner", "Psychiatrist", "Virologist")
+// Tarot card generator, librarian and Chaplain.
+
+/datum/uplink_item/jobspecific/tarot_generator
+ name = "Enchanted tarot card deck"
+ desc = "A magic tarot card deck \"borrowed\" from a Wizard federation storage unit. \
+ Capable of producing magic tarot cards of the 22 major arcana, and their reversed versions. Each card has a different effect. \
+ Throw the card at someone to use it on them, or use it in hand to apply it to yourself. Unlimited uses, 25 second cooldown, can have up to 3 cards in the world."
+ reference = "tarot"
+ item = /obj/item/tarot_generator
+ cost = 55 //This can do a lot of stuff, but is quite random. As such, higher price.
+ job = list("Chaplain", "Librarian")
//--------------------------//
// Species Restricted Gear //
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 481d2dbfc02..e298a454a26 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -318,7 +318,7 @@ GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list(
selection_title = "Archives"
creation_message = "You invoke the dark magic of the tomes creating a %ITEM%!"
choosable_items = list("Shuttle Curse" = /obj/item/shuttle_curse, "Zealot's Blindfold" = /obj/item/clothing/glasses/hud/health/night/cultblind,
- "Veil Shifter" = /obj/item/cult_shift, "Reality sunderer" = /obj/item/portal_amulet) //Add void torch to veil shifter spawn
+ "Veil Shifter" = /obj/item/cult_shift, "Reality sunderer" = /obj/item/portal_amulet, "Blank Tarot Card" = /obj/item/blank_tarot_card)
/obj/structure/cult/functional/archives/Initialize(mapload)
. = ..()
diff --git a/code/game/gamemodes/wizard/magic_tarot.dm b/code/game/gamemodes/wizard/magic_tarot.dm
new file mode 100644
index 00000000000..e3efcfa8128
--- /dev/null
+++ b/code/game/gamemodes/wizard/magic_tarot.dm
@@ -0,0 +1,885 @@
+/obj/item/tarot_generator
+ name = "Enchanted tarot card deck"
+ desc = "This tarot card box has quite the array of runes and artwork on it."
+ icon = 'icons/obj/playing_cards.dmi'
+ icon_state = "tarot_box"
+ w_class = WEIGHT_CLASS_SMALL
+ /// What is the maximum number of cards the tarot generator can have in the world at a time?
+ var/maximum_cards = 3
+ /// List of cards we have created, to check against maximum, and so we can purge them from the pack.
+ var/list/our_card_list = list()
+ ///How long the cooldown is each time we draw a card before we can draw another?
+ var/our_card_cooldown_time = 25 SECONDS
+ COOLDOWN_DECLARE(card_cooldown)
+
+/obj/item/tarot_generator/wizard
+ maximum_cards = 5
+ our_card_cooldown_time = 12 SECONDS // A minute for a full hand of 5 cards
+
+/obj/item/tarot_generator/attack_self(mob/user)
+ if(!COOLDOWN_FINISHED(src, card_cooldown))
+ to_chat(user, "[src]'s magic is still recovering from the last card, wait [round(COOLDOWN_TIMELEFT(src, card_cooldown) / 10)] more second\s!")
+ return
+ if(length(our_card_list) >= maximum_cards)
+ to_chat(user, "[src]'s magic can only support up to [maximum_cards] in the world at once, use or destroy some!")
+ return
+ var/obj/item/magic_tarot_card/MTC = new /obj/item/magic_tarot_card(get_turf(src), src)
+ our_card_list += MTC
+ user.put_in_hands(MTC)
+ to_chat(user, "You draw [MTC.name]... [MTC.card_desc]") //No period on purpose.
+ COOLDOWN_START(src, card_cooldown, our_card_cooldown_time)
+
+/obj/item/tarot_generator/examine(mob/user)
+ . = ..()
+ . += "Alt-Shift-Click to destroy all cards it has produced."
+ . += "It has [length(our_card_list)] card\s in the world right now."
+ if(!COOLDOWN_FINISHED(src, card_cooldown))
+ . += "You may draw another card again in [round(COOLDOWN_TIMELEFT(src, card_cooldown) / 10)] second\s."
+
+/obj/item/tarot_generator/AltShiftClick(mob/user)
+ for(var/obj/item/magic_tarot_card/MTC in our_card_list)
+ MTC.dust()
+ to_chat(user, "You dispell the cards [src] had created.")
+
+// Booster packs filled with 3, 5, or 7 playing cards! Used by the wizard space ruin, or rarely in lavaland tendril chests.
+/obj/item/tarot_card_pack
+ name = "\improper Enchanted Arcana Pack"
+ desc = "A pack of 3 Enchanted tarot cards. Collect them all!"
+ icon = 'icons/obj/playing_cards.dmi'
+ icon_state = "pack"
+ ///How many cards in a pack. 3 in base, 5 in jumbo, 7 in mega
+ var/cards = 3
+
+/obj/item/tarot_card_pack/attack_self(mob/user)
+ user.visible_message("[user] tears open [src].", \
+ "You tear open [src]!")
+ playsound(loc, 'sound/items/poster_ripped.ogg', 50, TRUE)
+ for(var/i in 1 to cards)
+ new /obj/item/magic_tarot_card(get_turf(src))
+ qdel(src)
+
+/obj/item/tarot_card_pack/jumbo
+ name = "\improper Jumbo Arcana Pack"
+ desc = "A Jumbo card pack from your friend Jimbo!"
+ icon_state = "jumbopack"
+ cards = 5
+
+/obj/item/tarot_card_pack/mega
+ name = "\improper MEGA Arcana Pack"
+ desc = "Sadly, you won't find a Joker for an angel room, or a Soul card in here either."
+ icon_state = "megapack"
+ cards = 7
+
+// Blank tarot cards. Made by the cult, however also good for space ruins potentially, where one feels a card pack would be too much?
+/obj/item/blank_tarot_card
+ name = "blank tarot card"
+ desc = "A blank tarot card."
+ icon = 'icons/obj/playing_cards.dmi'
+ icon_state = "tarot_blank"
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 10
+ throwforce = 0
+ force = 0
+ resistance_flags = FLAMMABLE
+ /// If a person can choose what the card produces. No cost if they can choose.
+ var/let_people_choose = FALSE
+
+/obj/item/blank_tarot_card/examine(mob/user)
+ . = ..()
+ if(!let_people_choose)
+ . += "With a bit of Ink, a work of art could be created. Will you provide your Ink?"
+ else
+ . += "We have the Ink... Could you provide your Vision instead?"
+
+/obj/item/blank_tarot_card/attack_self(mob/user)
+ if(!ishuman(user))
+ return
+ if(!let_people_choose)
+ var/mob/living/carbon/human/H = user
+ if(H.dna && (NO_BLOOD in H.dna.species.species_traits))
+ to_chat(user, "No blood to provide?... Then no Ink for the art...")
+ return
+ if(H.blood_volume <= 100) //Shouldn't happen, they should be dead, but failsafe. Not bleeding as then they could recover the blood with blood rites
+ return
+ H.blood_volume -= 100
+ H.drop_item()
+ var/obj/item/magic_tarot_card/MTC = new /obj/item/magic_tarot_card(get_turf(src))
+ user.put_in_hands(MTC)
+ to_chat(user, "Your blood flows into [src]... And your Ink makes a work of art! [MTC.name]... [MTC.card_desc]") //No period on purpose.
+ qdel(src)
+ return
+ var/tarot_type
+ var/tarot_name
+ var/list/card_by_name = list()
+ for(var/T in subtypesof(/datum/tarot) - /datum/tarot/reversed)
+ var/datum/tarot/temp = T
+ card_by_name[temp.name] = T
+
+ tarot_name = tgui_input_list(user, "Choose the Work of Art to create.", "Art Creation", card_by_name)
+ tarot_type = card_by_name[tarot_name]
+ if(tarot_type)
+ user.drop_item()
+ var/obj/item/magic_tarot_card/MTC = new /obj/item/magic_tarot_card(get_turf(src), null, tarot_type)
+ user.put_in_hands(MTC)
+ to_chat(user, "You put your Vision into [src], and your Vision makes a work of Art! [MTC.name]... [MTC.card_desc]") //No period on purpose.
+ qdel(src)
+
+/obj/item/blank_tarot_card/choose //For admins mainly, to spawn a specific tarot card. Not recommended for ruins.
+ let_people_choose = TRUE
+
+/obj/item/magic_tarot_card
+ name = "XXII - The Unknown"
+ desc = "A beautiful tarot card. However, it feels like... more?"
+ icon = 'icons/obj/playing_cards.dmi'
+ icon_state = "tarot_the_unknown"
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 10
+ throwforce = 0
+ force = 0
+ resistance_flags = FLAMMABLE
+ /// The deck that created us. Notifies it we have been deleted on use.
+ var/obj/item/tarot_generator/creator_deck
+ /// Our magic tarot card datum that lets the tarot card do stuff on use, or hitting someone
+ var/datum/tarot/our_tarot
+ /// Our fancy description given to use by the tarot datum.
+ var/card_desc = "Untold answers... wait what? This is a bug, report this as an issue on github!"
+ ///Is the card face down? Shows the card back, hides the examine / name.
+ var/face_down = FALSE
+
+/obj/item/magic_tarot_card/Initialize(mapload, obj/item/tarot_generator/source, datum/tarot/chosen_tarot)
+ . = ..()
+ if(source)
+ creator_deck = source
+ if(chosen_tarot)
+ our_tarot = new chosen_tarot
+ if(!istype(our_tarot))
+ var/tarotpath = pick(subtypesof(/datum/tarot) - /datum/tarot/reversed)
+ our_tarot = new tarotpath
+ name = our_tarot.name
+ card_desc = our_tarot.desc
+ icon_state = "tarot_[our_tarot.card_icon]"
+
+/obj/item/magic_tarot_card/Destroy()
+ if(creator_deck)
+ creator_deck.our_card_list -= src
+ return ..()
+
+/obj/item/magic_tarot_card/examine(mob/user)
+ . = ..()
+ if(!face_down)
+ . += "[card_desc]"
+ . += "Alt-Shift-Click to flip the card over."
+
+/obj/item/magic_tarot_card/attack_self(mob/user)
+ if(our_tarot)
+ INVOKE_ASYNC(our_tarot, TYPE_PROC_REF(/datum/tarot, activate), user)
+ poof()
+
+/obj/item/magic_tarot_card/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable)
+ if(face_down)
+ flip()
+ . = ..()
+
+/obj/item/magic_tarot_card/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ . = ..()
+ if(isliving(hit_atom) && our_tarot)
+ INVOKE_ASYNC(our_tarot, TYPE_PROC_REF(/datum/tarot, activate), hit_atom)
+ poof()
+
+/obj/item/magic_tarot_card/AltShiftClick(mob/user)
+ flip()
+
+/obj/item/magic_tarot_card/proc/flip()
+ if(!face_down)
+ icon_state = "cardback[our_tarot.reversed ? "?" : ""]"
+ name = "Enchanted tarot card"
+ face_down = TRUE
+ else
+ name = our_tarot.name
+ icon_state = "tarot_[our_tarot.card_icon]"
+ face_down = FALSE
+
+/obj/item/magic_tarot_card/proc/poof()
+ new /obj/effect/temp_visual/revenant(get_turf(src))
+ qdel(src)
+
+/obj/item/magic_tarot_card/proc/dust()
+ visible_message("[src] disintegrates into dust!")
+ new /obj/effect/temp_visual/revenant(get_turf(src))
+ qdel(src)
+
+/datum/tarot
+ /// Name used for the card
+ var/name = "XXII - The Unknown."
+ /// Desc used for the card description of the card
+ var/desc = "Untold answers... wait what? This is a bug, report this as an issue on github!"
+ /// What icon is used for the card?
+ var/card_icon = "the_unknown"
+ /// Are we reversed? Used for the card back.
+ var/reversed = FALSE
+
+/datum/tarot/proc/activate(mob/living/target)
+ stack_trace("A bugged tarot card was spawned and used. Please make an issue report! Type was [src.type]")
+
+/datum/tarot/reversed
+ name = "XXII - The Unknown?"
+ desc = "Untold answers... wait what? This is a bug, report this as an issue on github! This one was a reversed arcana!"
+ card_icon = "the_unknown?"
+ reversed = TRUE
+
+/datum/tarot/the_fool
+ name = "0 - The Fool"
+ desc = "Where journey begins."
+ card_icon = "the_fool"
+
+/datum/tarot/the_fool/activate(mob/living/target)
+ target.forceMove(pick(GLOB.latejoin))
+ to_chat(target, "You are abruptly pulled through space!")
+
+/datum/tarot/the_magician
+ name = "I - The Magician"
+ desc = "May you never miss your goal."
+ card_icon = "the_magician"
+
+/datum/tarot/the_magician/activate(mob/living/target)
+ target.apply_status_effect(STATUS_EFFECT_BADASS)
+ to_chat(target, "You feel badass.")
+
+/datum/tarot/the_high_priestess
+ name = "II - The High Priestess"
+ desc = "Mother is watching you."
+ card_icon = "the_high_priestess"
+
+/datum/tarot/the_high_priestess/activate(mob/living/target)
+ new /obj/effect/abstract/bubblegum_rend_helper(get_turf(target), target, 20)
+
+/obj/effect/abstract/bubblegum_rend_helper
+ name = "bubblegum_rend_helper"
+
+/obj/effect/abstract/bubblegum_rend_helper/Initialize(mapload, mob/living/owner, damage)
+ . = ..()
+ INVOKE_ASYNC(src, PROC_REF(rend), owner, damage)
+
+/obj/effect/abstract/bubblegum_rend_helper/proc/rend(mob/living/owner, damage)
+ if(!owner)
+ for(var/mob/living/L in shuffle(view(9, src)))
+ owner = L
+ break
+ owner.Immobilize(3 SECONDS)
+ for(var/i in 1 to 3)
+ var/turf/first_turf = get_turf(owner)
+ new /obj/effect/decal/cleanable/blood/bubblegum(first_turf)
+ if(prob(50))
+ new /obj/effect/temp_visual/bubblegum_hands/rightsmack(first_turf)
+ else
+ new /obj/effect/temp_visual/bubblegum_hands/leftsmack(first_turf)
+ sleep(6)
+ var/turf/second_turf = get_turf(owner)
+ to_chat(owner, "Something huge rends you!")
+ playsound(second_turf, 'sound/misc/demon_attack1.ogg', 100, TRUE, -1)
+ owner.adjustBruteLoss(damage)
+ qdel(src)
+
+/datum/tarot/the_empress
+ name = "III - The Empress"
+ desc = "May your rage bring power."
+ card_icon = "the_empress"
+
+/datum/tarot/the_empress/activate(mob/living/target)
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.reagents.add_reagent("mephedrone", 4.5)
+ H.reagents.add_reagent("mitocholide", 12)
+
+/datum/tarot/the_emperor
+ name = "IV - The Emperor"
+ desc = "Challenge me!"
+ card_icon = "the_emperor"
+
+/datum/tarot/the_emperor/activate(mob/living/target)
+ var/list/L = list()
+ for(var/turf/T in get_area_turfs(/area/station/command/bridge))
+ if(is_blocked_turf(T))
+ continue
+ L.Add(T)
+
+ if(!length(L))
+ to_chat(target, "Huh. No bridge? Well, that sucks.")
+ return
+
+ target.forceMove(pick(L))
+ to_chat(target, "You are abruptly pulled through space!")
+
+/datum/tarot/the_hierophant
+ name = "V - The Hierophant"
+ desc = "Two prayers for the lost."
+ card_icon = "the_hierophant"
+
+/datum/tarot/the_hierophant/activate(mob/living/target)
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/H = target
+ if(!H.wear_suit)
+ return
+ H.wear_suit.setup_hierophant_shielding()
+ H.update_appearance(UPDATE_ICON)
+
+/datum/tarot/the_lovers
+ name = "VI - The Lovers"
+ desc = "May you prosper and be in good health."
+ card_icon = "the_lovers"
+
+/datum/tarot/the_lovers/activate(mob/living/target)
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.adjustBruteLoss(-40, robotic = TRUE)
+ H.adjustFireLoss(-40, robotic = TRUE)
+ H.blood_volume = min(H.blood_volume + 100, BLOOD_VOLUME_NORMAL)
+ else
+ target.adjustBruteLoss(-40)
+ target.adjustFireLoss(-40)
+ target.adjustOxyLoss(-40)
+ target.adjustToxLoss(-40)
+
+/datum/tarot/the_chariot
+ name = "VII - The Chariot"
+ desc = "May nothing stand before you."
+ card_icon = "the_chariot"
+
+/datum/tarot/the_chariot/activate(mob/living/target)
+ target.apply_status_effect(STATUS_EFFECT_BLOOD_RUSH)
+ target.apply_status_effect(STATUS_EFFECT_BLOODDRUNK_CHARIOT)
+
+/datum/tarot/justice
+ name = "VIII - Justice"
+ desc = "May your future become balanced."
+ card_icon = "justice"
+
+/datum/tarot/justice/activate(mob/living/target)
+ var/turf/target_turf = get_turf(target)
+ new /obj/item/storage/firstaid/regular(target_turf)
+ new /obj/item/grenade/chem_grenade/waterpotassium(target_turf)
+ new /obj/item/card/emag/magic_key(target_turf)
+ new /obj/item/stack/spacecash/c100(target_turf)
+
+/datum/tarot/the_hermit
+ name = "IX - The Hermit"
+ desc = "May you see what life has to offer."
+ card_icon = "the_hermit"
+
+/datum/tarot/the_hermit/activate(mob/living/target)
+ var/list/viable_vendors = list()
+ for(var/obj/machinery/economy/vending/candidate in GLOB.machines)
+ if(!is_station_level(candidate.z))
+ continue
+ viable_vendors += candidate
+
+ if(!length(viable_vendors))
+ to_chat(target, "No vending machines? Well, with luck cargo will have something to offer. If you go there yourself.")
+ return
+
+ target.forceMove(get_turf(pick(viable_vendors)))
+ to_chat(target, "You are abruptly pulled through space!")
+
+/datum/tarot/wheel_of_fortune
+ name = "X - Wheel of Fortune"
+ desc = "Spin the wheel of destiny."
+ card_icon = "wheel_of_fortune"
+
+/datum/tarot/wheel_of_fortune/activate(mob/living/target)
+ var/list/static/bad_vendors = list(
+ /obj/machinery/economy/vending/liberationstation,
+ /obj/machinery/economy/vending/toyliberationstation,
+ /obj/machinery/economy/vending/wallmed
+ )
+ var/turf/target_turf = get_turf(target)
+ var/vendorpath = pick(subtypesof(/obj/machinery/economy/vending) - bad_vendors)
+ new vendorpath(target_turf)
+
+/datum/tarot/strength
+ name = "XI - Strength"
+ desc = "May your power bring rage."
+ card_icon = "strength"
+
+/datum/tarot/strength/activate(mob/living/target)
+ target.apply_status_effect(STATUS_EFFECT_VAMPIRE_GLADIATOR)
+ target.apply_status_effect(STATUS_EFFECT_BLOOD_SWELL)
+
+/datum/tarot/the_hanged_man
+ name = "XII - The Hanged Man"
+ desc = "May you find enlightenment."
+ card_icon = "the_hanged_man"
+
+/datum/tarot/the_hanged_man/activate(mob/living/target)
+ if(target.flying)
+ return
+ target.flying = TRUE
+ addtimer(VARSET_CALLBACK(target, flying, FALSE), 60 SECONDS)
+
+/datum/tarot/death
+ name = "XIII - Death"
+ desc = "Lay waste to all that oppose you."
+ card_icon = "death"
+
+/datum/tarot/death/activate(mob/living/target)
+ for(var/mob/living/L in oview(9, target))
+ L.adjustBruteLoss(20)
+ L.adjustFireLoss(20)
+
+/datum/tarot/temperance
+ name = "XIV - Temperance"
+ desc = "May you be pure in heart."
+ card_icon = "temperance"
+
+/datum/tarot/temperance/activate(mob/living/target)
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/H = target
+ var/obj/item/organ/internal/body_egg/egg = H.get_int_organ(/obj/item/organ/internal/body_egg)
+ if(egg)
+ egg.remove(H)
+ H.vomit()
+ egg.forceMove(get_turf(H))
+ H.reagents.add_reagent("mutadone", 1)
+ for(var/obj/item/organ/internal/I in H.internal_organs)
+ I.heal_internal_damage(60)
+ H.apply_status_effect(STATUS_EFFECT_PANACEA)
+ for(var/thing in H.viruses)
+ var/datum/disease/D = thing
+ if(D.severity == NONTHREAT)
+ continue
+ D.cure()
+
+/datum/tarot/the_devil
+ name = "XV - The Devil"
+ desc = "Revel in the power of darkness."
+ card_icon = "the_devil"
+
+/datum/tarot/the_devil/activate(mob/living/target)
+ target.apply_status_effect(STATUS_EFFECT_SHADOW_MEND_DEVIL)
+
+/datum/tarot/the_tower
+ name = "XVI - The Tower"
+ desc = "Destruction brings creation."
+ card_icon = "the_tower"
+
+/datum/tarot/the_tower/activate(mob/living/target)
+ var/obj/item/grenade/clusterbuster/ied/bakoom = new(get_turf(target))
+ bakoom.prime()
+
+/// I'm sorry matt, this is very funny.
+/datum/tarot/the_stars
+ name = "XVII - The Stars"
+ desc = "May you find what you desire."
+ card_icon = "the_stars"
+
+/datum/tarot/the_stars/activate(mob/living/target)
+ var/list/L = list()
+ for(var/turf/T in get_area_turfs(/area/station/security/evidence))
+ if(is_blocked_turf(T))
+ continue
+ L.Add(T)
+
+ if(!length(L))
+ to_chat(target, "Huh. No evidence? Well, that means they can't charge you with a crime, right?")
+ return
+
+ target.forceMove(pick(L))
+ to_chat(target, "You are abruptly pulled through space!")
+ for(var/obj/structure/closet/C in shuffle(view(9, target)))
+ if(istype(C, /obj/structure/closet/secure_closet))
+ var/obj/structure/closet/secure_closet/SC = C
+ SC.locked = FALSE
+ C.open()
+ break //Only open one locker
+
+/datum/tarot/the_moon
+ name = "XVIII - The Moon"
+ desc = "May you find all you have lost."
+ card_icon = "the_moon"
+
+/datum/tarot/the_moon/activate(mob/living/target)
+ var/list/funny_ruin_list = list()
+ var/turf/target_turf = get_turf(target)
+ for(var/I in GLOB.ruin_landmarks)
+ var/obj/effect/landmark/ruin/ruin_landmark = I
+ if(ruin_landmark.z == target_turf.z)
+ funny_ruin_list += ruin_landmark
+
+ if(length(funny_ruin_list))
+ var/turf/T = get_turf(pick(funny_ruin_list))
+ target.forceMove(T)
+ to_chat(target, "You are abruptly pulled through space!")
+ T.ChangeTurf(/turf/simulated/floor/plating) //we give them plating so they are not trapped in a wall, and a pickaxe to avoid being trapped in a wall
+ new /obj/item/pickaxe/emergency(T)
+ target.update_parallax_contents()
+ return
+ //We did not find a ruin on the same level. Well. I hope you have a space suit, but we'll go space ruins as they are mostly sorta kinda safer.
+ for(var/I in GLOB.ruin_landmarks)
+ var/obj/effect/landmark/ruin/ruin_landmark = I
+ if(!is_mining_level(ruin_landmark.z))
+ funny_ruin_list += ruin_landmark
+
+ if(!length(funny_ruin_list))
+ to_chat(target, "Huh. No space ruins? Well, this card is RUINED!")
+
+ var/turf/T = get_turf(pick(funny_ruin_list))
+ target.forceMove(T)
+ to_chat(target, "You are abruptly pulled through space!")
+ T.ChangeTurf(/turf/simulated/floor/plating) //we give them plating so they are not trapped in a wall, and a pickaxe to avoid being trapped in a wall
+ new /obj/item/pickaxe/emergency(T)
+ target.update_parallax_contents()
+ return
+
+/datum/tarot/the_sun
+ name = "XIX - The Sun"
+ desc = "May the light heal and enlighten you."
+ card_icon = "the_sun"
+
+/datum/tarot/the_sun/activate(mob/living/target)
+ target.revive()
+
+/datum/tarot/judgement
+ name = "XX - Judgement"
+ desc = "Judge lest ye be judged."
+ card_icon = "judgement"
+
+/datum/tarot/judgement/activate(mob/living/target)
+ notify_ghosts("[target] has used a judgment card. Judge them. Or not, up to you.", enter_link = "(Click to judge)", source = target, action = NOTIFY_FOLLOW)
+
+/datum/tarot/the_world
+ name = "XXI - The World"
+ desc = "Open your eyes and see."
+ card_icon = "the_world"
+
+/datum/tarot/the_world/activate(mob/living/target)
+ var/datum/effect_system/smoke_spread/bad/smoke = new()
+ smoke.set_up(10, FALSE, target)
+ smoke.start()
+ target.apply_status_effect(STATUS_EFFECT_XRAY)
+
+////////////////////////////////
+////////REVERSED ARCANA/////////
+////////////////////////////////
+
+/datum/tarot/reversed/the_fool
+ name = "0 - The Fool?"
+ desc = "Let go and move on."
+ card_icon = "the_fool?"
+
+/datum/tarot/reversed/the_fool/activate(mob/living/target)
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/H = target
+ for(var/obj/item/I in H)
+ if(istype(/obj/item/bio_chip, I))
+ continue
+ H.unEquip(I)
+
+/datum/tarot/reversed/the_magician
+ name = "I - The Magician?"
+ desc = "May no harm come to you."
+ card_icon = "the_magician?"
+
+/datum/tarot/reversed/the_magician/activate(mob/living/target)
+ var/list/thrown_atoms = list()
+ var/sparkle_path = /obj/effect/temp_visual/gravpush
+ for(var/turf/T in range(5, target)) //Done this way so things don't get thrown all around hilariously.
+ for(var/atom/movable/AM in T)
+ thrown_atoms += AM
+
+ for(var/atom/movable/AM as anything in thrown_atoms)
+ if(AM == target || AM.anchored || (ismob(AM) && !isliving(AM)))
+ continue
+
+ var/throw_target = get_edge_target_turf(target, get_dir(target, get_step_away(AM, target)))
+ var/dist_from_user = get_dist(target, AM)
+ if(dist_from_user == 0)
+ if(isliving(AM))
+ var/mob/living/M = AM
+ M.Weaken(6 SECONDS)
+ M.adjustBruteLoss(10)
+ to_chat(M, "You're slammed into the floor by [name]!")
+ add_attack_logs(target, M, "[M] was thrown by [target]'s [name]", ATKLOG_ALMOSTALL)
+ else
+ new sparkle_path(get_turf(AM), get_dir(target, AM))
+ if(isliving(AM))
+ var/mob/living/M = AM
+ to_chat(M, "You're thrown back by [name]!")
+ add_attack_logs(target, M, "[M] was thrown by [target]'s [name]", ATKLOG_ALMOSTALL)
+ INVOKE_ASYNC(AM, TYPE_PROC_REF(/atom/movable, throw_at), throw_target, ((clamp((3 - (clamp(dist_from_user - 2, 0, dist_from_user))), 3, 3))), 1) //So stuff gets tossed around at the same time.
+
+/datum/tarot/reversed/the_high_priestess
+ name = "II - The High Priestess?"
+ desc = "Run."
+ card_icon = "the_high_priestess?"
+
+/datum/tarot/reversed/the_high_priestess/activate(mob/living/target)
+ target.visible_message("WHO DARES TO TRY TO USE MY POWER IN A CARD?")
+ target.apply_status_effect(STATUS_EFFECT_REVERSED_HIGH_PRIESTESS)
+
+/datum/tarot/reversed/the_empress
+ name = "III - The Empress?"
+ desc = "May your love bring protection."
+ card_icon = "the_empress?"
+
+/datum/tarot/reversed/the_empress/activate(mob/living/target)
+ for(var/mob/living/L in oview(9, target))
+ L.apply_status_effect(STATUS_EFFECT_PACIFIED)
+
+/datum/tarot/reversed/the_emperor
+ name = "IV - The Emperor?"
+ desc = "May you find a worthy opponent."
+ card_icon = "the_emperor?"
+
+/datum/tarot/reversed/the_emperor/activate(mob/living/target)
+ var/list/L = list()
+ var/list/heads = SSticker.mode.get_all_heads()
+ for(var/datum/mind/head in heads)
+ if(ishuman(head.current))
+ L.Add(head.current)
+
+ if(!length(L))
+ to_chat(target, "Huh. No command members? I hope you didn't kill them all already...")
+ return
+
+ target.forceMove(get_turf(pick(L)))
+ to_chat(target, "You are abruptly pulled through space!")
+
+/datum/tarot/reversed/the_hierophant
+ name = "V - The Hierophant?"
+ desc = "Two prayers for the forgotten."
+ card_icon = "the_hierophant?"
+
+/datum/tarot/reversed/the_hierophant/activate(mob/living/target)
+ var/active_chasers = 0
+ for(var/mob/living/M in shuffle(orange(7, target)))
+ if(M.stat == DEAD) //Let us not have dead mobs be used to make a disco inferno.
+ continue
+ if(active_chasers >= 2)
+ return
+ var/obj/effect/temp_visual/hierophant/chaser/C = new(get_turf(target), target, M, 1, FALSE)
+ C.moving = 2
+ C.standard_moving_before_recalc = 2
+ C.moving_dir = text2dir(pick("NORTH", "SOUTH", "EAST", "WEST"))
+ active_chasers++
+
+/datum/tarot/reversed/the_lovers
+ name = "VI - The Lovers?"
+ desc = "May your heart shatter to pieces."
+ card_icon = "the_lovers?"
+
+/datum/tarot/reversed/the_lovers/activate(mob/living/target)
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/H = target
+ H.apply_damage(20, BRUTE, BODY_ZONE_CHEST)
+ H.bleed(120)
+ var/obj/item/organ/external/chest = H.get_organ(BODY_ZONE_CHEST)
+ chest.fracture()
+ var/datum/organ/heart/datum_heart = H.get_int_organ_datum(ORGAN_DATUM_HEART)
+ var/obj/item/organ/internal/our_heart = datum_heart.linked_organ
+ our_heart.receive_damage(20, TRUE)
+
+/datum/tarot/reversed/the_chariot
+ name = "VII - The Chariot?"
+ desc = "May nothing walk past you."
+ card_icon = "the_chariot?"
+
+/datum/tarot/reversed/the_chariot/activate(mob/living/target)
+ target.Stun(4 SECONDS)
+ new /obj/structure/closet/statue/indestructible(get_turf(target), target)
+
+/datum/tarot/reversed/justice
+ name = "VIII - Justice?"
+ desc = "May your sins come back to torment you."
+ card_icon = "justice?"
+
+/datum/tarot/reversed/justice/activate(mob/living/target)
+ var/list/static/ignored_supply_pack_types = list(
+ /datum/supply_packs/abstract,
+ /datum/supply_packs/abstract/shuttle
+ )
+ var/chosen = pick(SSeconomy.supply_packs - ignored_supply_pack_types)
+ var/datum/supply_packs/the_pack = new chosen()
+ var/spawn_location = get_turf(target)
+ var/obj/structure/closet/crate/crate = the_pack.create_package(spawn_location)
+ crate.name = "magic [crate.name]"
+ qdel(the_pack)
+
+/datum/tarot/reversed/the_hermit
+ name = "IX - The Hermit?"
+ desc = "May you see the value of all things in life."
+ card_icon = "the_hermit?"
+
+/datum/tarot/reversed/the_hermit/activate(mob/living/target) //Someone can improve this in the future (hopefully comment will not be here in 10 years.)
+ for(var/obj/item/I in view(7, target))
+ if(istype(I, /obj/item/gun))
+ new /obj/item/stack/spacecash/c200(get_turf(I))
+ qdel(I)
+ continue
+ if(istype(I, /obj/item/grenade))
+ new /obj/item/stack/spacecash/c50(get_turf(I))
+ qdel(I)
+ if(istype(I, /obj/item/clothing/suit/armor))
+ new /obj/item/stack/spacecash/c100(get_turf(I))
+ qdel(I)
+ if(istype(I, /obj/item/melee/baton))
+ new /obj/item/stack/spacecash/c100(get_turf(I))
+ qdel(I)
+
+/datum/tarot/reversed/wheel_of_fortune
+ name = "X - Wheel of Fortune?"
+ desc = "Throw the dice of fate."
+ card_icon = "wheel_of_fortune?"
+
+/datum/tarot/reversed/wheel_of_fortune/activate(mob/living/target)
+ var/obj/item/dice/d20/fate/one_use/gonna_roll_a_one = new /obj/item/dice/d20/fate/one_use(get_turf(target))
+ gonna_roll_a_one.diceroll(target)
+
+/datum/tarot/reversed/strength
+ name = "XI - Strength?"
+ desc = "May you break their resolve."
+ card_icon = "strength?"
+
+/datum/tarot/reversed/strength/activate(mob/living/target)
+ for(var/mob/living/M in oview(9, target))
+ M.Hallucinate(2 MINUTES)
+ new /obj/effect/hallucination/delusion(get_turf(M), M)
+ M.adjustBrainLoss(30)
+
+/datum/tarot/reversed/the_hanged_man
+ name = "XII - The Hanged Man?"
+ desc = "May your greed know no bounds."
+ card_icon = "the_hanged_man?"
+
+/datum/tarot/reversed/the_hanged_man/activate(mob/living/target)
+ var/obj/structure/cursed_slot_machine/pull_the_lever_kronk = new /obj/structure/cursed_slot_machine(get_turf(target))
+ if(ishuman(target))
+ var/mob/living/carbon/human/WRONG_LEVER = target
+ pull_the_lever_kronk.attack_hand(WRONG_LEVER)
+
+/datum/tarot/reversed/death
+ name = "XIII - Death?"
+ desc = "May life spring forth from the fallen."
+ card_icon = "death?"
+
+/datum/tarot/reversed/death/activate(mob/living/target)
+ new /obj/structure/constructshell(get_turf(target))
+ new /obj/item/soulstone/anybody(get_turf(target))
+
+/datum/tarot/reversed/temperance
+ name = "XIV - Temperance?"
+ desc = "May your hunger be satiated."
+ card_icon = "temperance?"
+
+/datum/tarot/reversed/temperance/activate(mob/living/target)
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/H = target
+ for(var/i in 1 to 5)
+ var/datum/reagents/R = new /datum/reagents(10)
+ R.add_reagent(get_unrestricted_random_reagent_id(), 10)
+ R.reaction(H, REAGENT_INGEST)
+ R.trans_to(H, 10)
+ target.visible_message("[target] consumes 5 pills rapidly!")
+
+/datum/tarot/reversed/the_devil
+ name = "XV - The Devil?"
+ desc = "Bask in the light of your mercy."
+ card_icon = "the_devil?"
+
+/datum/tarot/reversed/the_devil/activate(mob/living/target)
+ var/obj/item/grenade/clusterbuster/i_hate_nians = new(get_turf(target))
+ i_hate_nians.prime()
+
+/datum/tarot/reversed/the_tower
+ name = "XVI - The Tower?"
+ desc = "Creation brings destruction."
+ card_icon = "the_tower?"
+
+/datum/tarot/reversed/the_tower/activate(mob/living/target)
+ for(var/turf/T in RANGE_TURFS(9, target))
+ if(locate(/mob/living) in T)
+ continue
+ if(istype(T, /turf/simulated/wall/indestructible))
+ continue
+ if(prob(66))
+ continue
+ T.ChangeTurf(/turf/simulated/mineral/random/labormineral)
+
+/datum/tarot/reversed/the_stars
+ name = "XVII - The Stars?"
+ desc = "May your loss bring fortune."
+ card_icon = "the_stars?"
+
+/datum/tarot/reversed/the_stars/activate(mob/living/target) //Heavy clone damage hit, but gain 2 cards. Not teathered to the card producer. Could lead to card stacking, but would require the sun to fix easily
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/H = target
+ H.adjustCloneLoss(50)
+ for(var/obj/item/organ/external/E in shuffle(H.bodyparts))
+ switch(rand(1,3))
+ if(1)
+ E.fracture()
+ if(2)
+ E.cause_internal_bleeding()
+ if(3)
+ E.cause_burn_wound()
+ break // I forgot the break the first time. Very funny.
+
+ H.drop_l_hand()
+ H.drop_r_hand()
+ var/obj/item/magic_tarot_card/MTC = new /obj/item/magic_tarot_card(get_turf(src))
+ var/obj/item/magic_tarot_card/MPC = new /obj/item/magic_tarot_card(get_turf(src))
+ H.put_in_hands(MTC)
+ H.put_in_hands(MPC)
+
+/datum/tarot/reversed/the_moon
+ name = "XVIII - The Moon?"
+ desc = "May you remember lost memories."
+ card_icon = "the_moon?"
+
+/datum/tarot/reversed/the_moon/activate(mob/living/target)
+ for(var/mob/living/L in view(5, target)) //Shorter range as this kinda can give away antagonists, though that is also funny.
+ target.mind.show_memory(L, 0) //Safe code? Bank accounts? PDA codes? It's yours my friend, as long as you have enough tarots
+
+/datum/tarot/reversed/the_sun
+ name = "XIX - The Sun?"
+ desc = "May the darkness swallow all around you."
+ card_icon = "the_sun?"
+
+/datum/tarot/reversed/the_sun/activate(mob/living/target)
+ target.apply_status_effect(STATUS_EFFECT_REVERSED_SUN)
+
+/datum/tarot/reversed/judgement
+ name = "XX - Judgement?"
+ desc = "May you redeem those found wanting" //Who wants more, but ghosts for something interesting
+ card_icon = "judgement?"
+
+/datum/tarot/reversed/judgement/activate(mob/living/target)
+ var/datum/event_container/EC = SSevents.event_containers[EVENT_LEVEL_MODERATE]
+ var/decrease = 5 MINUTES
+ EC.next_event_time -= decrease
+ log_and_message_admins("decreased timer for [GLOB.severity_to_string[EC.severity]] events by 5 minutes by use of a [src].")
+
+/datum/tarot/reversed/the_world
+ name = "XXI - The World?"
+ desc = "Step into the abyss."
+ card_icon = "the_world?"
+
+/datum/tarot/reversed/the_world/activate(mob/living/target)
+ var/list/L = list()
+ for(var/turf/T in get_area_turfs(/area/mine/outpost)) //Lavaland is the abyss, but also too hot to send people too. Mining base should be fair!
+ if(is_blocked_turf(T))
+ continue
+ L.Add(T)
+
+ if(!length(L))
+ to_chat(target, "Hmm. No base? A miner issue.")
+ return
+
+ target.forceMove(pick(L))
+ to_chat(target, "You are abruptly pulled through space!")
diff --git a/code/game/gamemodes/wizard/rightandwrong.dm b/code/game/gamemodes/wizard/rightandwrong.dm
index 48e603a0994..0b2c52f3660 100644
--- a/code/game/gamemodes/wizard/rightandwrong.dm
+++ b/code/game/gamemodes/wizard/rightandwrong.dm
@@ -79,7 +79,9 @@ GLOBAL_LIST_INIT(summoned_magic, list(
/obj/item/scrying,
/obj/item/clothing/suit/space/hardsuit/wizard,
/obj/item/immortality_talisman,
- /obj/item/melee/ghost_sword))
+ /obj/item/melee/ghost_sword,
+ /obj/item/tarot_card_pack,
+ /obj/item/tarot_card_pack/jumbo))
GLOBAL_LIST_INIT(summoned_special_magic, list(
/obj/item/gun/magic/staff/animate,
@@ -87,7 +89,8 @@ GLOBAL_LIST_INIT(summoned_special_magic, list(
/obj/item/contract,
/obj/item/gun/magic/staff/chaos,
/obj/item/necromantic_stone,
- /obj/item/blood_contract))
+ /obj/item/blood_contract,
+ /obj/item/tarot_generator))
//everything above except for single use spellbooks, because they are counted separately (and are for basic bitches anyways)
GLOBAL_LIST_INIT(summoned_magic_objectives, list(
@@ -100,7 +103,8 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list(
/obj/item/necromantic_stone,
/obj/item/scrying,
/obj/item/spellbook,
- /obj/item/storage/belt/wands/full))
+ /obj/item/storage/belt/wands/full,
+ /obj/item/tarot_generator))
// If true, it's the probability of triggering "survivor" antag.
GLOBAL_VAR_INIT(summon_guns_triggered, FALSE)
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index 743079d34e5..d175fcbf6ad 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -483,6 +483,15 @@
cost = 1
category = "Artefacts"
+/datum/spellbook_entry/item/tarot_generator
+ name = "Enchanted tarot card deck"
+ desc = "An magic tarot card deck, enchanted with special Ink. \
+ Capable of producing magic tarot cards of the 22 major arcana, both normal and reversed. Each card has a different effect. \
+ Throw the card at someone to use it on them, or use it in hand to apply it to yourself. Unlimited uses, 12 second cooldown, can have up to 5 cards in the world."
+ item_path = /obj/item/tarot_generator/wizard
+ cost = 2
+ category = "Artefacts"
+
//Weapons and Armors
/datum/spellbook_entry/item/battlemage
name = "Battlemage Armor"
diff --git a/code/game/objects/effects/temporary_visuals/cult_visuals.dm b/code/game/objects/effects/temporary_visuals/cult_visuals.dm
index a1b2d4320a9..3f6d09dbaf6 100644
--- a/code/game/objects/effects/temporary_visuals/cult_visuals.dm
+++ b/code/game/objects/effects/temporary_visuals/cult_visuals.dm
@@ -9,6 +9,13 @@
name = "blood sparks"
icon_state = "bloodsparkles"
+/obj/effect/temp_visual/cult/sparks/hierophant
+ icon = 'icons/effects/effects.dmi'
+ randomdir = TRUE
+ duration = 12
+ name = "purple sparks"
+ icon_state = "hierophant_blast"
+
/obj/effect/temp_visual/dir_setting/cult/phase
name = "phase glow"
duration = 12
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 49c45f27fe8..18b6d11151a 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -514,7 +514,7 @@ GLOBAL_LIST_INIT(cult_recipes, list (
new /datum/stack_recipe/cult("runed girder (used to make cult walls)", /obj/structure/girder/cult, 1, time = 1 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE),
new /datum/stack_recipe/cult("pylon (heals nearby cultists)", /obj/structure/cult/functional/pylon, 4, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE),
new /datum/stack_recipe/cult("forge (crafts shielded robes, flagellant's robes, and mirror shields)", /obj/structure/cult/functional/forge, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE),
- new /datum/stack_recipe/cult("archives (crafts zealot's blindfolds, shuttle curse orbs, veil shifters, and reality sunderers)", /obj/structure/cult/functional/archives, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE),
+ new /datum/stack_recipe/cult("archives (crafts zealot's blindfolds, shuttle curse orbs, veil shifters, reality sunderers, and blank tarot cards)", /obj/structure/cult/functional/archives, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE),
new /datum/stack_recipe/cult("altar (crafts eldritch whetstones, construct shells, and flasks of unholy water)", /obj/structure/cult/functional/altar, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE, cult_structure = TRUE),
))
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index 7a825263df9..cc9c370b973 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -47,6 +47,21 @@
return
A.emag_act(user)
+/obj/item/card/emag/magic_key
+ name = "magic key"
+ desc = "It's a magic key, that will open one door!"
+ icon_state = "magic_key"
+ origin_tech = "magnets=2"
+
+/obj/item/card/emag/magic_key/afterattack(atom/target, mob/user, proximity)
+ if(!istype(target, /obj/machinery/door))
+ return
+ var/obj/machinery/door/D = target
+ D.locked = FALSE
+ update_icon()
+ . = ..()
+ qdel(src)
+
/obj/item/card/cmag
desc = "It's a card coated in a slurry of electromagnetic bananium."
name = "jestographic sequencer"
diff --git a/code/game/objects/items/weapons/grenades/clusterbuster.dm b/code/game/objects/items/weapons/grenades/clusterbuster.dm
index f08704aa25d..a03f07be8b0 100644
--- a/code/game/objects/items/weapons/grenades/clusterbuster.dm
+++ b/code/game/objects/items/weapons/grenades/clusterbuster.dm
@@ -227,6 +227,11 @@
desc = "For when you need to knock out EVERYONE."
payload = /obj/item/grenade/gas/knockout
+/obj/item/grenade/clusterbuster/ied
+ name = "\improper IED Cluster Grenade"
+ desc = "For when you need to do something between everything and nothing."
+ payload = /obj/item/grenade/iedcasing
+
////////////Clusterbuster of Clusterbusters////////////
//As a note: be extrodinarily careful about make the payload clusterbusters as it can quickly destroy the MC/Server
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 453c0e0c8cd..dbc5d3f5ced 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -720,7 +720,8 @@
/obj/item/warp_cube/red = 1,
/obj/item/reagent_containers/drinks/everfull = 2,
/obj/item/clothing/suit/space/hardsuit/wizard = 2,
- /obj/item/immortality_talisman = 1 ) //spells recharge when invincible
+ /obj/item/immortality_talisman = 1, //spells recharge when invincible
+ /obj/item/tarot_generator/wizard = 2)
var/obj/item/pickeda = pick(list_a)
value += list_a[pickeda]
new pickeda(src)
diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm
index 5dffb626cf7..0be43090cc9 100644
--- a/code/game/objects/structures/crates_lockers/closets/statue.dm
+++ b/code/game/objects/structures/crates_lockers/closets/statue.dm
@@ -117,3 +117,17 @@
user.dust()
dump_contents()
visible_message("[src] shatters!")
+
+/obj/structure/closet/statue/indestructible
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ timer = 240 SECONDS_TO_LIFE_CYCLES
+
+/obj/structure/closet/statue/indestructible/ex_act(severity)
+ return //No delimbing them
+
+/obj/structure/closet/statue/indestructible/shatter(mob/user)
+ return //No. Failsafe.
+
+/obj/structure/closet/statue/indestructible/singularity_act()
+ return //I mean maybe but no.
+
diff --git a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm
index c872499557d..be522b5ebb3 100644
--- a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm
+++ b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm
@@ -18,8 +18,12 @@
/obj/item/guardiancreator = 1, // jackpot.
/obj/item/spellbook/oneuse/knock = 1, // tresspassing charges incoming
/obj/item/gun/magic/wand/resurrection = 1, // medbay's best friend
+ /obj/item/tarot_generator = 1, // A little bit of everything, all of the time.
/obj/item/spellbook/oneuse/charge = 20, // and now for less useful stuff to dilute the good loot chances
/obj/item/spellbook/oneuse/summonitem = 20,
/obj/item/spellbook/oneuse/forcewall = 10,
- /obj/item/book/granter/spell/summon_cheese = 20 // hungry wizard stuff
+ /obj/item/tarot_card_pack = 10,
+ /obj/item/tarot_card_pack/jumbo = 6,
+ /obj/item/tarot_card_pack/mega = 4,
+ /obj/item/book/granter/spell/summon_cheese = 15 // hungry wizard stuff
)
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 852149ec771..a339ad902a0 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -611,6 +611,23 @@
/obj/item/clothing/suit/proc/setup_shielding()
return
+///Hierophant card shielding. Saves me time.
+/obj/item/clothing/suit/proc/setup_hierophant_shielding()
+ var/datum/component/shielded/shield = GetComponent(/datum/component/shielded)
+ if(!shield)
+ AddComponent(/datum/component/shielded, recharge_start_delay = 0 SECONDS, shield_icon = "shield-hierophant", run_hit_callback = CALLBACK(src, PROC_REF(hierophant_shield_damaged)))
+ return
+ if(shield.shield_icon == "shield-hierophant") //If the hierophant shield has been used, recharge it. Otherwise, it's a shielded component we don't want to touch
+ shield.current_charges = 3
+
+/// A proc for callback when the shield breaks, since I am stupid and want custom effects.
+/obj/item/clothing/suit/proc/hierophant_shield_damaged(mob/living/wearer, attack_text, new_current_charges)
+ wearer.visible_message("[attack_text] is deflected in a burst of dark-purple sparks!")
+ new /obj/effect/temp_visual/cult/sparks/hierophant(get_turf(wearer))
+ playsound(wearer,'sound/magic/blind.ogg', 200, TRUE, -2)
+ if(new_current_charges == 0)
+ wearer.visible_message("The runed shield around [wearer] suddenly disappears!")
+
//Proc that opens and closes jackets.
/obj/item/clothing/suit/proc/adjustsuit(mob/user)
if(ignore_suitadjust)
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index 65386ffc6de..099d24c5e85 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -69,6 +69,18 @@
if(23)
new /obj/item/borg/upgrade/modkit/lifesteal(src)
new /obj/item/bedsheet/cult(src)
+ if(24)
+ switch(rand(1, 11))
+ if(1)
+ new /obj/item/blank_tarot_card(src)
+ if(2 to 5)
+ new /obj/item/tarot_card_pack(src)
+ if(6 to 8)
+ new /obj/item/tarot_card_pack/jumbo(src)
+ if(9, 10)
+ new /obj/item/tarot_card_pack/mega(src)
+ if(11)
+ new /obj/item/tarot_generator(src) // ~1/250? Seems reasonable
//KA modkit design discs
/obj/item/disk/design_disk/modkit_disk
diff --git a/code/modules/reagents/chemistry/reagents_holder.dm b/code/modules/reagents/chemistry/reagents_holder.dm
index 851788b90d8..4d68ff36daa 100644
--- a/code/modules/reagents/chemistry/reagents_holder.dm
+++ b/code/modules/reagents/chemistry/reagents_holder.dm
@@ -969,6 +969,17 @@
var/picked_reagent = pick(random_reagents)
return picked_reagent
+/// Returns a random reagent ID, with real non blacklisted balance boosting action!
+/proc/get_unrestricted_random_reagent_id()
+ var/static/list/random_reagents
+ if(!length(random_reagents))
+ random_reagents = list()
+ for(var/datum/reagent/thing as anything in subtypesof(/datum/reagent))
+ var/R = initial(thing.id)
+ random_reagents += R
+ var/picked_reagent = pick(random_reagents)
+ return picked_reagent
+
/datum/reagents/proc/get_reagent_from_id(id)
var/datum/reagent/result = null
for(var/A in reagent_list)
diff --git a/code/modules/station_goals/bluespace_tap.dm b/code/modules/station_goals/bluespace_tap.dm
index cfa51561e12..3f88756eeb3 100644
--- a/code/modules/station_goals/bluespace_tap.dm
+++ b/code/modules/station_goals/bluespace_tap.dm
@@ -116,7 +116,11 @@
/obj/item/bedsheet/cult = 2,
/obj/item/bedsheet/wiz = 2,
/obj/item/stack/sheet/mineral/tranquillite/fifty = 3,
- /obj/item/clothing/gloves/combat = 5
+ /obj/item/clothing/gloves/combat = 5,
+ /obj/item/blank_tarot_card = 5,
+ /obj/item/tarot_card_pack = 5,
+ /obj/item/tarot_card_pack/jumbo = 3,
+ /obj/item/tarot_card_pack/mega = 2
)
/obj/effect/spawner/lootdrop/bluespace_tap/organic
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index d2419382171..656a8380279 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi
index 7596aae9be0..3818dd761e9 100644
Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ
diff --git a/icons/obj/playing_cards.dmi b/icons/obj/playing_cards.dmi
index 866cf0a5fc4..58d9508af72 100644
Binary files a/icons/obj/playing_cards.dmi and b/icons/obj/playing_cards.dmi differ
diff --git a/paradise.dme b/paradise.dme
index be76cb94d91..6774197686a 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -752,6 +752,7 @@
#include "code\game\gamemodes\vampire\vampire_gamemode.dm"
#include "code\game\gamemodes\wizard\artefact.dm"
#include "code\game\gamemodes\wizard\godhand.dm"
+#include "code\game\gamemodes\wizard\magic_tarot.dm"
#include "code\game\gamemodes\wizard\raginmages.dm"
#include "code\game\gamemodes\wizard\rightandwrong.dm"
#include "code\game\gamemodes\wizard\soulstone.dm"