mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-31 12:01:47 +00:00
* Refactors tcg code a bit. Adds support for "keywords"
Idea is to allow card makers to embed tooltips in their card
descriptions.
These tooltips are defined in the keywords.json file
They can be referenced using {$keyword_name}
I've also done some refactoring to move more logic and state onto the
subsystem, and implemented a few keywords from the wiki
* New keywords, applies the old ones to the second card set
* Adds support for embedding react components in tgui chat
This is done by adding the data-component attribute to an html element
The value of that attibute is the component you want to use.
New components can be added by modifying the TGUI_CHAT_COMPONENTS list
in tgui-panel/chat/renderer.js.
Props can also be passed in in a limited capacity.
Any props you wish to pass must be added to
TGUI_CHAT_ATTRIBUTES_TO_PROPS.
This is due to a style restriction of html attributes, they cannot
contain an upper case char.
Use this list to convert between attibute compatible text and the prop's
name.
Props support 3 datatypes.
true and false can be passed by wrapping them in ""s. (Note to self add
a special char here to prevent colison with people just passing the
string true.
Numbers are supported in a limited capacity. Whitespace is not allowed,
but floats and ints are fair game.
And of course, strings are fully supported.
I've currently added support for Tooltip, since that's what I'm using
this for. Also added some tooltip html styles to the chat css.
* Implements the embedded component system to make tcg cards have nice pretty tooltips so people don't need to have the wiki open on one screen
* Adds documentation for embedding tgui components in chat, adds some protection against accidentially sending true as a bool
* Adds italitcs to the tooltips, moves the span stuff to a macro
* tGUI -> tgui, thank fikou
Co-authored-by: Fikou <23585223+Fikou@users.noreply.github.com>
* Style suggestions
Co-authored-by: Aleksej Komarov <stylemistake@gmail.com>
* Removes unneeded key from the components list
Co-authored-by: Aleksej Komarov <stylemistake@gmail.com>
* Removes needless span
* Actually adds the tooltip, oops
Co-authored-by: Fikou <23585223+Fikou@users.noreply.github.com>
Co-authored-by: Aleksej Komarov <stylemistake@gmail.com>
189 lines
8.1 KiB
Plaintext
189 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 ..()
|
|
|
|
///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/checkCardpacks(cardPackList)
|
|
var/toReturn = ""
|
|
for(var/cardPack in cardPackList)
|
|
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/checkCardDatums()
|
|
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/checkCardDistribution(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, /proc/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)
|