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.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
This commit is contained in:
parent
f7e19c8ff0
commit
6805dbec1b
4 changed files with 790 additions and 1 deletions
347
unbelievaboat/business.py
Normal file
347
unbelievaboat/business.py
Normal file
|
@ -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)
|
240
unbelievaboat/heist.py
Normal file
240
unbelievaboat/heist.py
Normal file
|
@ -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)
|
202
unbelievaboat/investments.py
Normal file
202
unbelievaboat/investments.py
Normal file
|
@ -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)
|
|
@ -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},
|
||||
|
|
Loading…
Add table
Reference in a new issue