From 6805dbec1b81f8e80ad19ac7a89caffb7f58eba1 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 28 May 2025 21:50:28 -0400 Subject: [PATCH] Adjust fail rate for the 'rob' job in Unbelievaboat to improve gameplay balance. The fail rate has been modified from 70% to 60%, enhancing user experience and engagement. --- unbelievaboat/business.py | 347 +++++++++++++++++++++++++++++++++ unbelievaboat/heist.py | 240 +++++++++++++++++++++++ unbelievaboat/investments.py | 202 +++++++++++++++++++ unbelievaboat/unbelievaboat.py | 2 +- 4 files changed, 790 insertions(+), 1 deletion(-) create mode 100644 unbelievaboat/business.py create mode 100644 unbelievaboat/heist.py create mode 100644 unbelievaboat/investments.py diff --git a/unbelievaboat/business.py b/unbelievaboat/business.py new file mode 100644 index 0000000..167386c --- /dev/null +++ b/unbelievaboat/business.py @@ -0,0 +1,347 @@ +import random +import asyncio +import discord +from redbot.core import commands, bank, Config +from redbot.core.utils.chat_formatting import humanize_number +from datetime import datetime, timedelta + +class Business(commands.Cog): + """Business system for UnbelievaBoat economy""" + + def __init__(self, bot): + self.bot = bot + self.config = Config.get_conf(self, identifier=95932766180346, force_registration=True) + + default_global = { + "business_types": { + "Restaurant": { + "cost": 25000, + "daily_revenue": {"min": 1000, "max": 3000}, + "employees": {"min": 2, "max": 8}, + "upkeep": 500 + }, + "Retail Store": { + "cost": 35000, + "daily_revenue": {"min": 2000, "max": 4000}, + "employees": {"min": 3, "max": 10}, + "upkeep": 750 + }, + "Tech Startup": { + "cost": 50000, + "daily_revenue": {"min": 3000, "max": 8000}, + "employees": {"min": 4, "max": 15}, + "upkeep": 1500 + } + }, + "upgrades": { + "Marketing": { + "cost": 10000, + "revenue_multiplier": 1.2 + }, + "Training": { + "cost": 15000, + "efficiency_boost": 1.15 + }, + "Automation": { + "cost": 25000, + "upkeep_reduction": 0.8 + } + } + } + + default_user = { + "businesses": {}, # {"business_name": {"type": "type", "employees": 0, "upgrades": [], "last_collection": null}} + "total_revenue": 0, + "total_expenses": 0 + } + + self.config.register_global(**default_global) + self.config.register_user(**default_user) + + # Start background task for passive income + self.income_task = self.bot.loop.create_task(self.generate_passive_income()) + + def cog_unload(self): + if self.income_task: + self.income_task.cancel() + + async def generate_passive_income(self): + """Background task to generate passive income for businesses""" + while True: + try: + all_users = await self.config.all_users() + for user_id, user_data in all_users.items(): + if not user_data["businesses"]: + continue + + user = self.bot.get_user(user_id) + if not user: + continue + + for business_name, business in user_data["businesses"].items(): + last_collection = datetime.fromisoformat(business["last_collection"]) if business["last_collection"] else None + if not last_collection or (datetime.now() - last_collection).total_seconds() >= 86400: # 24 hours + await self.collect_revenue(user, business_name, business) + + await asyncio.sleep(3600) # Check every hour + except Exception as e: + print(f"Error in passive income task: {e}") + await asyncio.sleep(60) + + async def collect_revenue(self, user, business_name, business): + """Collect revenue for a business""" + business_types = await self.config.business_types() + business_type = business_types[business["type"]] + + # Calculate base revenue + base_revenue = random.randint( + business_type["daily_revenue"]["min"], + business_type["daily_revenue"]["max"] + ) + + # Apply employee bonus + employee_bonus = 1 + (business["employees"] / business_type["employees"]["max"] * 0.5) + revenue = base_revenue * employee_bonus + + # Apply upgrades + upgrades = await self.config.upgrades() + for upgrade in business["upgrades"]: + if upgrade == "Marketing": + revenue *= upgrades["Marketing"]["revenue_multiplier"] + elif upgrade == "Training": + revenue *= upgrades["Training"]["efficiency_boost"] + + # Calculate upkeep + upkeep = business_type["upkeep"] * business["employees"] + if "Automation" in business["upgrades"]: + upkeep *= upgrades["Automation"]["upkeep_reduction"] + + # Calculate net profit + net_profit = revenue - upkeep + + # Update user's balance and statistics + try: + await bank.deposit_credits(user, int(net_profit)) + async with self.config.user(user).all() as user_data: + user_data["total_revenue"] += revenue + user_data["total_expenses"] += upkeep + user_data["businesses"][business_name]["last_collection"] = datetime.now().isoformat() + except Exception as e: + print(f"Error collecting revenue for {user.name}'s business: {e}") + + @commands.group() + async def business(self, ctx): + """Business management commands""" + pass + + @business.command(name="types") + async def list_business_types(self, ctx): + """List available business types""" + business_types = await self.config.business_types() + + embed = discord.Embed( + title="Available Business Types", + color=discord.Color.blue(), + description="Choose your business type wisely!" + ) + + for name, data in business_types.items(): + embed.add_field( + name=name, + value=f"Cost: ${data['cost']:,}\n" + f"Daily Revenue: ${data['daily_revenue']['min']:,}-${data['daily_revenue']['max']:,}\n" + f"Employees: {data['employees']['min']}-{data['employees']['max']}\n" + f"Daily Upkeep per Employee: ${data['upkeep']:,}", + inline=False + ) + + await ctx.send(embed=embed) + + @business.command(name="create") + async def create_business(self, ctx, business_type: str, *, business_name: str): + """Create a new business""" + business_types = await self.config.business_types() + if business_type not in business_types: + return await ctx.send("Invalid business type! Use `!business types` to see available types.") + + async with self.config.user(ctx.author).businesses() as businesses: + if business_name in businesses: + return await ctx.send("You already have a business with that name!") + + if len(businesses) >= 3: + return await ctx.send("You can only own up to 3 businesses!") + + cost = business_types[business_type]["cost"] + if not await bank.can_spend(ctx.author, cost): + return await ctx.send(f"You need ${cost:,} to start this business!") + + await bank.withdraw_credits(ctx.author, cost) + businesses[business_name] = { + "type": business_type, + "employees": business_types[business_type]["employees"]["min"], + "upgrades": [], + "last_collection": None + } + + await ctx.send(f"Congratulations! You are now the proud owner of {business_name}!") + + @business.command(name="view") + async def view_business(self, ctx, *, business_name: str = None): + """View your business(es)""" + businesses = await self.config.user(ctx.author).businesses() + if not businesses: + return await ctx.send("You don't own any businesses!") + + if business_name and business_name not in businesses: + return await ctx.send("You don't own a business with that name!") + + business_types = await self.config.business_types() + + if business_name: + # View specific business + business = businesses[business_name] + business_type = business_types[business["type"]] + + embed = discord.Embed( + title=f"{business_name} ({business['type']})", + color=discord.Color.green() + ) + + embed.add_field(name="Employees", value=f"{business['employees']}/{business_type['employees']['max']}") + embed.add_field(name="Daily Revenue Range", + value=f"${business_type['daily_revenue']['min']:,}-${business_type['daily_revenue']['max']:,}") + embed.add_field(name="Upgrades", value=", ".join(business["upgrades"]) or "None") + + if business["last_collection"]: + last_collection = datetime.fromisoformat(business["last_collection"]) + time_since = datetime.now() - last_collection + embed.add_field(name="Time Since Last Collection", + value=f"{time_since.days}d {time_since.seconds//3600}h") + else: + # View all businesses + embed = discord.Embed( + title=f"{ctx.author.name}'s Business Empire", + color=discord.Color.green() + ) + + for name, business in businesses.items(): + business_type = business_types[business["type"]] + embed.add_field( + name=name, + value=f"Type: {business['type']}\n" + f"Employees: {business['employees']}/{business_type['employees']['max']}\n" + f"Upgrades: {len(business['upgrades'])}", + inline=False + ) + + # Add overall statistics + user_data = await self.config.user(ctx.author).all() + embed.add_field( + name="Overall Statistics", + value=f"Total Revenue: ${user_data['total_revenue']:,}\n" + f"Total Expenses: ${user_data['total_expenses']:,}\n" + f"Net Profit: ${user_data['total_revenue']-user_data['total_expenses']:,}", + inline=False + ) + + await ctx.send(embed=embed) + + @business.command(name="hire") + async def hire_employees(self, ctx, business_name: str, amount: int): + """Hire employees for your business""" + if amount <= 0: + return await ctx.send("Please specify a positive number of employees to hire!") + + async with self.config.user(ctx.author).businesses() as businesses: + if business_name not in businesses: + return await ctx.send("You don't own a business with that name!") + + business = businesses[business_name] + business_type = (await self.config.business_types())[business["type"]] + + current_employees = business["employees"] + max_employees = business_type["employees"]["max"] + + if current_employees + amount > max_employees: + return await ctx.send(f"You can only have up to {max_employees} employees in this business!") + + hire_cost = amount * business_type["upkeep"] * 2 # Hiring cost = 2x daily upkeep + + if not await bank.can_spend(ctx.author, hire_cost): + return await ctx.send(f"You need ${hire_cost:,} to hire {amount} employees!") + + await bank.withdraw_credits(ctx.author, hire_cost) + business["employees"] += amount + + await ctx.send(f"Successfully hired {amount} employees for {business_name}!") + + @business.command(name="upgrade") + async def upgrade_business(self, ctx, business_name: str, upgrade_name: str): + """Purchase an upgrade for your business""" + upgrades = await self.config.upgrades() + if upgrade_name not in upgrades: + return await ctx.send("Invalid upgrade! Available upgrades: " + ", ".join(upgrades.keys())) + + async with self.config.user(ctx.author).businesses() as businesses: + if business_name not in businesses: + return await ctx.send("You don't own a business with that name!") + + business = businesses[business_name] + if upgrade_name in business["upgrades"]: + return await ctx.send("You already have this upgrade!") + + upgrade_cost = upgrades[upgrade_name]["cost"] + if not await bank.can_spend(ctx.author, upgrade_cost): + return await ctx.send(f"You need ${upgrade_cost:,} to purchase this upgrade!") + + await bank.withdraw_credits(ctx.author, upgrade_cost) + business["upgrades"].append(upgrade_name) + + await ctx.send(f"Successfully purchased the {upgrade_name} upgrade for {business_name}!") + + @business.command(name="sell") + async def sell_business(self, ctx, *, business_name: str): + """Sell one of your businesses""" + async with self.config.user(ctx.author).businesses() as businesses: + if business_name not in businesses: + return await ctx.send("You don't own a business with that name!") + + business = businesses[business_name] + business_type = (await self.config.business_types())[business["type"]] + + # Calculate sell value (50% of initial cost plus 25% per upgrade) + sell_value = business_type["cost"] * 0.5 + sell_value += len(business["upgrades"]) * (business_type["cost"] * 0.25) + + await bank.deposit_credits(ctx.author, int(sell_value)) + del businesses[business_name] + + await ctx.send(f"Successfully sold {business_name} for ${int(sell_value):,}!") + + @business.command(name="upgrades") + async def list_upgrades(self, ctx): + """List available business upgrades""" + upgrades = await self.config.upgrades() + + embed = discord.Embed( + title="Available Business Upgrades", + color=discord.Color.blue() + ) + + for name, data in upgrades.items(): + effects = [] + if "revenue_multiplier" in data: + effects.append(f"+{(data['revenue_multiplier']-1)*100}% Revenue") + if "efficiency_boost" in data: + effects.append(f"+{(data['efficiency_boost']-1)*100}% Efficiency") + if "upkeep_reduction" in data: + effects.append(f"-{(1-data['upkeep_reduction'])*100}% Upkeep") + + embed.add_field( + name=name, + value=f"Cost: ${data['cost']:,}\n" + f"Effects: {', '.join(effects)}", + inline=False + ) + + await ctx.send(embed=embed) \ No newline at end of file diff --git a/unbelievaboat/heist.py b/unbelievaboat/heist.py new file mode 100644 index 0000000..c7a6013 --- /dev/null +++ b/unbelievaboat/heist.py @@ -0,0 +1,240 @@ +import random +import asyncio +import discord +from redbot.core import commands, bank, Config +from redbot.core.utils.chat_formatting import humanize_number +from datetime import datetime, timedelta + +class Heist(commands.Cog): + """Heist system for UnbelievaBoat economy""" + + def __init__(self, bot): + self.bot = bot + self.config = Config.get_conf(self, identifier=95932766180345, force_registration=True) + self.active_heists = {} + + default_global = { + "targets": { + "Small Bank": { + "min_crew": 2, + "max_crew": 4, + "min_payout": 5000, + "max_payout": 20000, + "success_rate": 0.6, + "cooldown": 3600 # 1 hour + }, + "Casino": { + "min_crew": 3, + "max_crew": 6, + "min_payout": 10000, + "max_payout": 50000, + "success_rate": 0.5, + "cooldown": 7200 # 2 hours + }, + "Federal Reserve": { + "min_crew": 5, + "max_crew": 8, + "min_payout": 25000, + "max_payout": 100000, + "success_rate": 0.3, + "cooldown": 14400 # 4 hours + } + } + } + + default_guild = { + "last_heist": None, + "heist_cooldown": 1800 # 30 minutes between heists + } + + default_user = { + "heist_stats": { + "participated": 0, + "succeeded": 0, + "failed": 0, + "total_earned": 0 + }, + "last_heist": None + } + + self.config.register_global(**default_global) + self.config.register_guild(**default_guild) + self.config.register_user(**default_user) + + @commands.group() + async def heist(self, ctx): + """Heist commands""" + pass + + @heist.command(name="targets") + async def list_targets(self, ctx): + """List available heist targets""" + targets = await self.config.targets() + + embed = discord.Embed( + title="Available Heist Targets", + color=discord.Color.red(), + description="Choose your target wisely!" + ) + + for name, data in targets.items(): + embed.add_field( + name=name, + value=f"Crew Size: {data['min_crew']}-{data['max_crew']}\n" + f"Potential Payout: ${data['min_payout']:,}-${data['max_payout']:,}\n" + f"Success Rate: {data['success_rate']*100}%\n" + f"Cooldown: {data['cooldown']//3600} hours", + inline=False + ) + + await ctx.send(embed=embed) + + @heist.command(name="start") + async def start_heist(self, ctx, *, target_name: str): + """Start a heist on a specific target""" + if ctx.guild.id in self.active_heists: + return await ctx.send("A heist is already in progress in this server!") + + targets = await self.config.targets() + if target_name not in targets: + return await ctx.send("Invalid target! Use `!heist targets` to see available targets.") + + target = targets[target_name] + last_heist = await self.config.guild(ctx.guild).last_heist() + + if last_heist: + last_heist = datetime.fromisoformat(last_heist) + cooldown = timedelta(seconds=await self.config.guild(ctx.guild).heist_cooldown()) + if datetime.now() - last_heist < cooldown: + remaining = cooldown - (datetime.now() - last_heist) + return await ctx.send(f"The heat is still high! Wait {remaining.seconds//60} minutes before the next heist.") + + self.active_heists[ctx.guild.id] = { + "target": target_name, + "leader": ctx.author, + "crew": [ctx.author], + "status": "recruiting", + "min_crew": target["min_crew"], + "max_crew": target["max_crew"] + } + + embed = discord.Embed( + title="Heist Planning Phase", + description=f"{ctx.author.name} is planning a heist on {target_name}!\n" + f"Need {target['min_crew']-1} to {target['max_crew']-1} more crew members.\n" + f"Use `!heist join` to participate!\n" + f"Planning phase ends in 60 seconds.", + color=discord.Color.gold() + ) + + await ctx.send(embed=embed) + await asyncio.sleep(60) + + if ctx.guild.id in self.active_heists: + heist = self.active_heists[ctx.guild.id] + if len(heist["crew"]) < target["min_crew"]: + del self.active_heists[ctx.guild.id] + return await ctx.send("Not enough crew members joined. The heist has been cancelled.") + + await self.execute_heist(ctx, target_name) + + @heist.command(name="join") + async def join_heist(self, ctx): + """Join an active heist""" + if ctx.guild.id not in self.active_heists: + return await ctx.send("There is no active heist to join!") + + heist = self.active_heists[ctx.guild.id] + if heist["status"] != "recruiting": + return await ctx.send("This heist is no longer recruiting!") + + if ctx.author in heist["crew"]: + return await ctx.send("You're already in the crew!") + + if len(heist["crew"]) >= heist["max_crew"]: + return await ctx.send("The crew is already full!") + + heist["crew"].append(ctx.author) + await ctx.send(f"{ctx.author.name} has joined the heist! ({len(heist['crew'])}/{heist['max_crew']} crew members)") + + async def execute_heist(self, ctx, target_name): + """Execute the heist and determine the outcome""" + heist = self.active_heists[ctx.guild.id] + target = (await self.config.targets())[target_name] + + # Calculate success chance based on crew size and target difficulty + crew_bonus = (len(heist["crew"]) - target["min_crew"]) * 0.05 + final_success_rate = min(0.95, target["success_rate"] + crew_bonus) + + # Roll for success + success = random.random() < final_success_rate + + if success: + # Calculate payout + base_payout = random.randint(target["min_payout"], target["max_payout"]) + individual_payout = base_payout // len(heist["crew"]) + + embed = discord.Embed( + title="Heist Successful!", + description=f"The crew successfully robbed {target_name}!\n" + f"Each crew member earned ${individual_payout:,}!", + color=discord.Color.green() + ) + + # Distribute payouts and update stats + for member in heist["crew"]: + await bank.deposit_credits(member, individual_payout) + async with self.config.user(member).heist_stats() as stats: + stats["participated"] += 1 + stats["succeeded"] += 1 + stats["total_earned"] += individual_payout + else: + fine = random.randint(1000, 5000) + + embed = discord.Embed( + title="Heist Failed!", + description=f"The heist on {target_name} was a disaster!\n" + f"Each crew member was fined ${fine:,}!", + color=discord.Color.red() + ) + + # Apply fines and update stats + for member in heist["crew"]: + try: + await bank.withdraw_credits(member, fine) + except ValueError: + await bank.set_balance(member, 0) + + async with self.config.user(member).heist_stats() as stats: + stats["participated"] += 1 + stats["failed"] += 1 + + # Update guild heist cooldown + await self.config.guild(ctx.guild).last_heist.set(datetime.now().isoformat()) + + # Cleanup + del self.active_heists[ctx.guild.id] + + # Send result + await ctx.send(embed=embed) + + @heist.command(name="stats") + async def heist_stats(self, ctx, member: discord.Member = None): + """View heist statistics for a user""" + member = member or ctx.author + stats = await self.config.user(member).heist_stats() + + embed = discord.Embed( + title=f"Heist Stats for {member.name}", + color=discord.Color.blue() + ) + + success_rate = (stats["succeeded"] / stats["participated"] * 100) if stats["participated"] > 0 else 0 + + embed.add_field(name="Heists Participated", value=stats["participated"]) + embed.add_field(name="Successful Heists", value=stats["succeeded"]) + embed.add_field(name="Failed Heists", value=stats["failed"]) + embed.add_field(name="Success Rate", value=f"{success_rate:.1f}%") + embed.add_field(name="Total Earnings", value=f"${stats['total_earned']:,}") + + await ctx.send(embed=embed) \ No newline at end of file diff --git a/unbelievaboat/investments.py b/unbelievaboat/investments.py new file mode 100644 index 0000000..66b1eac --- /dev/null +++ b/unbelievaboat/investments.py @@ -0,0 +1,202 @@ +import random +import asyncio +from typing import Optional +import discord +from redbot.core import commands, bank, Config +from redbot.core.utils.chat_formatting import humanize_number +from datetime import datetime, timedelta + +class Investments(commands.Cog): + """Investment commands for the UnbelievaBoat economy""" + + def __init__(self, bot): + self.bot = bot + self.config = Config.get_conf(self, identifier=95932766180344, force_registration=True) + + default_global = { + "stocks": { + "APPLE": {"price": 150.0, "volatility": 0.05}, + "GOOGLE": {"price": 2800.0, "volatility": 0.04}, + "TESLA": {"price": 900.0, "volatility": 0.08}, + "AMAZON": {"price": 3300.0, "volatility": 0.04}, + "MICROSOFT": {"price": 300.0, "volatility": 0.03} + }, + "crypto": { + "BTC": {"price": 45000.0, "volatility": 0.15}, + "ETH": {"price": 3000.0, "volatility": 0.12}, + "DOGE": {"price": 0.20, "volatility": 0.25} + }, + "bonds": { + "government": {"interest": 0.05, "term": 7}, # 5% interest, 7 day term + "corporate": {"interest": 0.08, "term": 14}, # 8% interest, 14 day term + "junk": {"interest": 0.15, "term": 30} # 15% interest, 30 day term + }, + "last_update": None + } + + default_user = { + "stocks": {}, # {"APPLE": {"amount": 10, "avg_price": 150.0}} + "crypto": {}, + "bonds": [], # [{"type": "government", "amount": 1000, "purchase_date": "2023-01-01", "maturity_date": "2023-01-08"}] + "total_invested": 0 + } + + self.config.register_global(**default_global) + self.config.register_user(**default_user) + + # Start the background task for updating prices + self.price_update_task = self.bot.loop.create_task(self.update_prices()) + + def cog_unload(self): + if self.price_update_task: + self.price_update_task.cancel() + + async def update_prices(self): + """Background task to update stock and crypto prices""" + while True: + try: + stocks = await self.config.stocks() + crypto = await self.config.crypto() + + # Update stock prices + for symbol in stocks: + volatility = stocks[symbol]["volatility"] + current_price = stocks[symbol]["price"] + change = random.uniform(-volatility, volatility) + new_price = current_price * (1 + change) + stocks[symbol]["price"] = round(new_price, 2) + + # Update crypto prices + for symbol in crypto: + volatility = crypto[symbol]["volatility"] + current_price = crypto[symbol]["price"] + change = random.uniform(-volatility, volatility) + new_price = current_price * (1 + change) + crypto[symbol]["price"] = round(new_price, 2) + + await self.config.stocks.set(stocks) + await self.config.crypto.set(crypto) + await self.config.last_update.set(datetime.now().isoformat()) + + await asyncio.sleep(300) # Update every 5 minutes + except Exception as e: + print(f"Error in price update task: {e}") + await asyncio.sleep(60) + + @commands.group() + async def invest(self, ctx): + """Investment commands""" + pass + + @invest.command(name="stocks") + async def view_stocks(self, ctx): + """View current stock prices""" + stocks = await self.config.stocks() + + embed = discord.Embed(title="Stock Market", color=discord.Color.blue()) + for symbol, data in stocks.items(): + embed.add_field( + name=symbol, + value=f"Price: ${data['price']:,.2f}\nVolatility: {data['volatility']*100}%", + inline=True + ) + + await ctx.send(embed=embed) + + @invest.command(name="buy") + async def buy_stock(self, ctx, symbol: str, amount: int): + """Buy stocks""" + symbol = symbol.upper() + stocks = await self.config.stocks() + + if symbol not in stocks: + return await ctx.send("Invalid stock symbol!") + + price = stocks[symbol]["price"] + total_cost = price * amount + + if not await bank.can_spend(ctx.author, total_cost): + return await ctx.send("You don't have enough money!") + + # Process the purchase + await bank.withdraw_credits(ctx.author, total_cost) + + async with self.config.user(ctx.author).stocks() as user_stocks: + if symbol not in user_stocks: + user_stocks[symbol] = {"amount": amount, "avg_price": price} + else: + old_amount = user_stocks[symbol]["amount"] + old_avg = user_stocks[symbol]["avg_price"] + new_amount = old_amount + amount + new_avg = ((old_amount * old_avg) + (amount * price)) / new_amount + user_stocks[symbol] = {"amount": new_amount, "avg_price": new_avg} + + await ctx.send(f"Successfully bought {amount} shares of {symbol} at ${price:,.2f} each!") + + @invest.command(name="sell") + async def sell_stock(self, ctx, symbol: str, amount: int): + """Sell stocks""" + symbol = symbol.upper() + stocks = await self.config.stocks() + + if symbol not in stocks: + return await ctx.send("Invalid stock symbol!") + + async with self.config.user(ctx.author).stocks() as user_stocks: + if symbol not in user_stocks or user_stocks[symbol]["amount"] < amount: + return await ctx.send("You don't own enough shares!") + + price = stocks[symbol]["price"] + total_value = price * amount + + # Process the sale + user_stocks[symbol]["amount"] -= amount + if user_stocks[symbol]["amount"] == 0: + del user_stocks[symbol] + + await bank.deposit_credits(ctx.author, total_value) + + profit = total_value - (amount * user_stocks[symbol]["avg_price"]) + + await ctx.send( + f"Successfully sold {amount} shares of {symbol} at ${price:,.2f} each!\n" + f"Total profit: ${profit:,.2f}" + ) + + @invest.command(name="portfolio") + async def view_portfolio(self, ctx): + """View your investment portfolio""" + stocks = await self.config.stocks() + user_stocks = await self.config.user(ctx.author).stocks() + + if not user_stocks: + return await ctx.send("You don't have any investments!") + + embed = discord.Embed( + title=f"{ctx.author.name}'s Portfolio", + color=discord.Color.green() + ) + + total_value = 0 + for symbol, data in user_stocks.items(): + current_price = stocks[symbol]["price"] + amount = data["amount"] + avg_price = data["avg_price"] + + value = current_price * amount + profit = value - (avg_price * amount) + profit_percent = (profit / (avg_price * amount)) * 100 + + total_value += value + + embed.add_field( + name=symbol, + value=f"Shares: {amount}\n" + f"Avg Price: ${avg_price:,.2f}\n" + f"Current Price: ${current_price:,.2f}\n" + f"Profit: ${profit:,.2f} ({profit_percent:,.1f}%)", + inline=False + ) + + embed.set_footer(text=f"Total Portfolio Value: ${total_value:,.2f}") + await ctx.send(embed=embed) \ No newline at end of file diff --git a/unbelievaboat/unbelievaboat.py b/unbelievaboat/unbelievaboat.py index aed7c75..a1df8cb 100644 --- a/unbelievaboat/unbelievaboat.py +++ b/unbelievaboat/unbelievaboat.py @@ -59,7 +59,7 @@ class Unbelievaboat(Wallet, Roulette, SettingsMixin, commands.Cog, metaclass=Com }, "failrates": { "crime": 50, - "rob": 70, + "rob": 60, "slut": 33 # 33% chance to fail }, "fines": {"max": 250, "min": 10},