Files
Bubberstation/code/modules/cards/deck/deck.dm
SkyratBot 25f4961156 [MIRROR] Refactors memories to be less painful to add and apply, moves memory detail / text to memory subtypes. Adds some new memories to demonstrate. [MDB IGNORE] (#18487)
* Refactors memories to be less painful to add and apply, moves memory detail / text to memory subtypes. Adds some new memories to demonstrate.  (#72110)

So, a huge issue with memories and - what I personally believe is the
reason why not many have been added since their inception is - they're
very annoying to add!

Normally, adding subtypes of stuff like traumas or hallucinations are as
easy as doing just that, adding a subtype.

But memories used this factory argument passing method combined with
holding all their strings in a JSON file which made it just frustrating
to add, debug, or just mess with.

It also made it much harder to organize new memories keep it clean for
stuff like downstreams.

So I refactored it. Memories are now handled on a subtype by subtype
basis, instead of all memories being a `/datum/memory`.

Any variety of arguments can be passed into memories like addcomponent
(KWARGS) so each subtype can have their own `new` parameters.

This makes it much much easier to add a new memory. All you need to do
is make your subtype and add it somewhere. Don't need to mess with jsons
or defines or anything.

To demonstrate this, I added a few memories. Some existing memories had
their story values tweak to compensate.

Makes it way simpler to add new memories. Maybe we'll get some more fun
ones now?

🆑 Melbert
add: Roundstart captains will now memorize the code to the spare ID
safe.
add: Traitors will now memorize the location and code to their uplink.
add: Heads of staff winning a revolution will now get a memory of their
success.
add: Heads of staff and head revolutionaries who lose their respective
sides of the revolution also get a memory of their failure.
add: Completing a ritual of knowledge as a heretic grants you a quality
memory.
add: Successfully defusing a bomb now grants you a cool memory. Failing
it will also grant you a memory, though you will likely not be alive to
see it.
add: Planting bombs now increase their memory quality depending on how
cool the bomb is.
refactor: Memories have been refactored to be much easier to add.
/🆑

* Modular!

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Funce <funce.973@gmail.com>
2023-01-17 12:51:58 +13:00

236 lines
9.0 KiB
Plaintext

#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/toys/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_REF(on_wield))
RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, PROC_REF(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
initial_cards += "Joker Clown"
initial_cards += "Joker Mime"
for(var/suit in list("Hearts", "Spades", "Clubs", "Diamonds"))
initial_cards += "Ace of [suit]"
for(var/i in 2 to 10)
initial_cards += "[i] of [suit]"
for(var/person in list("Jack", "Queen", "King"))
initial_cards += "[person] of [suit]"
/// 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(HAS_TRAIT(user, TRAIT_XRAY_VISION) && count_cards() > 0)
. += span_notice("You scan the deck with your x-ray vision and the top card reads: [fetch_card_atoms()[1].cardname].")
// This can only happen if card_atoms have been generated
if(LAZYLEN(card_atoms) > 0)
var/obj/item/toy/singlecard/card = fetch_card_atoms()[1]
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)
shuffle_inplace(fetch_card_atoms())
playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
user.balloon_alert_to_viewers("shuffles the deck")
addtimer(CALLBACK(src, PROC_REF(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]
player.add_mood_event("playing_cards", /datum/mood_event/playing_cards)
player.add_mob_memory(/datum/memory/playing_cards, \
deuteragonist = dealer, \
game = cardgame_desc, \
protagonist_held_card = held_card_item, \
other_players = other_players)
/obj/item/toy/cards/deck/attack_hand(mob/living/user, list/modifiers, flip_card = FALSE)
if(!ishuman(user) || !user.canUseTopic(src, be_close = TRUE, no_dexterity = TRUE, no_tk = TRUE, need_hands = !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 = TRUE, no_dexterity = TRUE, no_tk = TRUE, need_hands = !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(count_cards())
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.fetch_card_atoms())
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."))
target.add_mood_event("lost_52_card_pickup", /datum/mood_event/lost_52_card_pickup)
thrower.add_mood_event("won_52_card_pickup", /datum/mood_event/won_52_card_pickup)
add_memory_in_range(target, 7, /datum/memory/playing_card_pickup, protagonist = thrower, deuteragonist = target, antagonist = src)
/*
|| 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