diff --git a/code/modules/games/52card.dm b/code/modules/games/52card.dm
new file mode 100644
index 00000000000..036df3dd0c3
--- /dev/null
+++ b/code/modules/games/52card.dm
@@ -0,0 +1,50 @@
+/obj/item/deck/holder
+ name = "card box"
+ desc = "A small leather case to show how classy you are compared to everyone else."
+ icon_state = "card_holder"
+
+/obj/item/deck/cards
+ name = "deck of cards"
+ desc = "A simple deck of playing cards."
+ icon_state = "deck"
+
+/obj/item/deck/cards/New()
+ ..()
+ for(var/suit in list("spades","clubs","diamonds","hearts"))
+ var/colour
+ if(suit == "spades" || suit == "clubs")
+ colour = "black_"
+ else
+ colour = "red_"
+
+ for(var/number in list("ace","two","three","four","five","six","seven","eight","nine","ten"))
+ cards += new /datum/playingcard("[number] of [suit]", "[colour]num")
+
+ for(var/number in list("jack","queen","king"))
+ cards += new /datum/playingcard("[number] of [suit]", "[colour]col")
+
+ for(var/i = 0, i<2, i++)
+ cards += new /datum/playingcard("joker", "joker")
+
+/obj/item/deck/doublecards
+ name = "double deck of cards"
+ desc = "A simple deck of playing cards. Multiplied by two. Does not necessarily come with twice the fun."
+ icon_state = "doubledeck"
+
+/obj/item/deck/doublecards/New()
+ ..()
+ for(var/f = 0, f<2, f++)
+ for(var/suit in list("spades","clubs","diamonds","hearts"))
+
+ var/colour
+ if(suit == "spades" || suit == "clubs")
+ colour = "black_"
+ else
+ colour = "red_"
+ for(var/number in list("ace","two","three","four","five","six","seven","eight","nine","ten",))
+ cards += new /datum/playingcard("[number] of [suit]", "[colour]num")
+ for(var/number in list("jack","queen","king"))
+ cards += new /datum/playingcard("[number] of [suit]", "[colour]col")
+
+ for(var/i = 0, i<2, i++)
+ cards += new /datum/playingcard("joker", "joker")
diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm
new file mode 100644
index 00000000000..e14aaa09ba7
--- /dev/null
+++ b/code/modules/games/cards.dm
@@ -0,0 +1,481 @@
+/datum/playingcard
+ var/name = "playing card"
+ var/card_icon = "card_back"
+ var/back_icon = "card_back"
+
+/datum/playingcard/New(newname, newcard_icon, newback_icon)
+ ..()
+ if(newname)
+ name = newname
+ if(newcard_icon)
+ card_icon = newcard_icon
+ if(newback_icon)
+ back_icon = newback_icon
+
+/obj/item/deck
+ w_class = WEIGHT_CLASS_SMALL
+ icon = 'icons/obj/playing_cards.dmi'
+ actions_types = list(/datum/action/item_action/draw_card, /datum/action/item_action/deal_card, /datum/action/item_action/deal_card_multi, /datum/action/item_action/shuffle)
+ var/list/cards = list()
+ var/cooldown = 0 // to prevent spam shuffle
+ throw_speed = 3
+ throw_range = 10
+ throwforce = 0
+ force = 0
+
+/obj/item/deck/attackby(obj/O as obj, mob/user as mob)
+ if(istype(O,/obj/item/cardhand))
+ var/obj/item/cardhand/H = O
+ if(H.parentdeck == src)
+ for(var/datum/playingcard/P in H.cards)
+ cards += P
+ qdel(H)
+ to_chat(user,"You place your cards on the bottom of [src].")
+ return
+ else
+ to_chat(user,"You can't mix cards from different decks!")
+ return
+
+ ..()
+
+/obj/item/deck/examine(mob/user)
+ ..()
+ to_chat(user,"It contains [cards.len ? cards.len : "no"] cards")
+
+/obj/item/deck/attack_hand(mob/user as mob)
+ draw_card(user)
+
+// Datum actions
+/datum/action/item_action/draw_card
+ name = "Draw - Draw one card"
+ button_icon_state = "draw"
+ use_itemicon = FALSE
+
+/datum/action/item_action/draw_card/Trigger()
+ if(istype(target, /obj/item/deck))
+ var/obj/item/deck/D = target
+ return D.draw_card(owner)
+ return ..()
+
+/datum/action/item_action/deal_card
+ name = "Deal - deal one card to a person next to you"
+ button_icon_state = "deal_card"
+ use_itemicon = FALSE
+
+/datum/action/item_action/deal_card/Trigger()
+ if(istype(target, /obj/item/deck))
+ var/obj/item/deck/D = target
+ return D.deal_card()
+ return ..()
+
+/datum/action/item_action/deal_card_multi
+ name = "Deal multiple card - Deal multiple card to a person next to you"
+ button_icon_state = "deal_card_multi"
+ use_itemicon = FALSE
+
+/datum/action/item_action/deal_card_multi/Trigger()
+ if(istype(target, /obj/item/deck))
+ var/obj/item/deck/D = target
+ return D.deal_card_multi()
+ return ..()
+
+/datum/action/item_action/shuffle
+ name = "Shuffle - shuffle the deck"
+ button_icon_state = "shuffle"
+ use_itemicon = FALSE
+
+/datum/action/item_action/shuffle/Trigger()
+ if(istype(target, /obj/item/deck))
+ var/obj/item/deck/D = target
+ return D.deckshuffle()
+ return ..()
+
+// Datum actions
+
+/obj/item/deck/verb/draw_card(mob/user)
+
+ set category = "Object"
+ set name = "Draw"
+ set desc = "Draw a card from a deck."
+ set src in view(1)
+
+ var/mob/living/carbon/human/M = user
+
+ if(user.incapacitated() || !Adjacent(user))
+ return
+
+ if(!cards.len)
+ to_chat(user,"There are no cards in the deck.")
+ return
+
+ var/obj/item/cardhand/H = M.is_in_hands(/obj/item/cardhand)
+ if(H && !(H.parentdeck == src))
+ to_chat(user,"You can't mix cards from different decks!")
+ return
+ if(H && ((1 + H.cards.len) > H.maxcardlen))
+ to_chat(user,"You can't hold that many cards in one hand!")
+ return
+
+ if(!H)
+ H = new(get_turf(src))
+ user.put_in_hands(H)
+
+ var/datum/playingcard/P = cards[1]
+ H.cards += P
+ cards -= P
+ H.parentdeck = src
+ H.update_icon()
+ user.visible_message("[user] draws a card.","You draws a card.")
+ to_chat(user,"It's [P].")
+
+/obj/item/deck/verb/deal_card()
+
+ set category = "Object"
+ set name = "Deal"
+ set desc = "Deal a card from a deck."
+ set src in view(1)
+
+ if(usr.incapacitated() || !Adjacent(usr))
+ return
+
+ if(!cards.len)
+ to_chat(usr,"There are no cards in the deck.")
+ return
+
+ var/list/players = list()
+ for(var/mob/living/player in viewers(3))
+ if(!player.incapacitated())
+ players += player
+ //players -= usr
+
+ var/mob/living/M = input("Who do you wish to deal a card to?") as null|anything in players
+ if(!usr || !src || !M) return
+
+ deal_at(usr, M, 1)
+
+/obj/item/deck/verb/deal_card_multi()
+
+ set category = "Object"
+ set name = "Deal Multiple Cards"
+ set desc = "Deal multiple cards from a deck."
+ set src in view(1)
+
+ if(usr.incapacitated() || !Adjacent(usr))
+ return
+
+ if(!cards.len)
+ to_chat(usr,"There are no cards in the deck.")
+ return
+
+ var/list/players = list()
+ for(var/mob/living/player in viewers(3))
+ if(!player.incapacitated())
+ players += player
+ var/maxcards = max(min(cards.len,10),1)
+ var/dcard = input("How many card(s) do you wish to deal? You may deal up to [maxcards] cards.") as num
+ if(dcard > maxcards)
+ return
+ var/mob/living/M = input("Who do you wish to deal [dcard] card(s)?") as null|anything in players
+ if(!usr || !src || !M || !Adjacent(usr))
+ return
+
+ deal_at(usr, M, dcard)
+
+/obj/item/deck/proc/deal_at(mob/user, mob/target, dcard) // Take in the no. of card to be dealt
+ var/obj/item/cardhand/H = new(get_step(user, user.dir))
+ for(var/i in 1 to dcard)
+ H.cards += cards[1]
+ cards -= cards[1]
+ H.parentdeck = src
+ H.concealed = TRUE
+ H.update_icon()
+ if(user == target)
+ user.visible_message("[user] deals [dcard] card(s) to \himself.")
+ else
+ user.visible_message("[user] deals [dcard] card(s) to [target].")
+ H.throw_at(get_step(target,target.dir),3,1,H)
+
+
+/obj/item/deck/attack_self()
+ deckshuffle()
+
+/obj/item/deck/verb/verb_shuffle()
+ set category = "Object"
+ set name = "Shuffle"
+ set desc = "Shuffle the cards in the deck."
+ set src in view(1)
+ deckshuffle()
+
+/obj/item/deck/proc/deckshuffle()
+ var/mob/living/user = usr
+ if(cooldown < world.time - 5 SECONDS)
+ cards = shuffle(cards)
+ user.visible_message("[user] shuffles [src].")
+ playsound(user, 'sound/items/cardshuffle.ogg', 50, 1)
+ cooldown = world.time
+
+
+/obj/item/deck/MouseDrop(atom/over_object) // Code from Paper bin, so you can still pick up the deck
+ var/mob/M = usr
+ if(M.incapacitated() || !Adjacent(M))
+ return
+
+ if(over_object == M)
+ M.put_in_hands(src)
+
+ else if(istype(over_object, /obj/screen))
+ switch(over_object.name)
+ if("r_hand")
+ if(!remove_item_from_storage(M))
+ M.unEquip(src)
+ M.put_in_r_hand(src)
+ if("l_hand")
+ if(!remove_item_from_storage(M))
+ M.unEquip(src)
+ M.put_in_l_hand(src)
+
+ add_fingerprint(M)
+ usr.visible_message("[usr] picks up the deck.")
+
+/obj/item/pack
+ name = "Card Pack"
+ desc = "For those with disposable income."
+
+ icon_state = "card_pack"
+ icon = 'icons/obj/playing_cards.dmi'
+ w_class = WEIGHT_CLASS_TINY
+ var/list/cards = list()
+ var/parentdeck = null // For future card pack that need to be compatible with eachother i.e. cardemon
+
+
+/obj/item/pack/attack_self(mob/user as mob)
+ user.visible_message("[name] rips open the [src]!", "You rips open the [src]!")
+ var/obj/item/cardhand/H = new()
+
+ H.cards += cards
+ cards.Cut()
+ user.unEquip(src, force = 1)
+ qdel(src)
+
+ H.update_icon()
+ user.put_in_active_hand(H)
+
+/obj/item/cardhand
+ name = "hand of cards"
+ desc = "Some playing cards."
+ icon = 'icons/obj/playing_cards.dmi'
+ icon_state = "empty"
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 4
+ throw_range = 20
+ throwforce = 0
+ force = 0
+ var/maxcardlen = 20
+ actions_types = list(/datum/action/item_action/remove_card, /datum/action/item_action/discard)
+
+ var/concealed = FALSE
+ var/list/cards = list()
+ var/parentdeck = null
+
+/obj/item/cardhand/attackby(obj/O as obj, mob/user as mob)
+ if(cards.len == 1 && istype(O, /obj/item/pen))
+ var/datum/playingcard/P = cards[1]
+ if(P.name != "Blank Card")
+ to_chat(user,"You cannot write on that card.")
+ return
+ var/cardtext = sanitize(input(user, "What do you wish to write on the card?", "Card Editing") as text|null, MAX_PAPER_MESSAGE_LEN)
+ if(!cardtext)
+ return
+ P.name = cardtext
+ // SNOWFLAKE FOR CAG, REMOVE IF OTHER CARDS ARE ADDED THAT USE THIS.
+ P.card_icon = "cag_white_card"
+ update_icon()
+ else if(istype(O,/obj/item/cardhand))
+ var/obj/item/cardhand/H = O
+ if((H.cards.len + cards.len) > maxcardlen)
+ to_chat(user,"You can't hold that many cards in one hand!")
+ return
+ if(H.parentdeck == src.parentdeck)
+ for(var/datum/playingcard/P in cards)
+ H.cards += P
+ H.concealed = src.concealed
+ qdel(src)
+ H.update_icon()
+ return
+ else
+ to_chat(user,"You cannot mix cards from other deck!")
+ return
+ ..()
+
+/obj/item/cardhand/attack_self(var/mob/user as mob)
+ concealed = !concealed
+ update_icon()
+ user.visible_message("[user] [concealed ? "conceals" : "reveals"] their hand.")
+
+/obj/item/cardhand/examine(mob/user)
+ ..(user)
+ if((!concealed) && cards.len)
+ to_chat(user,"It contains:")
+ for(var/datum/playingcard/P in cards)
+ to_chat(user,"the [P.name].")
+
+// Datum action here
+
+/datum/action/item_action/remove_card
+ name = "Remove a card - Remove a single card from the hand."
+ button_icon_state = "remove_card"
+ use_itemicon = FALSE
+
+/datum/action/item_action/remove_card/Trigger()
+ if(istype(target, /obj/item/cardhand))
+ var/obj/item/cardhand/C = target
+ return C.Removecard()
+ return ..()
+
+/datum/action/item_action/discard
+ name = "Discard - Place (a) card(s) from your hand in front of you."
+ button_icon_state = "discard"
+ use_itemicon = FALSE
+
+/datum/action/item_action/discard/Trigger()
+ if(istype(target, /obj/item/cardhand))
+ var/obj/item/cardhand/C = target
+ return C.discard()
+ return ..()
+
+// No more datum action here
+
+/obj/item/cardhand/verb/Removecard()
+
+ set category = "Object"
+ set name = "Remove card"
+ set desc = "Remove a card from the hand."
+ set src in view(1)
+
+ var/mob/living/carbon/user = usr
+
+ if(user.incapacitated() || !Adjacent(user))
+ return
+
+ var/pickablecards = list()
+ for(var/datum/playingcard/P in cards)
+ pickablecards[P.name] = P
+ var/pickedcard = input("Which card do you want to remove from the hand?") as null|anything in pickablecards
+
+ if(QDELETED(src))
+ return
+
+ var/datum/playingcard/card = pickablecards[pickedcard]
+
+ var/obj/item/cardhand/H = new(get_turf(src))
+ user.put_in_active_hand(H)
+ H.cards += card
+ cards -= card
+ H.parentdeck = parentdeck
+ H.concealed = concealed
+ H.update_icon()
+ update_icon()
+
+ if(!cards.len)
+ qdel(src)
+
+/obj/item/cardhand/verb/discard(var/mob/user as mob)
+
+ set category = "Object"
+ set name = "Discard"
+ set desc = "Place (a) card(s) from your hand in front of you."
+
+ var/maxcards = min(cards.len,5)
+ var/discards = input("How many cards do you want to discard? You may discard up to [maxcards] card(s)") as num
+ if(discards > maxcards)
+ return
+ for(var/i in 1 to discards)
+ var/list/to_discard = list()
+ for(var/datum/playingcard/P in cards)
+ to_discard[P.name] = P
+ var/discarding = input("Which card do you wish to put down?") as null|anything in to_discard
+
+ if(QDELETED(src))
+ return
+
+ var/datum/playingcard/card = to_discard[discarding]
+ to_discard.Cut()
+
+ var/obj/item/cardhand/H = new(get_turf(src))
+ H.cards += card
+ cards -= card
+ H.concealed = FALSE
+ H.parentdeck = parentdeck
+ H.update_icon()
+ if(cards.len)
+ update_icon()
+ if(H.cards.len)
+ usr.visible_message("The [user] plays the [discarding].", "You play the [discarding].")
+ H.loc = get_step(usr,usr.dir)
+
+ if(!cards.len)
+ qdel(src)
+
+/obj/item/cardhand/update_icon(var/direction = 0)
+
+ if(!cards.len)
+ qdel(src)
+ return
+ else if(cards.len > 1)
+ name = "hand of cards"
+ desc = "Some playing cards."
+ else
+ name = "a playing card"
+ desc = "A playing card."
+
+ overlays.Cut()
+
+ if(cards.len == 1)
+ var/datum/playingcard/P = cards[1]
+ var/image/I = new(icon, (concealed ? "[P.back_icon]" : "[P.card_icon]") )
+ I.pixel_x += (-5+rand(10))
+ I.pixel_y += (-5+rand(10))
+ overlays += I
+ return
+
+ var/offset = Floor(20/cards.len + 1)
+
+ var/matrix/M = matrix()
+ if(direction)
+ switch(direction)
+ if(NORTH)
+ M.Translate( 0, 0)
+ if(SOUTH)
+ M.Translate( 0, 4)
+ if(WEST)
+ M.Turn(90)
+ M.Translate( 3, 0)
+ if(EAST)
+ M.Turn(90)
+ M.Translate(-2, 0)
+ var/i = 0
+ for(var/datum/playingcard/P in cards)
+ var/image/I = new(icon, (concealed ? "[P.back_icon]" : "[P.card_icon]") )
+ //I.pixel_x = origin+(offset*i)
+ switch(direction)
+ if(SOUTH)
+ I.pixel_x = 8-(offset*i)
+ if(WEST)
+ I.pixel_y = -6+(offset*i)
+ if(EAST)
+ I.pixel_y = 8-(offset*i)
+ else
+ I.pixel_x = -7+(offset*i)
+ I.transform = M
+ overlays += I
+ i++
+
+/obj/item/cardhand/dropped(mob/user as mob)
+ ..()
+ if(locate(/obj/structure/table, loc))
+ update_icon(user.dir)
+ else
+ update_icon()
+
+/obj/item/cardhand/pickup(mob/user as mob)
+ update_icon()
diff --git a/code/modules/games/tarot.dm b/code/modules/games/tarot.dm
new file mode 100644
index 00000000000..bd26c34d437
--- /dev/null
+++ b/code/modules/games/tarot.dm
@@ -0,0 +1,32 @@
+/* this is a playing card deck based off of the Rider-Waite Tarot Deck.
+*/
+
+/obj/item/deck/tarot
+ name = "deck of tarot cards"
+ desc = "For all your occult needs!"
+ icon_state = "deck_tarot"
+
+/obj/item/deck/tarot/New()
+ ..()
+
+ for(var/tarotname in list("Fool","Magician","High Priestess","Empress","Emperor","Hierophant","Lovers","Chariot","Strength","Hermit","Wheel of Fortune","Justice","Hanged Man","Death","Temperance","Devil","Tower","Star","Moon","Sun","Judgement","World"))
+ cards += new /datum/playingcard("[tarotname]", "tarot_major", "card_back_tarot")
+ for(var/suit in list("wands","pentacles","cups","swords"))
+ for(var/number in list("ace","two","three","four","five","six","seven","eight","nine","ten","page","knight","queen","king"))
+ cards += new /datum/playingcard("[number] of [suit]", "tarot_[suit]", "card_back_tarot")
+
+/obj/item/deck/tarot/deckshuffle()
+ var/mob/living/user = usr
+ if(cooldown < world.time - 5 SECONDS)
+ var/list/newcards = list()
+ while(cards.len)
+ var/datum/playingcard/P = pick(cards)
+ P.name = replacetext(P.name," reversed","")
+ if(prob(50))
+ P.name += " reversed"
+ newcards += P
+ cards -= P
+ cards = newcards
+ playsound(user, 'sound/items/cardshuffle.ogg', 50, 1)
+ user.visible_message("[user] shuffles [src].", "You shuffle the [src].")
+ cooldown = world.time
diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi
index 3acfa100085..5dd923111e6 100644
Binary files a/icons/mob/actions.dmi and b/icons/mob/actions.dmi differ
diff --git a/icons/obj/playing_cards.dmi b/icons/obj/playing_cards.dmi
new file mode 100644
index 00000000000..315e4335979
Binary files /dev/null and b/icons/obj/playing_cards.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 4415ebe5f48..09d89d97bcd 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1416,6 +1416,9 @@
#include "code\modules\food_and_drinks\recipes\recipes_microwave.dm"
#include "code\modules\food_and_drinks\recipes\recipes_oven.dm"
#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_table.dm"
+#include "code\modules\games\52card.dm"
+#include "code\modules\games\cards.dm"
+#include "code\modules\games\tarot.dm"
#include "code\modules\holiday\christmas.dm"
#include "code\modules\holiday\holiday.dm"
#include "code\modules\hydroponics\biogenerator.dm"