Skip to content

Commit f27d602

Browse files
AmbratolmAmbratolm
authored andcommitted
Added new actor rpg stats
1 parent 558c24e commit f27d602

File tree

7 files changed

+274
-21
lines changed

7 files changed

+274
-21
lines changed

bot/cogs/ai_cog.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class AiCog(Cog, description="Integrated generative AI chat bot."):
3434
MAX_FILE_SIZE = 524288 # 512 KB == 0.5 MB
3535
REPLY_DELAY_RANGE = (1, 5) # 1 sec - 5 sec
3636
AUTO_REPLY_DELAY_RANGE = (5, 1800) # 5 sec - 30 min
37-
AUTO_REPLY_CHANCE = 0.2 # 20 %
37+
AUTO_REPLY_CHANCE = 0.1 # 10 %
3838
INITIATIVE_DELAY_RANGE = (1800, 7200) # 30 min - 2 hr
3939

4040
def __init__(self, bot: ActBot):
@@ -131,8 +131,9 @@ async def on_message(self, message: Message):
131131
return
132132

133133
# Ignore mentionless message or attempt auto-reply
134+
message_is_mentionless = self.bot.user not in message.mentions
134135
reply_delay = 0
135-
if self.bot.user not in message.mentions:
136+
if message_is_mentionless:
136137
if random() > self.AUTO_REPLY_CHANCE:
137138
return
138139
else:
@@ -144,8 +145,28 @@ async def on_message(self, message: Message):
144145
)
145146
reply_delay = randint(self.REPLY_DELAY_RANGE[0], self.REPLY_DELAY_RANGE[1])
146147

148+
# Check if message is a reply to someone else
149+
preface = ""
150+
if (
151+
message_is_mentionless
152+
and message.reference
153+
and message.reference.message_id
154+
):
155+
referenced_message = await message.channel.fetch_message(
156+
message.reference.message_id
157+
)
158+
if referenced_message.author != self.bot.user:
159+
preface = (
160+
f"[Context: {message.author.name} was replying to {referenced_message.author.name} "
161+
f"who said: '{referenced_message.content}']"
162+
)
163+
else:
164+
preface = "[Context: Replying to ur previous message] "
165+
147166
# Create prompt
148-
text_prompt, file_prompt = await self.create_prompt(message=message)
167+
text_prompt, file_prompt = await self.create_prompt(
168+
message=message, preface=preface
169+
)
149170

150171
# Prepare delayed reply task
151172
async def respond():

