iservice #3
6
rpg/__init__.py
Normal file
6
rpg/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from .rpg import RPG
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
cog = RPG(bot)
|
||||||
|
await bot.add_cog(cog)
|
||||||
|
await bot.add_cog(cog.commands)
|
||||||
126
rpg/actions.py
Normal file
126
rpg/actions.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
import discord
|
||||||
|
from .check import Check
|
||||||
|
from redbot.core import commands, Config
|
||||||
|
from .inventory import RPGInventory
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
class RPGActions:
|
||||||
|
"""
|
||||||
|
Implements the core RPG mechanics and actions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, rpg_cog):
|
||||||
|
self.rpg_cog = rpg_cog
|
||||||
|
self.inventory = RPGInventory(rpg_cog)
|
||||||
|
|
||||||
|
async def create_character(self, interaction: discord.Interaction, user, character_name):
|
||||||
|
"""
|
||||||
|
Creates a new character for the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Character Name Validation
|
||||||
|
check = Check(interaction, length=30)
|
||||||
|
if not check.length_under(interaction.message) or not character_name.isalnum():
|
||||||
|
return await self.rpg_cog.send_message(interaction, "Invalid character name. Please choose a name between 2 and 30 characters long, containing only letters and numbers.")
|
||||||
|
|
||||||
|
# Retrieve the 'characters' group
|
||||||
|
characters_group = self.rpg_cog.config.member(user).characters
|
||||||
|
|
||||||
|
# Then, get all characters within that group (await the .all() call)
|
||||||
|
existing_characters = await characters_group.all()
|
||||||
|
|
||||||
|
# Check for duplicate character names (case-insensitive)
|
||||||
|
if any(existing_name.lower() == character_name.lower() for existing_name in existing_characters):
|
||||||
|
return await self.rpg_cog.send_message(interaction, f"You already have a character named '{character_name}'. Choose another name.")
|
||||||
|
|
||||||
|
# Retrieve available classes from config
|
||||||
|
try:
|
||||||
|
available_classes = await self.rpg_cog.config.guild(user.guild).get_raw("classes", default=[])
|
||||||
|
except KeyError:
|
||||||
|
return await self.rpg_cog.send_message(interaction, "No classes have been configured yet. Please contact an admin.")
|
||||||
|
|
||||||
|
if not available_classes:
|
||||||
|
return await self.rpg_cog.send_message(interaction, "No classes are available yet. Please contact an admin.")
|
||||||
|
|
||||||
|
# Prompt user to choose a class
|
||||||
|
class_options = "\n".join([f"{i+1}. {class_name}" for i, class_name in enumerate(available_classes)])
|
||||||
|
await self.rpg_cog.send_message(interaction, f"Choose a class for your character:\n{class_options}")
|
||||||
|
|
||||||
|
def check(m):
|
||||||
|
return m.author == user and m.channel == interaction.channel and m.content.isdigit() and 1 <= int(m.content) <= len(available_classes)
|
||||||
|
|
||||||
|
try:
|
||||||
|
class_choice = await self.rpg_cog.bot.wait_for("message", check=check, timeout=30.0)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
return await self.rpg_cog.send_message(interaction, "Class selection timed out. Character creation canceled.")
|
||||||
|
|
||||||
|
selected_class = available_classes[int(class_choice.content) - 1]
|
||||||
|
|
||||||
|
# Retrieve default stats for the selected class
|
||||||
|
try:
|
||||||
|
default_stats = await self.rpg_cog.config.guild(user.guild).get_raw("characters", "classes", selected_class, "stats")
|
||||||
|
except KeyError:
|
||||||
|
return await self.rpg_cog.send_message(interaction, f"Default stats for class '{selected_class}' haven't been configured yet. Please contact an admin.")
|
||||||
|
|
||||||
|
# Create character data
|
||||||
|
character_data = {
|
||||||
|
"name": character_name,
|
||||||
|
"class": [selected_class], # Store the selected class
|
||||||
|
"level": 1,
|
||||||
|
"experience": 0,
|
||||||
|
"stats": default_stats.copy(),
|
||||||
|
"inventory": [],
|
||||||
|
"equipment": {},
|
||||||
|
"skills": [],
|
||||||
|
"gold": 0,
|
||||||
|
"max_health": 100,
|
||||||
|
"health": 100,
|
||||||
|
"mana": 50,
|
||||||
|
"max_mana": 50,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Store character data in config
|
||||||
|
await characters_group.set_raw(character_name, value=character_data)
|
||||||
|
await self.rpg_cog.config.member(user).active_character.set(character_name)
|
||||||
|
|
||||||
|
await interaction.response.send_message(interaction, f"Character '{character_name}' (Class: {selected_class}) created for {user.mention}!")
|
||||||
|
|
||||||
|
async def attack(self, ctx, attacker, target):
|
||||||
|
"""
|
||||||
|
Handles the attack action.
|
||||||
|
"""
|
||||||
|
# Check if attacker and target have active characters
|
||||||
|
if not await self._has_active_character(ctx, attacker):
|
||||||
|
return
|
||||||
|
if not await self._has_active_character(ctx, target):
|
||||||
|
return
|
||||||
|
|
||||||
|
attacker_data = await self.rpg_cog.config.member(attacker).active_character
|
||||||
|
target_data = await self.rpg_cog.config.member(target).active_character
|
||||||
|
|
||||||
|
# ... (rest of the attack logic)
|
||||||
|
|
||||||
|
async def heal(self, ctx, user):
|
||||||
|
"""
|
||||||
|
Handles the heal action.
|
||||||
|
"""
|
||||||
|
# ... (rest of the heal logic)
|
||||||
|
|
||||||
|
# Helper function to check if a user has an active character
|
||||||
|
async def _has_active_character(self, ctx, user):
|
||||||
|
active_character = await self.rpg_cog.config.member(user).active_character
|
||||||
|
if not active_character:
|
||||||
|
await self.rpg_cog.send_message(ctx, f"{user.mention}, you don't have an active character. Create one using `[p]create_character <name>`.")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def use_item(self, ctx, user, item_name):
|
||||||
|
"""
|
||||||
|
Handles the use of an item from the inventory.
|
||||||
|
"""
|
||||||
|
# ... (logic to check if the user has the item and handle its effects)
|
||||||
|
|
||||||
|
# Remove the used item from the inventory
|
||||||
|
await self.inventory.remove_item(ctx, user, item_name)
|
||||||
94
rpg/check.py
Normal file
94
rpg/check.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
from redbot.core import Config, commands
|
||||||
|
from collections.abc import Iterable
|
||||||
|
import validators as vals
|
||||||
|
import logging
|
||||||
|
import discord
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger("red.rpg")
|
||||||
|
|
||||||
|
class Check:
|
||||||
|
|
||||||
|
def __init__(self, ctx_or_interaction, custom: Iterable = None, length: int = None):
|
||||||
|
self.ctx_or_interaction = ctx_or_interaction # Store the context or interaction object
|
||||||
|
self.custom = custom
|
||||||
|
self.length = length
|
||||||
|
|
||||||
|
def _get_author(self):
|
||||||
|
"""
|
||||||
|
Helper function to get the author from either ctx or interaction.
|
||||||
|
"""
|
||||||
|
if isinstance(self.ctx_or_interaction, commands.Context):
|
||||||
|
return self.ctx_or_interaction.author
|
||||||
|
elif isinstance(self.ctx_or_interaction, discord.Interaction):
|
||||||
|
return self.ctx_or_interaction.user
|
||||||
|
else:
|
||||||
|
log.error(f"Unexpected object type in Check._get_author: {type(self.ctx_or_interaction)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def same(self, m):
|
||||||
|
author = self._get_author()
|
||||||
|
if author is None:
|
||||||
|
return False # Handle the case where author couldn't be determined
|
||||||
|
|
||||||
|
if isinstance(m, discord.Message):
|
||||||
|
return author == m.author
|
||||||
|
elif isinstance(m, discord.Interaction):
|
||||||
|
return author == m.user
|
||||||
|
else:
|
||||||
|
log.error(f"Unexpected object type in Check.same: {type(m)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def comfirm(self, m):
|
||||||
|
return self.same(m) and m.content.lower() in ("yes", "no")
|
||||||
|
|
||||||
|
def valid_int(self, m):
|
||||||
|
return self.same and m.content.isdigit()
|
||||||
|
|
||||||
|
def valid_float(self, m):
|
||||||
|
try:
|
||||||
|
return self.same(m) and float(m.content) >= 1
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def positive(self, m):
|
||||||
|
return self.same(m) and m.content.isdigit() and int(m.content) > 0
|
||||||
|
|
||||||
|
def role(self, m):
|
||||||
|
roles = [r.name for r in self.ctx.guild.roles if r.name != "Bot"]
|
||||||
|
return self.same(m) and m.content in roles
|
||||||
|
|
||||||
|
def member(self, m):
|
||||||
|
return self.same(m) and m.content in [x.name for x in self.ctx.guild.members]
|
||||||
|
|
||||||
|
def length_under(self, m):
|
||||||
|
try:
|
||||||
|
if isinstance(m, discord.Message):
|
||||||
|
content = m.content
|
||||||
|
elif isinstance(m, discord.Interaction):
|
||||||
|
content = m.data['components'][0]['components'][0]['value'] # Access modal input value
|
||||||
|
else:
|
||||||
|
raise ValueError("Unsupported object type for length_under check")
|
||||||
|
|
||||||
|
return self.same(m) and len(content) <= self.length
|
||||||
|
except TypeError:
|
||||||
|
raise ValueError("Length was not specified in Check")
|
||||||
|
|
||||||
|
def valid_image_url(self, m):
|
||||||
|
url = m.content.strip()
|
||||||
|
|
||||||
|
if not vals.url(url):
|
||||||
|
return False
|
||||||
|
|
||||||
|
valid_extensions = (".jpg", ".jpeg", ".png", ".gif")
|
||||||
|
if not any(url.lower().endswith(ext) for ext in valid_extensions):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def content(self, m):
|
||||||
|
try:
|
||||||
|
return self.same(m) and m.content in self.custom
|
||||||
|
except TypeError:
|
||||||
|
raise ValueError("A custom iterable was not set in Check")
|
||||||
21
rpg/commands.py
Normal file
21
rpg/commands.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import discord
|
||||||
|
from redbot.core import commands
|
||||||
|
from .menu import RPGMenu
|
||||||
|
|
||||||
|
class RPGCommands(commands.Cog):
|
||||||
|
"""
|
||||||
|
Handles user commands (primarily menus) for the RPG system.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, rpg_cog, bot):
|
||||||
|
self.rpg_cog = rpg_cog
|
||||||
|
self.bot = bot
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@commands.command(name="menu")
|
||||||
|
async def menu_command(self, ctx):
|
||||||
|
"""
|
||||||
|
Display the RPG menu.
|
||||||
|
"""
|
||||||
|
view = RPGMenu(self.rpg_cog)
|
||||||
|
await ctx.send("Choose an action:", view=view)
|
||||||
11
rpg/info.json
Normal file
11
rpg/info.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"author" : ["UnstableKitsune (unstablekitsune)"],
|
||||||
|
"install _msg" : "Oh you installed me! How dare you obtain me! return me right now!",
|
||||||
|
"name" : "RPG",
|
||||||
|
"short" : "Its a TTRPG",
|
||||||
|
"requirements" : [""],
|
||||||
|
"permissions" : [""],
|
||||||
|
"tags" : [""],
|
||||||
|
"min_python_version" : [3, 1, 1],
|
||||||
|
"end_user_data_statement" : ""
|
||||||
|
}
|
||||||
80
rpg/inventory.py
Normal file
80
rpg/inventory.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import discord
|
||||||
|
from redbot.core import commands, Config
|
||||||
|
|
||||||
|
class RPGInventory:
|
||||||
|
"""
|
||||||
|
Manages character inventories for the RPG system.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, rpg_cog):
|
||||||
|
self.rpg_cog = rpg_cog
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@commands.group()
|
||||||
|
async def inventory(self, ctx):
|
||||||
|
"""Main RPG command group."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@inventory.command(name="add")
|
||||||
|
async def add_item(self, ctx, user: discord.Member, item_name, quantity=1):
|
||||||
|
"""
|
||||||
|
Adds an item to the user's active character's inventory.
|
||||||
|
"""
|
||||||
|
character_data = await self.rpg_cog.config.member(user).get_raw("active_character")
|
||||||
|
if not character_data:
|
||||||
|
return await self.rpg_cog.send_message(ctx, f"{user.mention}, you don't have an active character.")
|
||||||
|
|
||||||
|
inventory = character_data.get("inventory", {})
|
||||||
|
if item_name in inventory:
|
||||||
|
inventory[item_name] += quantity
|
||||||
|
else:
|
||||||
|
inventory[item_name] = quantity
|
||||||
|
|
||||||
|
await self.rpg_cog.config.member(user).set_raw("active_character", "inventory", value=inventory)
|
||||||
|
await self.rpg_cog.send_message(ctx, f"{quantity} {item_name}(s) added to your inventory!")
|
||||||
|
|
||||||
|
@inventory.command(name="remove")
|
||||||
|
async def remove_item(self, ctx, user: discord.Member, item_name, quantity=1):
|
||||||
|
"""
|
||||||
|
Removes an item from the user's active character's inventory.
|
||||||
|
"""
|
||||||
|
character_data = await self.rpg_cog.config.member(user).get_raw("active_character")
|
||||||
|
if not character_data:
|
||||||
|
return await self.rpg_cog.send_message(ctx, f"{user.mention}, you don't have an active character.")
|
||||||
|
|
||||||
|
inventory = character_data.get("inventory", {})
|
||||||
|
if item_name in inventory:
|
||||||
|
if inventory[item_name] >= quantity:
|
||||||
|
inventory[item_name] -= quantity
|
||||||
|
if inventory[item_name] == 0:
|
||||||
|
del inventory[item_name]
|
||||||
|
await self.rpg_cog.config.member(user).set_raw("active_character", "inventory", value=inventory)
|
||||||
|
await self.rpg_cog.send_message(ctx, f"{quantity} {item_name}(s) removed from your inventory!")
|
||||||
|
else:
|
||||||
|
await self.rpg_cog.send_message(ctx, f"You don't have enough {item_name}s.")
|
||||||
|
else:
|
||||||
|
await self.rpg_cog.send_message(ctx, f"You don't have any {item_name}s in your inventory.")
|
||||||
|
|
||||||
|
@inventory.command(name="bag")
|
||||||
|
async def get_inventory(self, user):
|
||||||
|
"""
|
||||||
|
Retrieves the inventory of the user's active character.
|
||||||
|
"""
|
||||||
|
character_data = await self.rpg_cog.config.member(user).get_raw("active_character")
|
||||||
|
if not character_data:
|
||||||
|
return None # Or handle the case where the user has no active character
|
||||||
|
return character_data.get("inventory", {})
|
||||||
|
|
||||||
|
@inventory.command(name="showbag")
|
||||||
|
async def display_inventory(self, ctx, user: discord.Member):
|
||||||
|
"""
|
||||||
|
Displays the user's inventory in a user-friendly format.
|
||||||
|
"""
|
||||||
|
inventory = await self.get_inventory(user)
|
||||||
|
if not inventory:
|
||||||
|
return await self.rpg_cog.send_message(ctx, f"{user.mention}, your inventory is empty!")
|
||||||
|
|
||||||
|
# Format the inventory for display (you can customize this)
|
||||||
|
inventory_str = "\n".join([f"{item}: {quantity}" for item, quantity in inventory.items()])
|
||||||
|
embed = discord.Embed(title=f"{user.name}'s Inventory", description=inventory_str)
|
||||||
|
await self.rpg_cog.send_message(ctx, embed=embed)
|
||||||
55
rpg/menu.py
Normal file
55
rpg/menu.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import discord
|
||||||
|
from .check import Check
|
||||||
|
|
||||||
|
class RPGMenu(discord.ui.View):
|
||||||
|
def __init__(self, rpg_cog):
|
||||||
|
super().__init__()
|
||||||
|
self.rpg_cog = rpg_cog
|
||||||
|
|
||||||
|
@discord.ui.button(label="Create Character", style=discord.ButtonStyle.primary, custom_id="create_character_button")
|
||||||
|
async def create_character_button_callback(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
await interaction.response.send_modal(CreateCharacterModal(self.rpg_cog))
|
||||||
|
|
||||||
|
@discord.ui.button(label="Attack", style=discord.ButtonStyle.red, custom_id="attack_button")
|
||||||
|
async def attack_button_callback(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
# You'll need to implement the target selection logic here (e.g., using a dropdown or another interaction)
|
||||||
|
target = None # Placeholder for now
|
||||||
|
if target:
|
||||||
|
await self.rpg_cog.actions.attack(interaction, interaction.user, target)
|
||||||
|
else:
|
||||||
|
await interaction.response.send_message("You need to select a target to attack.", ephemeral=True)
|
||||||
|
|
||||||
|
@discord.ui.button(label="Heal", style=discord.ButtonStyle.green, custom_id="heal_button")
|
||||||
|
async def heal_button_callback(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
await self.rpg_cog.actions.heal(interaction, interaction.user)
|
||||||
|
|
||||||
|
@discord.ui.button(label="Stats", style=discord.ButtonStyle.blurple, custom_id="stats_button")
|
||||||
|
async def stats_button_callback(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
await self.rpg_cog.actions.display_stats(interaction, interaction.user)
|
||||||
|
|
||||||
|
@discord.ui.button(label="Inventory", style=discord.ButtonStyle.grey, custom_id="inventory_button")
|
||||||
|
async def inventory_button_callback(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
await self.rpg_cog.inventory.display_inventory(interaction, interaction.user)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateCharacterModal(discord.ui.Modal):
|
||||||
|
def __init__(self, rpg_cog):
|
||||||
|
super().__init__(title="Create Character")
|
||||||
|
self.rpg_cog = rpg_cog
|
||||||
|
self.character_name = discord.ui.TextInput(
|
||||||
|
label="Enter your character's name:",
|
||||||
|
placeholder="e.g., BraveAdventurer",
|
||||||
|
required=True,
|
||||||
|
max_length=30
|
||||||
|
)
|
||||||
|
self.add_item(self.character_name)
|
||||||
|
|
||||||
|
async def on_submit(self, interaction: discord.Interaction):
|
||||||
|
character_name = self.character_name.value
|
||||||
|
#author = interaction.user
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.rpg_cog.actions.create_character(interaction, interaction.user, character_name)
|
||||||
|
except Exception as e:
|
||||||
|
await interaction.response.send_message(f"```An error occurred while creating your character: {e}```")
|
||||||
|
self.rpg_cog.log.error(f"Error in create_character: {e}")
|
||||||
187
rpg/rpg.py
Normal file
187
rpg/rpg.py
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
from atexit import register
|
||||||
|
import logging
|
||||||
|
import discord
|
||||||
|
|
||||||
|
from distutils import config
|
||||||
|
from redbot.core import commands, Config
|
||||||
|
from .check import Check
|
||||||
|
from .commands import RPGCommands
|
||||||
|
from .actions import RPGActions
|
||||||
|
from .shop import RPGShops
|
||||||
|
from .menu import RPGMenu
|
||||||
|
from .inventory import RPGInventory
|
||||||
|
|
||||||
|
|
||||||
|
class RPG(commands.Cog):
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
self.menu = RPGMenu(self)
|
||||||
|
self.commands = RPGCommands(self, bot)
|
||||||
|
self.actions = RPGActions(self)
|
||||||
|
self.shop = RPGShops(self)
|
||||||
|
self.inventory = RPGInventory(self)
|
||||||
|
self.log = logging.getLogger("red.rpg")
|
||||||
|
self.config = Config.get_conf(self, identifier=6857309412)
|
||||||
|
|
||||||
|
default_guild_settings = {
|
||||||
|
"characters": {
|
||||||
|
"default_stats": { # Nested under 'characters'
|
||||||
|
"Strength": 1,
|
||||||
|
"Defense": 1,
|
||||||
|
"Agility": 1,
|
||||||
|
"Magic": 1,
|
||||||
|
"Luck": 1,
|
||||||
|
"Health": 100,
|
||||||
|
"Mana": 50,
|
||||||
|
"Stamina": 50,
|
||||||
|
"Experience": 0,
|
||||||
|
"Gold": 0,
|
||||||
|
"Damage": 1,
|
||||||
|
"Recovery": 1,
|
||||||
|
"Resist": 1,
|
||||||
|
"Critical": 1,
|
||||||
|
"Speed": 1,
|
||||||
|
"Level": 1,
|
||||||
|
"Class": [],
|
||||||
|
"Skills": [],
|
||||||
|
"Inventory": [],
|
||||||
|
"Equipment": {},
|
||||||
|
"Quests": [],
|
||||||
|
"Guild": None,
|
||||||
|
"Location": None,
|
||||||
|
"Followers": None,
|
||||||
|
"Pets": None,
|
||||||
|
},
|
||||||
|
"default_statsmax": { # Nested under 'characters'
|
||||||
|
"Strength": 999,
|
||||||
|
"Defense": 999,
|
||||||
|
"Agility": 999,
|
||||||
|
"Magic": 999,
|
||||||
|
"Luck": 999,
|
||||||
|
"Health": 999,
|
||||||
|
"Mana": 999,
|
||||||
|
"Stamina": 999,
|
||||||
|
"Experience": 999999999,
|
||||||
|
"Gold": 999999999,
|
||||||
|
"Damage": 999,
|
||||||
|
"Recovery": 999,
|
||||||
|
"Resist": 999,
|
||||||
|
"Critical": 999,
|
||||||
|
"Speed": 999,
|
||||||
|
"Level": 999999999,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"classes": { # Definitions of available classes
|
||||||
|
"Warrior": {
|
||||||
|
"stats": {
|
||||||
|
"Strength": 5,
|
||||||
|
"Defense": 4,
|
||||||
|
"Agility": 3,
|
||||||
|
# ... other stats with appropriate values for a warrior
|
||||||
|
},
|
||||||
|
"description": "A brave and sturdy warrior, skilled in melee combat."
|
||||||
|
},
|
||||||
|
"Mage": {
|
||||||
|
"stats": {
|
||||||
|
"Strength": 2,
|
||||||
|
"Defense": 2,
|
||||||
|
"Magic": 5,
|
||||||
|
# ... other stats with appropriate values for a mage
|
||||||
|
},
|
||||||
|
"description": "A wise and powerful mage, capable of wielding arcane magic."
|
||||||
|
},
|
||||||
|
"Rogue": {
|
||||||
|
"stats": {
|
||||||
|
"Strength": 3,
|
||||||
|
"Defense": 3,
|
||||||
|
"Agility": 5,
|
||||||
|
# ... other stats with appropriate values for a rogue
|
||||||
|
},
|
||||||
|
"description": "A nimble and cunning rogue, adept at stealth and trickery."
|
||||||
|
}
|
||||||
|
# ... add more classes as needed
|
||||||
|
},
|
||||||
|
"active_character": {}, # Store the name of the currently active character for each member
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default_user_settings = {
|
||||||
|
"active_character": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
default_shop = {
|
||||||
|
"Items": [],
|
||||||
|
"Prices": [],
|
||||||
|
"Currency": "Gold",
|
||||||
|
"Currency_Symbol": "G",
|
||||||
|
"Currency_Plural": "Gold",
|
||||||
|
"Currency_Plural_Symbol": "G",
|
||||||
|
"Currency_Emoji": "💰",
|
||||||
|
}
|
||||||
|
|
||||||
|
default_quest = {
|
||||||
|
"Name": None,
|
||||||
|
"Description": None,
|
||||||
|
"Reward": None,
|
||||||
|
"Requirements": None,
|
||||||
|
"Progress": None,
|
||||||
|
"Status": None,
|
||||||
|
"Type": None,
|
||||||
|
"Repeat": None,
|
||||||
|
"Time": None,
|
||||||
|
"Location": None,
|
||||||
|
"Guild": None,
|
||||||
|
"Followers": None,
|
||||||
|
"Pets": None,
|
||||||
|
}
|
||||||
|
self.config.register_guild(**default_guild_settings)
|
||||||
|
self.config.register_user(**default_user_settings)
|
||||||
|
self.config.register_global(**default_shop)
|
||||||
|
self.config.register_global(**default_quest)
|
||||||
|
|
||||||
|
|
||||||
|
self.commands = RPGCommands(self, bot)
|
||||||
|
self.actions = RPGActions(self)
|
||||||
|
self.shop = RPGShops(self)
|
||||||
|
self.menu = RPGMenu(self)
|
||||||
|
self.inventory = RPGInventory(self)
|
||||||
|
|
||||||
|
async def register_guild(self, ctx):
|
||||||
|
await self.config.register_guild(ctx.guild, **default_guild_settings)
|
||||||
|
await self.send_message(ctx, "Guild registered for RPG system.")
|
||||||
|
|
||||||
|
async def register_user(self, ctx):
|
||||||
|
await self.config.register_user(ctx.author, **default_user_settings)
|
||||||
|
await self.send_message(ctx, "User registered for RPG system.")
|
||||||
|
|
||||||
|
async def register_shop(self, ctx):
|
||||||
|
await self.config.register_global(**default_shop)
|
||||||
|
await self.send_message(ctx, "Shop registered for RPG system.")
|
||||||
|
|
||||||
|
async def register_quest(self, ctx):
|
||||||
|
await self.config.register_global(**default_quest)
|
||||||
|
await self.send_message(ctx, "Quest registered for RPG system.")
|
||||||
|
|
||||||
|
async def unregister_guild(self, ctx):
|
||||||
|
await self.config.unregister_guild(ctx.guild)
|
||||||
|
await self.send_message(ctx, "Guild unregistered for RPG system.")
|
||||||
|
|
||||||
|
async def unregister_user(self, ctx):
|
||||||
|
await self.config.unregister_user(ctx.author)
|
||||||
|
await self.send_message(ctx, "User unregistered for RPG system.")
|
||||||
|
|
||||||
|
async def unregister_shop(self, ctx):
|
||||||
|
await self.config.unregister_global("Shop")
|
||||||
|
await self.send_message(ctx, "Shop unregistered for RPG system.")
|
||||||
|
|
||||||
|
async def unregister_quest(self, ctx):
|
||||||
|
await self.config.unregister_global("Quest")
|
||||||
|
await self.send_message(ctx, "Quest unregistered for RPG system.")
|
||||||
|
|
||||||
|
async def send_message(self, ctx, message, interaction, embed=None):
|
||||||
|
if embed:
|
||||||
|
await ctx.send(message, embed=embed)
|
||||||
|
elif interaction:
|
||||||
|
await interaction.response.send(message)
|
||||||
|
else:
|
||||||
|
await ctx.send(message)
|
||||||
176
rpg/shop.py
Normal file
176
rpg/shop.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
from ast import alias
|
||||||
|
from enum import member
|
||||||
|
from locale import currency
|
||||||
|
import discord
|
||||||
|
from redbot.core import commands
|
||||||
|
from .inventory import RPGInventory
|
||||||
|
|
||||||
|
|
||||||
|
class RPGShops(commands.Cog):
|
||||||
|
|
||||||
|
def __init__(self, rpg_cog):
|
||||||
|
self.rpg_cog = rpg_cog
|
||||||
|
self.config = self.rpg_cog
|
||||||
|
|
||||||
|
default_guild = {
|
||||||
|
"shops": {},
|
||||||
|
"status": {
|
||||||
|
"active": bool,
|
||||||
|
"deactive": bool,
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"name": str,
|
||||||
|
"symbol": str,
|
||||||
|
"emoji": str,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#self.config.register_guild(**default_guild_settings)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
# --------------- SHOP GROUP COMMANDS -----------------------------------------------------
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@commands.group(name="shop", alias=["sp"])
|
||||||
|
async def shop(self, ctx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Comes from Base Shop Group Above
|
||||||
|
@shop.group(name="manager", alias=["mgr"])
|
||||||
|
async def manager(self, ctx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Comes from Manager Group Above
|
||||||
|
@manager.group(name="set", alias=["st"])
|
||||||
|
async def set(self, ctx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Comes From Set Group From Above
|
||||||
|
@set.group(name="status", alias=["sta"])
|
||||||
|
async def status(self, ctx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Comes From Set Group From Above
|
||||||
|
@set.group(name="currency", alias=["cur"])
|
||||||
|
async def currency(self, ctx):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
# ------------------ SHOP BASE COMMANDS --------------------------------------------------
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@shop.command(name="buy", alias=["by"]) # shop buy
|
||||||
|
async def buy(self, ctx, shop_name: str, item_name: str, quantity: int = 1):
|
||||||
|
"""
|
||||||
|
Buy an item from the shop.
|
||||||
|
"""
|
||||||
|
# Retrieve shop data
|
||||||
|
shops = await self.config.guild(ctx.guild).shops.all()
|
||||||
|
if shop_name not in shops:
|
||||||
|
return await ctx.send(f"Shop '{shop_name}' not found.")
|
||||||
|
|
||||||
|
shop_data = shops[shop_name]
|
||||||
|
items = shop_data.get("items", {}) # Assuming you store items in an 'items' dictionary within the shop data
|
||||||
|
if item_name not in items:
|
||||||
|
return await ctx.send(f"Item '{item_name}' not found in '{shop_name}'.")
|
||||||
|
|
||||||
|
item_data = items[item_name]
|
||||||
|
price = item_data.get("price")
|
||||||
|
stock = item_data.get("quantity")
|
||||||
|
|
||||||
|
# Check if the shop has enough stock
|
||||||
|
if stock is not None and stock < quantity:
|
||||||
|
return await ctx.send(f"Not enough '{item_name}' in stock. Only {stock} available.")
|
||||||
|
|
||||||
|
# Check if the user has enough currency
|
||||||
|
currency_name = await self.config.guild(ctx.guild).shops_currency_name.get()
|
||||||
|
user_balance = await self.rpg_cog.config.member(ctx.author).get_raw("gold") # Assuming gold is the currency
|
||||||
|
total_cost = price * quantity
|
||||||
|
if user_balance < total_cost:
|
||||||
|
return await ctx.send(f"You don't have enough {currency_name} to buy {quantity} {item_name}(s).")
|
||||||
|
|
||||||
|
# Deduct currency and add item to inventory
|
||||||
|
await self.rpg_cog.config.member(ctx.author).set_raw("gold", value=user_balance - total_cost)
|
||||||
|
await self.rpg_cog.inventory.add_item(ctx, ctx.author, item_name, quantity)
|
||||||
|
|
||||||
|
# Update shop inventory (if applicable)
|
||||||
|
if stock is not None:
|
||||||
|
shop_data["items"][item_name]["quantity"] -= quantity
|
||||||
|
await self.config.guild(ctx.guild).shops.set_raw(shop_name, value=shop_data)
|
||||||
|
|
||||||
|
await ctx.send(f"You bought {quantity} {item_name}(s) for {total_cost} {currency_name}!")
|
||||||
|
|
||||||
|
@shop.command(name="sell", alias=["sl"]) # shop sell
|
||||||
|
async def buy(self, ctx, item_name: str):
|
||||||
|
"""
|
||||||
|
Sell an item back to the shop.
|
||||||
|
"""
|
||||||
|
# ... (logic to handle the purchase and currency)
|
||||||
|
|
||||||
|
# Add the purchased item to the user's inventory
|
||||||
|
await self.inventory.remove_item(ctx, ctx.author, item_name)
|
||||||
|
|
||||||
|
@shop.command(name="give", alias=["gi"]) # shop give
|
||||||
|
async def give(self, ctx, item_name: str, target: member):
|
||||||
|
"""
|
||||||
|
Give an item to another player.
|
||||||
|
"""
|
||||||
|
# ... (logic to handle the transfer of items between players)
|
||||||
|
await self.inventory.remove_item(ctx, ctx.author, item_name)
|
||||||
|
await self.inventory.add_item(ctx, target, item_name)
|
||||||
|
|
||||||
|
@shop.command(name="list", alias=["ls"]) # shop list
|
||||||
|
async def list(self, ctx, shop_name: str):
|
||||||
|
"""
|
||||||
|
List the items available in a shop.
|
||||||
|
"""
|
||||||
|
# ... (logic to retrieve and display the items in the shop)
|
||||||
|
items = await self.rpg_cog.config.guild(ctx.guild).shops_items.get_raw(shop_name)
|
||||||
|
prices = await self.rpg_cog.config.guild(ctx.guild).shops_prices.get_raw(shop_name)
|
||||||
|
|
||||||
|
if items:
|
||||||
|
item_list = "\n".join([f"{item} - {price}" for item, price in zip(items, prices)])
|
||||||
|
await ctx.send(f"Items available in {shop_name}:\n{item_list}")
|
||||||
|
else:
|
||||||
|
await ctx.send(f"No items available in {shop_name}.")
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
# ------------------------- Manager Base Commands -----------------------------------------
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@manager.command(name="create", alias=["cr"])
|
||||||
|
async def create_shop(self, ctx, shop_name: str, *, description: str):
|
||||||
|
await ctx.send(f"Shop '{shop_name}' created!")
|
||||||
|
|
||||||
|
@manager.command(name="delete", alias=["del"])
|
||||||
|
async def delete_shop(self, ctx, shop_name: str):
|
||||||
|
await ctx.send(f"Shop '{shop_name}' deleted!")
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
# --------------------------- SET BASE GROUP ----------------------------------------------
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
# --------------------------- STATUS BASE COMMANDS ----------------------------------------
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@status.command(name="active", alias=["act"])
|
||||||
|
async def active_shop(self, ctx, shop_name: str):
|
||||||
|
await ctx.send(f"Shop '{shop_name}' activated!")
|
||||||
|
|
||||||
|
@status.command(name="deactive", alias=["deact"])
|
||||||
|
async def deactive_shop(self, ctx, shop_name: str):
|
||||||
|
await ctx.send(f"Shop '{shop_name}' deactivated!")
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
# --------------------------- CURRENCY BASE COMMANDS --------------------------------------
|
||||||
|
# -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@currency.command(name="name", alias=["nm"]) # Use @currency.command
|
||||||
|
async def name_currency(self, ctx, currency_name: str):
|
||||||
|
await ctx.send(f"Currency name set to '{currency_name}'!")
|
||||||
|
|
||||||
|
@currency.command(name="symbol", alias=["sym"]) # Use @currency.command
|
||||||
|
async def symbol_currency(self, ctx, currency_symbol: str):
|
||||||
|
await ctx.send(f"Currency symbol set to '{currency_symbol}'!")
|
||||||
|
|
||||||
|
@currency.command(name="emoji", alias=["emo"]) # Use @currency.command
|
||||||
|
async def emoji_currency(self, ctx, currency_emoji: str):
|
||||||
|
await ctx.send(f"Currency emoji set to '{currency_emoji}'!")
|
||||||
Reference in New Issue
Block a user