Card Shark DLC - GIMMIE MY MONEY OR I BREAK YOUR KNEECAPS (#64200)

Co-authored-by: Seth Scherer <supernovaa41@gmx.com>
Co-authored-by: Kylerace <kylerlumpkin1@gmail.com>
This commit is contained in:
Tim
2022-05-02 16:56:30 -07:00
committed by GitHub
co-authored by Seth Scherer Kylerace
parent 115f4284b6
commit 22aa3566ab
37 changed files with 1112 additions and 792 deletions
+143
View File
@@ -0,0 +1,143 @@
/obj/item/toy/cards/cardhand
name = "hand of cards"
desc = "A number of cards not in a deck, customarily held in ones hand."
icon = 'icons/obj/playing_cards.dmi'
icon_state = "none"
w_class = WEIGHT_CLASS_TINY
worn_icon_state = "card"
/obj/item/toy/cards/cardhand/Initialize(mapload, list/cards_to_combine)
. = ..()
if(!LAZYLEN(cards_to_combine) && (mapload && !LAZYLEN(cards)))
CRASH("[src] is being made into a cardhand without a list of cards to combine")
if(mapload && LAZYLEN(cards)) // these cards have not been initialized
for(var/card_name in cards)
var/obj/item/toy/singlecard/new_card = new (loc, card_name)
new_card.update_appearance()
cards_to_combine += new_card
cards = list() // reset our cards to an empty list
for(var/obj/item/toy/singlecard/new_card in cards_to_combine)
new_card.forceMove(src)
cards += new_card
register_context()
update_appearance()
/obj/item/toy/cards/cardhand/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is slitting [user.p_their()] wrists with \the [src]! It looks like [user.p_they()] [user.p_have()] a crummy hand!"))
playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
return BRUTELOSS
/obj/item/toy/cards/cardhand/examine(mob/user)
. = ..()
for(var/obj/item/toy/singlecard/card in cards)
if(HAS_TRAIT(user, TRAIT_XRAY_VISION))
. += span_notice("You scan the cardhand with your x-ray vision and there is a: [card.cardname]")
var/marked_color = card.getMarkedColor(user)
if(marked_color)
. += span_notice("There is a [marked_color] mark on the corner of a card in the cardhand!")
/obj/item/toy/cards/cardhand/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
if(istype(held_item, /obj/item/toy/cards/deck))
var/obj/item/toy/cards/deck/dealer_deck = held_item
if(dealer_deck.wielded)
context[SCREENTIP_CONTEXT_LMB] = "Deal card"
context[SCREENTIP_CONTEXT_RMB] = "Deal card faceup"
return CONTEXTUAL_SCREENTIP_SET
context[SCREENTIP_CONTEXT_LMB] = "Recycle cards"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/toy/singlecard))
context[SCREENTIP_CONTEXT_LMB] = "Combine cards"
context[SCREENTIP_CONTEXT_RMB] = "Combine cards faceup"
return CONTEXTUAL_SCREENTIP_SET
return NONE
/obj/item/toy/cards/cardhand/attack_self(mob/living/user)
if(!isliving(user) || !user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, NO_TK))
return
var/list/handradial = list()
for(var/obj/item/toy/singlecard/card in cards)
handradial[card] = image(icon = src.icon, icon_state = card.icon_state)
var/obj/item/toy/singlecard/choice = show_radial_menu(usr, src, handradial, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 36, require_near = TRUE)
if(!choice)
return FALSE
var/obj/item/toy/singlecard/selected_card = draw(user, choice)
selected_card.pickup(user)
user.put_in_hands(selected_card)
if(cards.len == 1)
user.temporarilyRemoveItemFromInventory(src, TRUE)
var/obj/item/toy/singlecard/last_card = draw(user)
last_card.pickup(user)
user.put_in_hands(last_card)
qdel(src) // cardhand is empty now so delete it
/obj/item/toy/cards/cardhand/proc/check_menu(mob/living/user)
return isliving(user) && !user.incapacitated()
/obj/item/toy/cards/cardhand/attackby(obj/item/weapon, mob/living/user, params, flip_card = FALSE)
var/obj/item/toy/singlecard/card
if(istype(weapon, /obj/item/toy/singlecard))
card = weapon
if(istype(weapon, /obj/item/toy/cards/deck))
var/obj/item/toy/cards/deck/dealer_deck = weapon
if(!dealer_deck.wielded) // recycle cardhand into deck (if unwielded)
dealer_deck.insert(src)
user.balloon_alert_to_viewers("puts card in deck")
return
card = dealer_deck.draw(user)
if(card)
if(flip_card)
card.Flip()
insert(card)
return
return ..()
/obj/item/toy/cards/cardhand/attackby_secondary(obj/item/weapon, mob/user, params)
attackby(weapon, user, params, flip_card = TRUE)
return SECONDARY_ATTACK_CONTINUE_CHAIN
#define CARDS_MAX_DISPLAY_LIMIT 5 // the amount of cards that are displayed in a hand
#define CARDS_PIXEL_X_OFFSET -5 // start out displaying the 1st card -5 pixels left
#define CARDS_ANGLE_OFFSET -45 // start out displaying the 1st card -45 degrees counter clockwise
/obj/item/toy/cards/cardhand/update_overlays()
. = ..()
cut_overlays()
if(cards.len <= 1)
icon_state = null // we want an error icon to appear if this doesn't get qdel
return
var/starting_card_pos = max(1, cards.len - CARDS_MAX_DISPLAY_LIMIT) // only display the top cards in the cardhand
var/cards_to_display = min(CARDS_MAX_DISPLAY_LIMIT, cards.len)
// 90 degrees from the 1st card to the last, so split the divider by total cards displayed
var/angle_divider = round(90/(cards_to_display - 1))
// 10 pixels from the 1st card to the last, so split the divider by total cards displayed
var/pixel_divider = round(10/(cards_to_display - 1))
// starting from the 1st card to last, we want to slowly increase the angle and pixel_x offset
// to spread the cards out using our dividers
for(var/i in 0 to cards_to_display - 1)
var/obj/item/toy/singlecard/card = cards[starting_card_pos + i]
var/image/card_overlay = image(icon, icon_state = card.icon_state, pixel_x = CARDS_PIXEL_X_OFFSET + (i * pixel_divider))
var/rotation_angle = CARDS_ANGLE_OFFSET + (i * angle_divider)
var/matrix/M = matrix()
M.Turn(rotation_angle)
card_overlay.transform = M
add_overlay(card_overlay)
#undef CARDS_MAX_DISPLAY_LIMIT
#undef CARDS_PIXEL_X_OFFSET
#undef CARDS_ANGLE_OFFSET
+114
View File
@@ -0,0 +1,114 @@
/*
** The base card class that is used by decks and cardhands
*/
/obj/item/toy/cards
resistance_flags = FLAMMABLE
max_integrity = 50
/// Do all the cards drop to the floor when thrown at a person
var/can_play_52_card_pickup = TRUE
/// List of cards for a hand or deck
var/list/cards = list()
/obj/item/toy/cards/Destroy()
if(LAZYLEN(cards))
QDEL_LIST(cards)
return ..()
/// This is how we play 52 card pickup
/obj/item/toy/cards/throw_impact(mob/living/target, datum/thrownthing/throwingdatum)
. = ..()
if(. || !istype(target)) // was it caught or is the target not a living mob
return .
if(!throwingdatum?.thrower) // if a mob didn't throw it (need two people to play 52 pickup)
return
var/has_no_cards = !LAZYLEN(cards)
if(has_no_cards)
return
for(var/obj/item/toy/singlecard/card in cards)
cards -= card
card.forceMove(target.drop_location())
if(prob(50))
card.Flip()
card.pixel_x = rand(-16, 16)
card.pixel_y = rand(-16, 16)
var/matrix/Matrix = matrix()
var/angle = pick(0, 90, 180, 270)
Matrix.Turn(angle)
card.transform = Matrix
card.update_appearance()
playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
if(istype(src, /obj/item/toy/cards/cardhand))
qdel(src)
return
update_appearance()
/**
* This is used to insert a list of cards into a deck or cardhand
*
* All cards that are inserted have their angle and pixel offsets reset to zero however their
* flip state does not change unless it's being inserted into a deck which is always facedown
* (see the deck/insert proc)
*
* Arguments:
* * card_item - Either a singlecard or cardhand that gets inserted into the src
*/
/obj/item/toy/cards/proc/insert(obj/item/toy/card_item)
var/cards_to_add = list()
var/obj/item/toy/cards/cardhand/recycled_cardhand
if(istype(card_item, /obj/item/toy/singlecard))
cards_to_add += card_item
if(istype(card_item, /obj/item/toy/cards/cardhand))
recycled_cardhand = card_item
for(var/obj/item/toy/singlecard/card in recycled_cardhand.cards)
cards_to_add += card
recycled_cardhand.cards -= card
card.moveToNullspace()
qdel(recycled_cardhand)
for(var/obj/item/toy/singlecard/card in cards_to_add)
card.forceMove(src)
// reset the position and angle
card.pixel_x = 0
card.pixel_y = 0
var/matrix/M = matrix()
M.Turn(0)
card.transform = M
card.update_appearance()
cards += card
cards_to_add -= card
update_appearance()
/**
* Draws a card from the deck or hand of cards.
*
* Draws the top card unless a card arg is supplied then it picks that specific card
* and returns it (the card arg is used by the radial menu for cardhands to select
* specific cards out of the cardhand)
* Arguments:
* * mob/living/user - The user drawing the card.
* * obj/item/toy/singlecard/card (optional) - The card drawn from the hand
**/
/obj/item/toy/cards/proc/draw(mob/living/user, obj/item/toy/singlecard/card)
if(!isliving(user) || !user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, NO_TK))
return
var/has_no_cards = !LAZYLEN(cards)
if(has_no_cards)
to_chat(user, span_warning("There are no more cards to draw!"))
return
card = card || cards[1] //draw the card on top
cards -= card
update_appearance()
playsound(src, 'sound/items/cardflip.ogg', 50, TRUE)
return card
+21
View File
@@ -0,0 +1,21 @@
/obj/item/toy/cards/deck/blank
name = "custom deck of cards"
desc = "A deck of playing cards that can be customized with writing."
cardgame_desc = "custom card game"
icon_state = "deck_white_full"
deckstyle = "white"
has_unique_card_icons = FALSE
is_standard_deck = FALSE
decksize = 25
can_play_52_card_pickup = FALSE
/obj/item/toy/cards/deck/blank/black
icon_state = "deck_black_full"
deckstyle = "black"
/obj/item/toy/cards/deck/blank/Initialize(mapload)
. = ..()
for(var/i in 1 to decksize)
var/obj/item/toy/singlecard/blank_card = new (src, "blank card", src)
blank_card.blank = TRUE
cards += blank_card
+38
View File
@@ -0,0 +1,38 @@
// CARDS AGAINST SPESS
// This is a parody of Cards Against Humanity (https://en.wikipedia.org/wiki/Cards_Against_Humanity)
// which is licensed under CC BY-NC-SA 2.0, the full text of which can be found at the following URL:
// https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode
// Original code by Zuhayr, Polaris Station, ported with modifications
/obj/item/toy/cards/deck/cas
name = "\improper CAS deck (white)"
desc = "A deck for the game Cards Against Spess, still popular after all these centuries. Warning: may include traces of broken fourth wall. This is the white deck."
cardgame_desc = "Cards Against Spess game"
icon_state = "deck_white_full"
deckstyle = "white"
has_unique_card_icons = FALSE
is_standard_deck = FALSE
decksize = 150
can_play_52_card_pickup = FALSE
/obj/item/toy/cards/deck/cas/black
name = "\improper CAS deck (black)"
desc = "A deck for the game Cards Against Spess, still popular after all these centuries. Warning: may include traces of broken fourth wall. This is the black deck."
icon_state = "deck_black_full"
deckstyle = "black"
decksize = 50
GLOBAL_LIST_INIT(card_decks, list(
black = world.file2list("strings/cas_black.txt"),
white = world.file2list("strings/cas_white.txt")
))
/obj/item/toy/cards/deck/cas/Initialize(mapload)
. = ..()
var/list/cards_against_space = GLOB.card_decks[deckstyle]
var/list/possible_cards = cards_against_space.Copy()
var/list/random_cards = list()
for(var/i in 1 to decksize)
random_cards += pick_n_take(possible_cards)
for(var/card in random_cards)
cards += new /obj/item/toy/singlecard(src, card, src)
+246
View File
@@ -0,0 +1,246 @@
#define DECK_SHUFFLE_TIME (5 SECONDS)
#define DECK_SYNDIE_SHUFFLE_TIME (3 SECONDS)
/obj/item/toy/cards/deck
name = "deck of cards"
desc = "A deck of space-grade playing cards."
icon = 'icons/obj/playing_cards.dmi'
icon_state = "deck_nanotrasen_full"
w_class = WEIGHT_CLASS_SMALL
worn_icon_state = "card"
hitsound = null
attack_verb_continuous = list("attacks")
attack_verb_simple = list("attack")
/// The amount of time it takes to shuffle
var/shuffle_time = DECK_SHUFFLE_TIME
/// Deck shuffling cooldown.
COOLDOWN_DECLARE(shuffle_cooldown)
/// If the deck is the standard 52 playing card deck (used for poker and blackjack)
var/is_standard_deck = TRUE
/// The amount of cards to spawn in the deck (optional)
var/decksize = INFINITY
/// The description of the cardgame that is played with this deck (used for memories)
var/cardgame_desc = "card game"
/// Wielding status for holding with two hands
var/wielded = FALSE
/// The holodeck computer used to spawn a holographic deck (see /obj/item/toy/cards/deck/syndicate/holographic)
var/obj/machinery/computer/holodeck/holodeck
/// If the cards in the deck have different card faces icons (blank and CAS decks do not)
var/has_unique_card_icons = TRUE
/// The art style of deck used (determines both deck and card icons used)
var/deckstyle = "nanotrasen"
/obj/item/toy/cards/deck/Initialize(mapload)
. = ..()
AddElement(/datum/element/drag_pickup)
RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
AddComponent(/datum/component/two_handed, attacksound='sound/items/cardflip.ogg')
register_context()
if(!is_standard_deck)
return
// generate a normal playing card deck
cards += new /obj/item/toy/singlecard(src, "Joker Clown", src)
cards += new /obj/item/toy/singlecard(src, "Joker Mime", src)
for(var/suit in list("Hearts", "Spades", "Clubs", "Diamonds"))
cards += new /obj/item/toy/singlecard(src, "Ace of [suit]", src)
for(var/i in 2 to 10)
cards += new /obj/item/toy/singlecard(src, "[i] of [suit]", src)
for(var/person in list("Jack", "Queen", "King"))
cards += new /obj/item/toy/singlecard(src, "[person] of [suit]", src)
/// triggered on wield of two handed item
/obj/item/toy/cards/deck/proc/on_wield(obj/item/source, mob/user)
SIGNAL_HANDLER
wielded = TRUE
/// triggered on unwield of two handed item
/obj/item/toy/cards/deck/proc/on_unwield(obj/item/source, mob/user)
SIGNAL_HANDLER
wielded = FALSE
/obj/item/toy/cards/deck/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is slitting [user.p_their()] wrists with \the [src]! It looks like their luck ran out!"))
playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
return BRUTELOSS
/obj/item/toy/cards/deck/examine(mob/user)
. = ..()
if(cards.len > 0)
var/obj/item/toy/singlecard/card = cards[1]
if(HAS_TRAIT(user, TRAIT_XRAY_VISION))
. += span_notice("You scan the deck with your x-ray vision and the top card reads: [card.cardname].")
var/marked_color = card.getMarkedColor(user)
if(marked_color)
. += span_notice("The top card of the deck has a [marked_color] mark on the corner!")
. += span_notice("Click and drag the deck to yourself to pickup.") // This should be a context screentip
/obj/item/toy/cards/deck/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
if(src == held_item)
var/obj/item/toy/cards/deck/dealer_deck = held_item
context[SCREENTIP_CONTEXT_LMB] = dealer_deck.wielded ? "Recycle mode" : "Dealer mode"
context[SCREENTIP_CONTEXT_ALT_LMB] = "Shuffle"
return CONTEXTUAL_SCREENTIP_SET
if(isnull(held_item))
context[SCREENTIP_CONTEXT_LMB] = "Draw card"
context[SCREENTIP_CONTEXT_RMB] = "Draw card faceup"
// add drag & drop screentip here in the future
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/toy/singlecard))
context[SCREENTIP_CONTEXT_LMB] = "Recycle card"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/toy/cards/cardhand))
context[SCREENTIP_CONTEXT_LMB] = "Recycle cards"
return CONTEXTUAL_SCREENTIP_SET
return NONE
/**
* Shuffles the cards in the deck
*
* Arguments:
* * user - The person shuffling the cards.
*/
/obj/item/toy/cards/deck/proc/shuffle_cards(mob/living/user)
if(!COOLDOWN_FINISHED(src, shuffle_cooldown))
return
COOLDOWN_START(src, shuffle_cooldown, shuffle_time)
cards = shuffle(cards)
playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
user.balloon_alert_to_viewers("shuffles the deck")
addtimer(CALLBACK(src, .proc/CardgameEvent, user), 60 SECONDS, TIMER_OVERRIDE|TIMER_UNIQUE)
/// This checks if nearby mobs are playing a cardgame and triggers a mood and memory
/obj/item/toy/cards/deck/proc/CardgameEvent(mob/living/dealer)
var/card_players = list()
for(var/mob/living/carbon/person in viewers(loc, COMBAT_MESSAGE_RANGE))
var/obj/item/toy/held_card_item = person.is_holding_item_of_type(/obj/item/toy/singlecard) || person.is_holding_item_of_type(/obj/item/toy/cards/deck) || person.is_holding_item_of_type(/obj/item/toy/cards/cardhand)
if(held_card_item)
card_players[person] = held_card_item
if(length(card_players) >= 2) // need at least 2 people to play a cardgame, duh!
for(var/mob/living/carbon/player in card_players)
var/other_players = english_list(card_players - player)
var/obj/item/toy/held_card_item = card_players[player]
SEND_SIGNAL(player, COMSIG_ADD_MOOD_EVENT, "playing_cards", /datum/mood_event/playing_cards)
player.mind?.add_memory(
MEMORY_PLAYING_CARDS,
list(
DETAIL_PROTAGONIST = player,
DETAIL_PLAYERS = other_players,
DETAIL_CARDGAME = cardgame_desc,
DETAIL_DEALER = dealer,
DETAIL_HELD_CARD_ITEM = held_card_item,
),
story_value = STORY_VALUE_OKAY,
memory_flags = MEMORY_CHECK_BLINDNESS
)
/obj/item/toy/cards/deck/attack_hand(mob/living/user, list/modifiers, flip_card = FALSE)
if(!ishuman(user) || !user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, NO_TK, !iscyborg(user)))
return
var/obj/item/toy/singlecard/card = draw(user)
if(!card)
return
if(flip_card)
card.Flip()
card.pickup(user)
user.put_in_hands(card)
user.balloon_alert_to_viewers("draws a card")
/obj/item/toy/cards/deck/attack_hand_secondary(mob/living/user, list/modifiers)
attack_hand(user, modifiers, flip_card = TRUE)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/toy/cards/deck/AltClick(mob/living/user)
if(user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, NO_TK, !iscyborg(user)))
if(wielded)
shuffle_cards(user)
else
to_chat(user, span_notice("You must hold the [src] with both hands to shuffle."))
return ..()
/obj/item/toy/cards/deck/update_icon_state()
switch(cards.len)
if(27 to INFINITY)
icon_state = "deck_[deckstyle]_full"
if(11 to 27)
icon_state = "deck_[deckstyle]_half"
if(1 to 11)
icon_state = "deck_[deckstyle]_low"
else
icon_state = "deck_[deckstyle]_empty"
return ..()
/obj/item/toy/cards/deck/insert(obj/item/toy/card_item)
// any card inserted into the deck is always facedown
if(istype(card_item, /obj/item/toy/singlecard))
var/obj/item/toy/singlecard/card = card_item
card.Flip(CARD_FACEDOWN)
if(istype(card_item, /obj/item/toy/cards/cardhand))
var/obj/item/toy/cards/cardhand/cardhand = card_item
for(var/obj/item/toy/singlecard/card in cardhand.cards)
card.Flip(CARD_FACEDOWN)
. = ..()
/obj/item/toy/cards/deck/attackby(obj/item/item, mob/living/user, params)
if(istype(item, /obj/item/toy/singlecard) || istype(item, /obj/item/toy/cards/cardhand))
insert(item)
var/card_grammar = istype(item, /obj/item/toy/singlecard) ? "card" : "cards"
user.balloon_alert_to_viewers("puts [card_grammar] in deck")
return
return ..()
/// This is how we play 52 card pickup
/obj/item/toy/cards/deck/throw_impact(mob/living/target, datum/thrownthing/throwingdatum)
. = ..()
if(. || !istype(target)) // was it caught or is the target not a living mob
return .
if(!throwingdatum?.thrower) // if a mob didn't throw it (need two people to play 52 pickup)
return
var/mob/living/thrower = throwingdatum.thrower
target.visible_message(span_warning("[target] is forced to play 52 card pickup!"), span_warning("You are forced to play 52 card pickup."))
SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "lost_52_card_pickup", /datum/mood_event/lost_52_card_pickup)
SEND_SIGNAL(thrower, COMSIG_ADD_MOOD_EVENT, "won_52_card_pickup", /datum/mood_event/won_52_card_pickup)
add_memory_in_range(
target,
7,
MEMORY_PLAYING_52_PICKUP,
list(DETAIL_PROTAGONIST = thrower, DETAIL_DEUTERAGONIST = target, DETAIL_WHAT_BY = src),
story_value = STORY_VALUE_OKAY,
memory_flags = MEMORY_CHECK_BLINDNESS
)
/*
|| Syndicate playing cards, for pretending you're Gambit and playing poker for the nuke disk. ||
*/
/obj/item/toy/cards/deck/syndicate
name = "suspicious looking deck of cards"
desc = "A deck of space-grade playing cards. They seem unusually rigid."
cardgame_desc = "suspicious card game"
icon_state = "deck_syndicate_full"
deckstyle = "syndicate"
hitsound = 'sound/weapons/bladeslice.ogg'
force = 5
throwforce = 10
attack_verb_continuous = list("attacks", "slices", "dices", "slashes", "cuts")
attack_verb_simple = list("attack", "slice", "dice", "slash", "cut")
resistance_flags = NONE
shuffle_time = DECK_SYNDIE_SHUFFLE_TIME
#undef DECK_SHUFFLE_TIME
#undef DECK_SYNDIE_SHUFFLE_TIME
+21
View File
@@ -0,0 +1,21 @@
/obj/item/toy/cards/deck/kotahi
name = "\improper KOTAHI deck"
desc = "A deck of kotahi cards. House rules to argue over not included."
cardgame_desc = "KOTAHI game"
icon_state = "deck_kotahi_full"
deckstyle = "kotahi"
is_standard_deck = FALSE
/obj/item/toy/cards/deck/kotahi/Initialize(mapload)
. = ..()
for(var/colour in list("Red","Yellow","Green","Blue"))
cards += new /obj/item/toy/singlecard(src, "[colour] 0", src) //kotahi decks have only one colour of each 0, weird huh?
for(var/k in 0 to 1) //two of each colour of number
cards += new /obj/item/toy/singlecard(src, "[colour] skip", src)
cards += new /obj/item/toy/singlecard(src, "[colour] reverse", src)
cards += new /obj/item/toy/singlecard(src, "[colour] draw 2", src)
for(var/i in 1 to 9)
cards += new /obj/item/toy/singlecard(src, "[colour] [i]", src)
for(var/k in 0 to 3) //4 wilds and draw 4s
cards += new /obj/item/toy/singlecard(src, "Wildcard", src)
cards += new /obj/item/toy/singlecard(src, "Draw 4", src)
+59
View File
@@ -0,0 +1,59 @@
#define TAROT_GHOST_TIMER (666 SECONDS) // this translates into 11 mins and 6 seconds
//These cards certainly won't tell the future, but you can play some nice games with them.
/obj/item/toy/cards/deck/tarot
name = "tarot game deck"
desc = "A full 78 card game deck of tarot cards. Complete with 4 suites of 14 cards, and a full suite of trump cards."
cardgame_desc = "tarot card reading"
icon_state = "deck_tarot_full"
deckstyle = "tarot"
is_standard_deck = FALSE
/obj/item/toy/cards/deck/tarot/Initialize(mapload)
. = ..()
for(var/suit in list("Hearts", "Pikes", "Clovers", "Tiles"))
for(var/i in 1 to 10)
cards += new /obj/item/toy/singlecard(src, "[i] of [suit]", src)
for(var/person in list("Valet", "Chevalier", "Dame", "Roi"))
cards += new /obj/item/toy/singlecard(src, "[person] of [suit]", src)
for(var/trump in list("The Magician", "The High Priestess", "The Empress", "The Emperor", "The Hierophant", "The Lover", "The Chariot", "Justice", "The Hermit", "The Wheel of Fortune", "Strength", "The Hanged Man", "Death", "Temperance", "The Devil", "The Tower", "The Star", "The Moon", "The Sun", "Judgement", "The World", "The Fool"))
cards += new /obj/item/toy/singlecard(src, trump, src)
/obj/item/toy/cards/deck/tarot/draw(mob/user)
. = ..()
if(prob(50))
var/obj/item/toy/singlecard/card = .
var/matrix/M = matrix()
M.Turn(180)
card.transform = M
/obj/item/toy/cards/deck/tarot/haunted
name = "haunted tarot game deck"
desc = "A spooky looking tarot deck. You can sense a supernatural presence linked to the cards..."
/// ghost notification cooldown
COOLDOWN_DECLARE(ghost_alert_cooldown)
/obj/item/toy/cards/deck/tarot/haunted/on_wield(obj/item/source, mob/living/carbon/user)
. = ..()
ADD_TRAIT(user, TRAIT_SIXTHSENSE, MAGIC_TRAIT)
to_chat(user, span_notice("The veil to the underworld is opened. You can sense the dead souls calling out..."))
if(!COOLDOWN_FINISHED(src, ghost_alert_cooldown))
return
COOLDOWN_START(src, ghost_alert_cooldown, TAROT_GHOST_TIMER)
notify_ghosts(
"Someone has begun playing with a [src.name] in [get_area(src)]!",
source = src,
header = "Haunted Tarot Deck",
ghost_sound = 'sound/effects/ghost2.ogg',
notify_volume = 75,
action = NOTIFY_ORBIT,
)
/obj/item/toy/cards/deck/tarot/haunted/on_unwield(obj/item/source, mob/living/carbon/user)
. = ..()
REMOVE_TRAIT(user, TRAIT_SIXTHSENSE, MAGIC_TRAIT)
to_chat(user, span_notice("The veil to the underworld closes shut. You feel your senses returning to normal."))
#undef TAROT_GHOST_TIMER
+34
View File
@@ -0,0 +1,34 @@
//It's Wiz-Off, the wizard themed card game! It's modular too, in case you might want to make it Syndie, Sec and Clown themed or something stupid like that.
/obj/item/toy/cards/deck/wizoff
name = "\improper Wiz-Off deck"
desc = "A Wiz-Off deck. Fight an arcane battle for the fate of the universe: Draw 5! Play 5! Best of 5! A rules card is attached."
cardgame_desc = "Wiz-Off game"
icon_state = "deck_wizoff_full"
deckstyle = "wizoff"
is_standard_deck = FALSE
/obj/item/toy/cards/deck/wizoff/Initialize(mapload)
. = ..()
var/card_list = strings("wizoff.json", "wizard")
cards += new /obj/item/toy/singlecard/wizoff_ruleset(src) // ruleset should be the top card
for(var/card in card_list)
cards += new /obj/item/toy/singlecard(src, card, src)
/obj/item/toy/singlecard/wizoff_ruleset
desc = "A ruleset for the playing card game Wiz-Off."
cardname = "Wizoff Ruleset"
deckstyle = "black"
has_unique_card_icons = FALSE
icon_state = "singlecard_down_black"
/obj/item/toy/singlecard/wizoff_ruleset/examine(mob/living/carbon/human/user)
. = ..()
. += span_notice("Remember the rules of Wiz-Off!")
. += span_info("Each player draws 5 cards.")
. += span_info("There are five rounds. Each round, a player selects a card to play, and the winner is selected based on the following rules:")
. += span_info("Defensive beats Offensive!")
. += span_info("Offensive beats Utility!")
. += span_info("Utility beats Defensive!")
. += span_info("If both players play the same type of spell, the higher number wins!")
. += span_info("The player who wins the most of the 5 rounds wins the game!")
. += span_notice("Now get ready to battle for the fate of the universe: Wiz-Off!")
+242
View File
@@ -0,0 +1,242 @@
#define CARD_FACEDOWN 0
#define CARD_FACEUP 1
/obj/item/toy/singlecard
name = "card"
desc = "A playing card used to play card games like poker."
icon = 'icons/obj/playing_cards.dmi'
icon_state = "sc_Ace of Spades_nanotrasen"
w_class = WEIGHT_CLASS_TINY
worn_icon_state = "card"
pixel_x = -5
resistance_flags = FLAMMABLE
max_integrity = 50
force = 0
throwforce = 0
throw_speed = 3
throw_range = 7
attack_verb_continuous = list("attacks")
attack_verb_simple = list("attack")
/// Artistic style of the deck
var/deckstyle = "nanotrasen"
/// If the cards in the deck have different icon states (blank and CAS decks do not)
var/has_unique_card_icons = TRUE
/// The name of the card
var/cardname = "Ace of Spades"
/// Is the card flipped facedown (FALSE) or flipped faceup (TRUE)
var/flipped = FALSE
/// The card is blank and can be written on with a pen.
var/blank = FALSE
/// The color used to mark a card for cheating (by pens or crayons)
var/marked_color
/obj/item/toy/singlecard/Initialize(mapload, cardname, obj/item/toy/cards/deck/parent_deck)
. = ..()
src.cardname = cardname || src.cardname
if(istype(parent_deck))
deckstyle = parent_deck.deckstyle
has_unique_card_icons = parent_deck.has_unique_card_icons
icon_state = "singlecard_down_[parent_deck.deckstyle]"
hitsound = parent_deck.hitsound
force = parent_deck.force
throwforce = parent_deck.throwforce
throw_speed = parent_deck.throw_speed
throw_range = parent_deck.throw_range
attack_verb_continuous = parent_deck.attack_verb_continuous
attack_verb_simple = parent_deck.attack_verb_simple
if(parent_deck.holodeck)
flags_1 |= HOLOGRAM_1
parent_deck.holodeck.spawned += src
register_context()
/obj/item/toy/singlecard/examine(mob/living/carbon/human/user)
. = ..()
if(!ishuman(user))
return
if(user.is_holding(src))
user.visible_message(span_notice("[user] checks [user.p_their()] card."), span_notice("The card reads: [cardname]."))
if(blank)
. += span_notice("The card is blank. Write on it with a pen.")
else if(HAS_TRAIT(user, TRAIT_XRAY_VISION))
. += span_notice("You scan the card with your x-ray vision and it reads: [cardname].")
else
. += span_warning("You need to have the card in your hand to check it!")
var/marked_color = getMarkedColor(user)
if(marked_color)
. += span_notice("The card has a [marked_color] mark on the corner!")
/obj/item/toy/singlecard/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
if(isnull(held_item) || src == held_item)
context[SCREENTIP_CONTEXT_ALT_LMB] = "Rotate counter-clockwise" // add a ALT_RMB screentip to rotate clockwise
context[SCREENTIP_CONTEXT_RMB] = "Flip card"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/toy/cards/deck))
var/obj/item/toy/cards/deck/dealer_deck = held_item
if(dealer_deck.wielded)
context[SCREENTIP_CONTEXT_LMB] = "Deal card"
context[SCREENTIP_CONTEXT_RMB] = "Deal card faceup"
return CONTEXTUAL_SCREENTIP_SET
context[SCREENTIP_CONTEXT_LMB] = "Recycle card"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/toy/singlecard))
context[SCREENTIP_CONTEXT_LMB] = "Combine cards"
context[SCREENTIP_CONTEXT_RMB] = "Combine cards faceup"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/toy/cards/cardhand))
context[SCREENTIP_CONTEXT_LMB] = "Combine cards"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/toy/crayon) || istype(held_item, /obj/item/pen))
context[SCREENTIP_CONTEXT_LMB] = blank ? "Write on card" : "Mark card"
return CONTEXTUAL_SCREENTIP_SET
return NONE
/obj/item/toy/singlecard/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is slitting [user.p_their()] wrists with \the [src]! It looks like [user.p_they()] [user.p_have()] an unlucky card!"))
playsound(src, 'sound/weapons/bladeslice.ogg', 50, TRUE)
return BRUTELOSS
/**
* Flips the card over
*
* * Arguments:
* * orientation (optional) - Sets flipped state to CARD_FACEDOWN or CARD_FACEUP if given orientation (otherwise just invert the flipped state)
*/
/obj/item/toy/singlecard/proc/Flip(orientation)
if(!isnull(orientation))
flipped = orientation
else
flipped = !flipped
name = flipped ? cardname : "card"
update_appearance()
/**
* Returns a color if a card is marked and the user can see it
*
* * Arguments:
* * user - We need to check if the user see the marked card
*/
/obj/item/toy/singlecard/proc/getMarkedColor(mob/living/carbon/user)
if(!istype(user))
return
var/is_marked_with_visible_color = (marked_color && marked_color != "invisible")
if(is_marked_with_visible_color || (marked_color == "invisible" && HAS_TRAIT(user, TRAIT_REAGENT_SCANNER)))
return marked_color
/obj/item/toy/singlecard/update_icon_state()
if(!flipped)
icon_state = "singlecard_down_[deckstyle]"
else if(has_unique_card_icons) // each card in a deck has a different icon
icon_state = "sc_[cardname]_[deckstyle]"
else // all cards are the same icon state (blank or scribble)
icon_state = blank ? "sc_blank_[deckstyle]" : "sc_scribble_[deckstyle]"
return ..()
/obj/item/toy/singlecard/update_name()
name = flipped ? cardname : "card"
return ..()
/obj/item/toy/singlecard/attackby(obj/item/item, mob/living/user, params, flip_card=FALSE)
var/obj/item/toy/singlecard/card
if(istype(item, /obj/item/toy/cards/deck))
var/obj/item/toy/cards/deck/dealer_deck = item
if(!dealer_deck.wielded) // recycle card into deck (if unwielded)
dealer_deck.insert(src)
user.balloon_alert_to_viewers("puts card in deck")
return
card = dealer_deck.draw(user)
if(istype(item, /obj/item/toy/singlecard))
card = item
if(card) // card + card = combine into cardhand
if(flip_card)
card.Flip()
if(istype(item, /obj/item/toy/cards/deck))
// only decks cause a balloon alert
user.balloon_alert_to_viewers("deals a card")
var/obj/item/toy/cards/cardhand/new_cardhand = new (drop_location(), list(src, card))
new_cardhand.pixel_x = pixel_x
new_cardhand.pixel_y = pixel_y
if(!isturf(loc)) // make a cardhand in our active hand
user.temporarilyRemoveItemFromInventory(src, TRUE)
new_cardhand.pickup(user)
user.put_in_active_hand(new_cardhand)
return
if(istype(item, /obj/item/toy/cards/cardhand)) // insert into cardhand
var/obj/item/toy/cards/cardhand/target_cardhand = item
target_cardhand.insert(src)
return
var/can_item_write
var/marked_cheating_color
if(istype(item, /obj/item/pen))
var/obj/item/pen/pen = item
can_item_write = TRUE
marked_cheating_color = (pen.colour == "white" && "invisible") || pen.colour
if(istype(item, /obj/item/toy/crayon))
var/obj/item/toy/crayon/crayon = item
can_item_write = TRUE
marked_cheating_color = (crayon.crayon_color == "mime" && "invisible") || crayon.crayon_color
if(can_item_write && !blank) // You cheated not only the game, but yourself
marked_color = marked_cheating_color
to_chat(user, span_notice("You put a [marked_color] mark in the corner of [src] with the [item]. Cheat to win!"))
return
if(can_item_write)
if(!user.is_literate())
to_chat(user, span_notice("You scribble illegibly on [src]!"))
return
var/cardtext = stripped_input(user, "What do you wish to write on the card?", "Card Writing", "", 50)
if(!cardtext || !user.canUseTopic(src, BE_CLOSE))
return
cardname = cardtext
blank = FALSE
update_appearance()
return
return ..()
/obj/item/toy/singlecard/attackby_secondary(obj/item/item, mob/living/user, modifiers)
attackby(item, user, modifiers, flip_card=TRUE)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/toy/singlecard/attack_hand_secondary(mob/living/carbon/human/user, modifiers)
attack_self(user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/toy/singlecard/attack_self_secondary(mob/living/carbon/human/user, modifiers)
attack_self(user)
/obj/item/toy/singlecard/attack_self(mob/living/carbon/human/user)
if(!ishuman(user) || !user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, NO_TK, !iscyborg(user)))
return
Flip()
if(isturf(src.loc)) // only display tihs message when flipping in a visible spot like on a table
user.balloon_alert_to_viewers("flips a card")
/obj/item/toy/singlecard/AltClick(mob/living/carbon/human/user)
if(user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, NO_TK, !iscyborg(user)))
transform = turn(transform, 90)
// use the simple_rotation component to make this turn with Alt+RMB & Alt+LMB at some point in the future - TimT
return ..()