Template for creating a basic gacha-style card collection Discord bot
The purpose of this Discord bot is to create a gacha-style card collection game for a Discord server. Members of the server would roll for cards and react to the emoji on the cards to claim them, with the goal of collecting the most rare cards.
The bot is coded in Python and is comprised of two files: initialize_gacha_format.py and bot_gacha_format.py. Running the initialize_gacha_format.py will start up the bot by connecting to bot_gacha_format.py, which is imported into initialize_gacha_format.py, and contains all the functions and specificities of the bot. In terms of libraries, the bot requires imports of discord, commands (from discord.ext), certifi, os, random2, json, and time.
The files include the general format of the code, but require input and revisions depending on how the coder wants their gacha to run. The comment blocks and comment statements contain information on how the functions run, as well as how the functions should be revised/formatted to the coder's preferences and wants. Here are some things to pay attention to:
- If there is a comment block with the format '''[content]''', this block needs to be replaced with what is asked for. For example, '''[desired claim cooldown in seconds]''' means that the coder must replace that block with their desired cooldown for claiming cards (ex: 1800). Blocks of this sort would be in the form of variable names, int, or float.
- If there is a comment block with the format '''["content"]''', this block needs to be replaced as well with what is being asked for. However, blocks of this sort are asking for a str input, so quotations should be put around the content. For example, '''["command to be typed to call for a roll"]''' means that the coder must replace that block with their desired command for rolling a card (ex: "!roll").
Aside from these two points, the template should be relatively self explanatory. Again, the comment blocks in the code provide more insight on how certain sections should be revised. The cards/odds, info menu, and help menu sections are quite customizable, so I included examples of how I would format them (but they are essentially just embed messages that you can format to your liking, if you choose to include them).
Data Storage:
-
To keep track of the stats of each member who is playing the gacha, an external json file is created in the form of a dictionary. This file is denoted by "data" in the code. In the dictionary, the keys are the unique ID of the members (message.author.id), the subkeys are "rolls_count", "last_rolled_time", "last_claimed_time", and the card names. The values are the corresponding number of rolls, time of the last roll, time of the last claim, and the number of each card a member has collected. The format for a member is as follows:
{"[Member ID]": {"rolls_count": 10, "last_rolled_time": 1775445.024922888, "last_claimed_time": 1775457.018707351", "Card 1": 3, "Card 2": 2, "Card 3": 5}}.
-
Each function that alters the dictionary starts with a load_data() function call and ends with a save_data() function call. The load_data() function checks if there is an existing json file of a predefined name, and opens it if there is. If there isn't, it creates a json file of that name. The save_data() function dumps the altered data into the existing json file and saves it. Consequently, the dictionary is constantly changing; it continuously keeps track of the roll count, roll status, claim status, card status for every member invloved in the gacha and alters those subkeys accordinly as function calls take place.
Time:
-
The time import is used to keep track of the "last_rolled_time" and the "last_claimed_time". When a member rolls for a card, a call is made to the function can_roll_card(). In that function, the "last_rolled_time" and "rolls_count" are extracted from the dictionary and the "current_time" is assigned using the time.perf_counter() command. This command returns the current time in seconds. The "elapsed_time", which denotes the time between the last roll and the current time, is calculated by subtracting the "last_rolled_time" from the "current_time". With the variables defined, the "rolls_count" and "elapsed_time" are compared with the "max_rolls" and the "cooldown_duration_roll" to determine if a member's call for a roll is valid. If not, the remaining cooldown time is returned. If yes, the program continues running and a new "last_rolled_time" is created through the time.perf_counter() command.
-
Similarly, the "last_claimed_time" is defined through a call to the function can_claim_card(). The "current_time" and "elapsed_time" variables are calculated in the same manner, and the "elapsed_time" is compared to the "cooldown_duration_claim" to determine the validity of a claim. If it is not valid, the remaining cooldown time is returned. If it is valid, the program continues, the card is appended to the dictionary, and a new "last_claimed_time" is created through the time.perf_counter().
Card Odds:
-
The card that gets sent by the bot is determined by a random number generator, and whatever number is generated corresponds to a certain card. Some cards may have a smaller range than others, which makes them more rare. I would recommend the odds to be in a skewed, exponential fashion. Having a distribution of odds where the range of numbers gets increasingly large linearly as cards become more common won't produce a genuine gacha effect. Of course, the odds are up to the coder, but the top cards should be quite hard to get, and a linear increase in number ranges will yield the top cards more often than expected.
-
When I first created this program, I had 18 cards in the card pool. I discovered that by basing my odds off of the Fibonacci sequence, I could achieve a relatively successful distribution. The top card would be assigned to the number 1, the second card to numbers 2 and 3, the third card to numbers 4 through 6, the fourth card to numbers 7 through 11, and so on and so forth until the eighteenth card is assigned to numbers 6764 through 10944. The random number generator would produce a number between 1 and 10944, and the corresponding card would be sent. The distribution ensured that the top cards would be quite hard to get, but not impossible. Every once in a while, a card in the top 4 ranks would appear in the gacha.