Files
Bubberstation/code/controllers/subsystem/tcgsetup.dm
SkyratBot 144b40c0df [MIRROR] Small changes to some card-related debug verbs [MDB IGNORE] (#18534)
* Small changes to some card-related debug verbs (#72361)

## About The Pull Request

Test Card Distribution debug verb has been altered slightly to prevent
runtimes. Backing out of any one of the menus would send null as an
argument, and cause a runtime.

The Validate Cards verb now returns a message if no errors are found. I
kept mistakenly clicking this verb thinking it was the Cardpack
Distribution one, and would get confused whenever nothing happened. Now
it returns a message!

Also converts some of the stuff I touch into snake case because pretty
code is nice.

## Why It's Good For The Game

Closes #66987. Feedback for the random debug buttons I accidentally
click is good.
## Changelog
🆑 Rhials
fix: backing out of the Test Card Packs debug menu will no longer cause
a runtime
fix: Validate Cards debug verb now gives feedback if no errors are
detected.
/🆑

* Small changes to some card-related debug verbs

Co-authored-by: Rhials <Datguy33456@gmail.com>
2023-01-06 08:48:33 -08:00

191 lines
8.1 KiB
Plaintext

SUBSYSTEM_DEF(trading_card_game)
name = "Trading Card Game"
flags = SS_NO_FIRE
init_order = INIT_ORDER_TCG
/// Base directory for all related string files
var/card_directory = "strings/tcg"
/// List of card files to load
var/list/card_files = list("set_one.json", "set_two.json")
/// List of keyword files
/// These allow you to add on hovor logic to parts of a card's text, displaying extra info
var/list/keyword_files = list("keywords.json")
/// What cardpack types to load
var/card_packs = list(/obj/item/cardpack/series_one, /obj/item/cardpack/resin)
var/list/cached_guar_rarity = list()
var/list/cached_rarity_table = list()
/// List of all cards by series, with cards cached by rarity to make those lookups faster
var/list/cached_cards = list()
/// List of loaded keywords matched with their hovor text
var/list/keywords = list()
var/loaded = FALSE
//Let's load the cards before the map fires, so we can load cards on the map safely
/datum/controller/subsystem/trading_card_game/Initialize()
reloadAllCardFiles()
return SS_INIT_SUCCESS
///Loads all the card files
/datum/controller/subsystem/trading_card_game/proc/loadAllCardFiles()
for(var/keyword_file in keyword_files)
loadKeywordFile(keyword_file, card_directory)
styleKeywords()
for(var/card_file in card_files)
loadCardFile(card_file, card_directory)
///Empty the rarity cache so we can safely add new cards
/datum/controller/subsystem/trading_card_game/proc/clearCards()
loaded = FALSE
cached_cards = list()
keywords = list()
///Reloads all card files
/datum/controller/subsystem/trading_card_game/proc/reloadAllCardFiles()
clearCards()
loadAllCardFiles()
loaded = TRUE
///Loads the contents of a json file into our global card list
/datum/controller/subsystem/trading_card_game/proc/loadKeywordFile(filename, directory = "strings/tcg")
var/list/keyword_data = json_decode(file2text("[directory]/[filename]"))
for(var/keyword in keyword_data)
if(keywords[keyword])
stack_trace("Dupe detected, [keyword] was defined by [directory]/[filename] after it already had a value!")
continue
keywords[keyword] = keyword_data[keyword]
///Styles our keywords, converting them from just the raw text to the output we want
/datum/controller/subsystem/trading_card_game/proc/styleKeywords()
// Add the tooltip component to our text, make it pretty
for(var/keyword in keywords)
var/tooltip_text = keywords[keyword]
keywords[keyword] = span_tooltip(tooltip_text, keyword)
///Takes a string as input. Searches it for keywords in the pattern {$keyword}, and replaces them with their expanded form, generated above
/datum/controller/subsystem/trading_card_game/proc/resolve_keywords(search_through)
var/starting_text = search_through
while(TRUE)
var/fragment_start = findtext(search_through, "{$")
if(!fragment_start)
break
var/fragment_end = findtext(search_through, "}")
if(!fragment_end)
CRASH("[starting_text] contains a {$ that denotes the start of a keyword replacement, but not a closing }!")
///Gets the keyword this string wants to use
///We offset the start by two indexes to account for
var/keyword = copytext(search_through, fragment_start + 2, fragment_end)
var/replacement = keywords[keyword]
if(!replacement)
CRASH("[starting_text] contains a non-existent keyword! \[[keyword]\]")
search_through = replacetext(search_through, "{$[keyword]}", replacement)
return search_through
///Loads the contents of a json file into our global card list
/datum/controller/subsystem/trading_card_game/proc/loadCardFile(filename, directory = "strings/tcg")
var/list/json = json_decode(file2text("[directory]/[filename]"))
var/list/cards = json["cards"]
var/list/templates = list()
for(var/list/data in json["templates"])
templates[data["template"]] = data
for(var/list/data in cards)
var/datum/card/card = new(data, templates)
//Lets cache the id by rarity, for top speed lookup later
if(!cached_cards[card.series])
cached_cards[card.series] = list()
cached_cards[card.series]["ALL"] = list()
if(!cached_cards[card.series][card.rarity])
cached_cards[card.series][card.rarity] = list()
cached_cards[card.series][card.rarity] += card.id
//Let's actually store the datum here
cached_cards[card.series]["ALL"][card.id] = card
///Because old me wanted to keep memory costs down, each cardpack type shares a rarity list
///We do the spooky stuff in here to ensure we don't have too many lists lying around
/datum/controller/subsystem/trading_card_game/proc/get_rarity_table(type, list/sample_table)
//Pass by refrance moment
//This lets us only have one rarity table per pack, badmins beware
//Yes this is horribly overengineered. No I am not sorry
if(!cached_rarity_table[type])
cached_rarity_table[type] = sample_table
return cached_rarity_table[type]
///See above
/datum/controller/subsystem/trading_card_game/proc/get_guarenteed_rarity_table(type, list/sample_table)
if(!cached_guar_rarity[type])
cached_guar_rarity[type] = sample_table
return cached_guar_rarity[type]
///Prints all the cards names
/datum/controller/subsystem/trading_card_game/proc/printAllCards()
for(var/card_set in cached_cards)
message_admins("Printing the [card_set] set")
for(var/card in cached_cards[card_set]["ALL"])
var/datum/card/toPrint = cached_cards[card_set]["ALL"][card]
message_admins(toPrint.name)
///Checks the passed type list for missing raritys, or raritys out of bounds
/datum/controller/subsystem/trading_card_game/proc/check_cardpacks(card_pack_list)
var/toReturn = ""
for(var/cardPack in card_pack_list)
var/obj/item/cardpack/pack = new cardPack()
//Lets see if someone made a type yeah?
if(!cached_cards[pack.series])
toReturn += "[pack.series] does not have any cards in it\n"
continue
for(var/card in cached_cards[pack.series]["ALL"])
var/datum/card/template = cached_cards[pack.series]["ALL"][card]
if(template.rarity == "ALL")
toReturn += "[pack.type] has a rarity [template.rarity] on the card [template.id] that needs to be changed to something that isn't \"ALL\"\n"
continue
if(!(template.rarity in pack.rarity_table))
toReturn += "[pack.type] has a rarity [template.rarity] on the card [template.id] that does not exist\n"
continue
//Lets run a check to see if all the rarities exist that we want to exist exist
for(var/pack_rarity in pack.rarity_table)
if(!cached_cards[pack.series][pack_rarity])
toReturn += "[pack.type] does not have the required rarity [pack_rarity]\n"
qdel(pack)
return toReturn
///Checks the global card list for cards that don't override all the default values of the card datum
/datum/controller/subsystem/trading_card_game/proc/check_card_datums()
var/toReturn = ""
var/datum/thing = new()
for(var/series in cached_cards)
var/cards = cached_cards[series]["ALL"]
for(var/card in cards)
var/datum/card/target = cached_cards[series]["ALL"][card]
var/toAdd = "The card [target.id] in [series] has the following default variables:"
var/shouldAdd = FALSE
for(var/current_var in (target.vars ^ thing.vars))
if(current_var == "icon" && target.vars[current_var] == DEFAULT_TCG_DMI)
continue
if(target.vars[current_var] == initial(target.vars[current_var]))
shouldAdd = TRUE
toAdd += "\n[current_var] with a value of [target.vars[current_var]]"
if(shouldAdd)
toReturn += toAdd
qdel(thing)
return toReturn
///Used to test open a large amount of cardpacks
/datum/controller/subsystem/trading_card_game/proc/check_card_distribution(cardPack, batchSize, batchCount, guaranteed)
var/totalCards = 0
//Gotta make this look like an associated list so the implicit "does this exist" checks work proper later
var/list/cardsByCount = list("" = 0)
var/obj/item/cardpack/pack = new cardPack()
for(var/index in 1 to batchCount)
var/list/cards = pack.buildCardListWithRarity(batchSize, guaranteed)
for(var/id in cards)
totalCards++
cardsByCount[id] += 1
var/toSend = "Out of [totalCards] cards"
for(var/id in sort_list(cardsByCount, GLOBAL_PROC_REF(cmp_num_string_asc)))
if(id)
var/datum/card/template = cached_cards[pack.series]["ALL"][id]
toSend += "\nID:[id] [template.name] [(cardsByCount[id] * 100) / totalCards]% Total:[cardsByCount[id]]"
message_admins(toSend)
qdel(pack)