bot/cogs/board_cog.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ async def profile(
2828
await interaction.response.defer()
2929
member = member or interaction.user
3030
embed = EmbedX.info(icon="👤", title=member.display_name)
31+
embed.add_field(name="", value=member.mention, inline=False)
3132
if isinstance(member, Member):
3233
embed.description = " ".join(
3334
[
@@ -42,26 +43,49 @@ async def profile(
4243
actor = self.bot.create_actor(member)
4344
embed.add_field(name="", value="", inline=False)
4445
embed.add_field(
45-
name="Rank", value=f"🏆 **{actor.rank_name}**\n{actor.rank_bar}"
46+
name="Rank", value=f"🏆 **{actor.rank_name}**\n`{actor.rank_bar}`"
4647
)
4748
embed.add_field(
4849
name="Level",
49-
value=f"🏅 **{actor.level}**\n{actor.level_bar}",
50+
value=f"🏅 **{actor.level}**\n`{actor.level_bar}`",
5051
)
5152
embed.add_field(
5253
name="Experience",
53-
value=f"⏫ **{intcomma(actor.xp)}** / {actor.next_level_xp}\n{actor.xp_bar}",
54+
value=f"⏫ **{intcomma(actor.xp)}** / {intcomma(actor.next_level_xp)}\n`{actor.xp_bar}`",
5455
)
5556
embed.add_field(name="", value="", inline=False)
5657
embed.add_field(name="Gold", value=f"💰 **{intcomma(actor.gold)}**")
5758
embed.add_field(name="Items", value=f"🎒 **{intcomma(len(actor.items))}**")
59+
embed.add_field(
60+
name="Equipment",
61+
value=f"🧰 **{intcomma(len(actor.equipment))}**",
62+
)
63+
embed.add_field(name="", value="", inline=False)
64+
embed.add_field(
65+
name="Health",
66+
value=f":heart: **{intcomma(actor.health)}** / {intcomma(actor.base_max_health)}\n`{actor.health_bar}`",
67+
)
68+
embed.add_field(
69+
name="Energy",
70+
value=f"⚡ **{intcomma(actor.energy)}** / {intcomma(actor.max_energy)}\n`{actor.energy_bar}`",
71+
)
72+
embed.add_field(name="", value="", inline=False)
73+
embed.add_field(
74+
name="Attack", value=f":crossed_swords: **{intcomma(actor.attack)}**"
75+
)
76+
embed.add_field(name="Defense", value=f"🛡 **{intcomma(actor.defense)}**")
77+
embed.add_field(name="Speed", value=f"🥾 **{intcomma(actor.speed)}**")
78+
embed.add_field(name="", value="", inline=False)
5879
if isinstance(member, Member):
5980
embed.add_field(
6081
name="Joined",
6182
value=f"⌚ {member.guild.name} **{naturaltime(member.joined_at or 0)}**\n-# ⌚ Discord **{naturaltime(member.created_at)}**",
6283
)
6384
embed.add_field(name="", value="", inline=False)
64-
embed.set_footer(text=f"#️⃣{member.name} \n🆔{member.id}")
85+
embed.set_footer(
86+
text=f"#️⃣{member.name}\n🆔{member.id}",
87+
icon_url=member.display_avatar.url,
88+
)
6589
embed.set_thumbnail(url=member.display_avatar.url)
6690
await interaction.followup.send(embed=embed)
6791

bot/cogs/combat_cog.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from discord import Color, Embed, Guild, Interaction, Member, User, app_commands
2+
from discord.ext.commands import Cog
3+
from humanize import intcomma, naturalsize, naturaltime
4+
from odmantic import query
5+
6+
from bot.main import ActBot
7+
from bot.ui import EmbedX
8+
from db.actor import Actor
9+
10+
11+
def add_preview_notice(embed: Embed):
12+
embed.set_author(name="FEATURE PREVIEW")
13+
embed.add_field(name="", value="", inline=False)
14+
embed.add_field(name="", value="")
15+
embed.add_field(
16+
name=":warning: Important Notice",
17+
value="This feature is not yet functional. "
18+
"It's a work-in-progress and will be released in a future update. "
19+
"Thank you for your patience. 🙏",
20+
)
21+
return embed
22+
23+
24+
# ----------------------------------------------------------------------------------------------------
25+
# * Combat Cog
26+
# ----------------------------------------------------------------------------------------------------
27+
class CombatCog(Cog, description="Allows players to engage in battles."):
28+
def __init__(self, bot: ActBot):
29+
self.bot = bot
30+
31+
@app_commands.guild_only()
32+
@app_commands.command(description="Attack a member")
33+
async def attack(self, interaction: Interaction, member: Member):
34+
command_name = interaction.command.name if interaction.command else "?"
35+
await interaction.response.send_message(
36+
content=f"{member.mention} 😱",
37+
embed=add_preview_notice(
38+
EmbedX.info(
39+
icon=":crossed_swords:",
40+
title="Combat",
41+
description=f"{interaction.user.mention} attacked {member.mention}.",
42+
)
43+
),
44+
)

bot/cogs/farm_cog.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@ def calculate_xp_reward(message: Message):
132132

133133
return randint(1, word_count)
134134

135-
@staticmethod
136-
async def try_award_role(member: Member, role_name: str) -> Role | None:
137-
role = utils.get(member.guild.roles, name=role_name)
138-
if role in member.roles:
139-
return None
140-
elif not role:
141-
role = await member.guild.create_role(name=role_name)
142-
await member.add_roles(role)
143-
return role
135+
# @staticmethod
136+
# async def try_award_role(member: Member, role_name: str) -> Role | None:
137+
# role = utils.get(member.guild.roles, name=role_name)
138+
# if role in member.roles:
139+
# return None
140+
# elif not role:
141+
# role = await member.guild.create_role(name=role_name)
142+
# await member.add_roles(role)
143+
# return role

bot/cogs/item_cog.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from discord import Embed, Interaction, app_commands
2+
from discord.ext.commands import Cog
3+
4+
from bot.main import ActBot
5+
from bot.ui import EmbedX
6+
7+
ALPHA_PREVIEW_NOTICE = {"title": "", "content": "**: "}
8+
9+
10+
def add_preview_notice(embed: Embed):
11+
embed.set_author(name="FEATURE PREVIEW")
12+
embed.add_field(name="", value="", inline=False)
13+
embed.add_field(name="", value="")
14+
embed.add_field(
15+
name=":warning: Important Notice",
16+
value="This feature is not yet functional. "
17+
"It's a work-in-progress and will be released in a future update. "
18+
"Thank you for your patience. 🙏",
19+
)
20+
return embed
21+
22+
23+
# ----------------------------------------------------------------------------------------------------
24+
# * Item Cog
25+
# ----------------------------------------------------------------------------------------------------
26+
class ItemCog(Cog, description="Allows players to use items."):
27+
def __init__(self, bot: ActBot):
28+
self.bot = bot
29+
30+
@app_commands.guild_only()
31+
@app_commands.command(description="View purchasable items")
32+
async def shop(self, interaction: Interaction):
33+
await interaction.response.send_message(
34+
embed=add_preview_notice(
35+
EmbedX.info(
36+
icon="🏬",
37+
title="Shop",
38+
description="\n⚔ Sword\n🛡 Shield\n🥾 Boot\n🍎 Apple\n🍌 Banana\n🍔 Burger\n🔫 Gun\n👕 T-Shirt",
39+
)
40+
)
41+
)
42+
43+
@app_commands.guild_only()
44+
@app_commands.command(description="Purchase an item")
45+
async def buy(self, interaction: Interaction, item: str):
46+
await interaction.response.send_message(
47+
embed=add_preview_notice(
48+
EmbedX.info(
49+
icon="🛒",
50+
title="Purchase",
51+
description=f"{interaction.user.mention} purchased **{item}**.",
52+
)
53+
),
54+
)
55+
56+
@app_commands.guild_only()
57+
@app_commands.command(description="View your item inventory")
58+
async def inventory(self, interaction: Interaction):
59+
await interaction.response.send_message(
60+
embed=add_preview_notice(
61+
EmbedX.info(
62+
icon="🎒",
63+
title="Inventory",
64+
description="🐵 Abducted Monkey **x 3**",
65+
),
66+
)
67+
)
68+
69+
@app_commands.guild_only()
70+
@app_commands.command(description="Equip an equippable item")
71+
async def equip(self, interaction: Interaction, item: str):
72+
command_name = interaction.command.name if interaction.command else "?"
73+
await interaction.response.send_message(
74+
embed=add_preview_notice(
75+
EmbedX.info(
76+
icon="🎒",
77+
title="Item Equipage",
78+
description=f"{interaction.user.mention} equipped **{item}**.",
79+
)
80+
),
81+
)
82+
83+
@app_commands.guild_only()
84+
@app_commands.command(description="Use a consumable item")
85+
async def use(self, interaction: Interaction, item: str):
86+
await interaction.response.send_message(
87+
embed=add_preview_notice(
88+
EmbedX.info(
89+
icon="🎒",
90+
title="Item Consumption",
91+
description=f"{interaction.user.mention} used **{item}**.",
92+
)
93+
),
94+
)

db/Item.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from datetime import datetime
2+
from typing import Any, ClassVar, Literal, Optional
3+
4+
from odmantic import Field, Model
5+
from pydantic import NonNegativeInt
6+
7+
from utils.misc import text_progress_bar
8+
9+
# ----------------------------------------------------------------------------------------------------
10+
# * Item
11+
# ----------------------------------------------------------------------------------------------------
12+
class Item(Model):
13+
model_config = {"collection": "items"}
14+
15+
id: str = Field(primary_field=True)
16+
name: str = ""
17+
description: str = ""
18+
price: NonNegativeInt = 0
19+
type: Literal["equippable", "consumable", "special"]
20+
# rarity: Literal["common", "uncommon", "rare", "epic", "legendary"]
21+
22+
# Effects when used/equipped
23+
max_health_bonus: NonNegativeInt = 0
24+
max_energy_bonus: NonNegativeInt = 0
25+
attack_bonus: NonNegativeInt = 0
26+
defense_bonus: NonNegativeInt = 0
27+
speed_bonus: NonNegativeInt = 0
28+
effects: dict[str, Any] = {}

db/actor.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,33 @@ class Actor(Model):
1818
display_name: str = ""
1919
is_member: bool = True # Still member in the server
2020

21+
# AI
2122
ai_interacted_at: Optional[datetime] = None # Last time actor interacted with AI
2223
ai_chat_history: list[dict[str, Any]] = []
2324

24-
xp: NonNegativeInt = 0
25-
level: NonNegativeInt = 0
26-
rank: NonNegativeInt = 0
25+
# Combat stats
26+
health: NonNegativeInt = 1
27+
base_max_health: NonNegativeInt = 1
28+
energy: NonNegativeInt = 1
29+
base_max_energy: NonNegativeInt = 1
30+
base_attack: NonNegativeInt = 1
31+
base_defense: NonNegativeInt = 1
32+
base_speed: NonNegativeInt = 1
33+
34+
# Gold, Items, & Equipment
2735
gold: NonNegativeInt = 0
2836
items: list[str] = []
37+
equipment: list[str] = [] # Equipped items
38+
MAX_ITEMS: ClassVar[int] = 20
39+
MAX_EQUIPMENT: ClassVar[int] = 3
2940

41+
# Progress
42+
xp: NonNegativeInt = 0
43+
level: NonNegativeInt = 0
44+
rank: NonNegativeInt = 0
3045
LEVEL_BASE_XP: ClassVar[int] = 100
3146
LEVEL_EXPONENT: ClassVar[float] = 2.5
3247
MAX_LEVEL: ClassVar[int] = 99
33-
3448
RANK_BASE_LEVEL: ClassVar[int] = 30
3549
RANK_EXPONENT: ClassVar[float] = 1
3650
RANK_NAMES: ClassVar[list[str]] = [
@@ -50,6 +64,26 @@ class Actor(Model):
5064

5165
# ----------------------------------------------------------------------------------------------------
5266

67+
@property
68+
def max_health(self):
69+
return self.base_max_health + 0
70+
71+
@property
72+
def max_energy(self):
73+
return self.base_max_energy + 0
74+
75+
@property
76+
def attack(self):
77+
return self.base_attack + 0
78+
79+
@property
80+
def defense(self):
81+
return self.base_defense + 0
82+
83+
@property
84+
def speed(self):
85+
return self.base_speed + 0
86+
5387
@property
5488
def rank_name(self) -> str:
5589
"""Get name of current rank."""
@@ -94,13 +128,21 @@ def try_rank_up(self) -> bool:
94128

95129
# ----------------------------------------------------------------------------------------------------
96130

131+
@property
132+
def health_bar(self) -> str:
133+
return text_progress_bar(self.health, self.base_max_health, 6, "▰", "▱")
134+
135+
@property
136+
def energy_bar(self) -> str:
137+
return text_progress_bar(self.energy, self.max_energy, 6, "▰", "▱")
138+
97139
@property
98140
def rank_bar(self) -> str:
99141
return text_progress_bar(self.rank, self.MAX_RANKS, 5, "⭐", "☆")
100142

101143
@property
102144
def level_bar(self) -> str:
103-
return text_progress_bar(self.level, self.next_rank_level, 5, "", "")
145+
return text_progress_bar(self.level, self.next_rank_level, 5, "", "")
104146

105147
@property
106148
def xp_bar(self) -> str:

0 commit comments

Comments
 (0)