Initial commit: Echoes of the Ashes - Telegram RPG Bot
This commit is contained in:
603
bot/keyboards.py
Normal file
603
bot/keyboards.py
Normal file
@@ -0,0 +1,603 @@
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from data.world_loader import game_world
|
||||
from data.items import ITEMS
|
||||
|
||||
# ... (main_menu_keyboard, move_keyboard are unchanged) ...
|
||||
def main_menu_keyboard() -> InlineKeyboardMarkup:
|
||||
keyboard = [[InlineKeyboardButton("🗺️ Move", callback_data="move_menu"), InlineKeyboardButton("👀 Inspect Area", callback_data="inspect_area")], [InlineKeyboardButton("👤 Profile", callback_data="profile"), InlineKeyboardButton("🎒 Inventory", callback_data="inventory_menu")]]
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
async def move_keyboard(current_location_id: str, player_id: int) -> InlineKeyboardMarkup:
|
||||
"""
|
||||
Create a movement keyboard with stamina costs.
|
||||
Layout:
|
||||
[ North (⚡5) ]
|
||||
[ West (⚡5) ] [ East (⚡5) ]
|
||||
[ South (⚡5) ]
|
||||
[ Other exits (inside, down, etc.) ]
|
||||
[ Back ]
|
||||
"""
|
||||
from bot import database, logic
|
||||
|
||||
keyboard = []
|
||||
location = game_world.get_location(current_location_id)
|
||||
player = await database.get_player(player_id)
|
||||
inventory = await database.get_inventory(player_id)
|
||||
|
||||
if location and player:
|
||||
# Dictionary to hold direction buttons
|
||||
compass_directions = {}
|
||||
other_exits = []
|
||||
|
||||
for direction, destination_id in location.exits.items():
|
||||
destination = game_world.get_location(destination_id)
|
||||
if destination:
|
||||
# Calculate stamina cost for this specific route
|
||||
stamina_cost = logic.calculate_travel_stamina_cost(player, inventory, location, destination)
|
||||
|
||||
# Map direction to emoji and label
|
||||
direction_lower = direction.lower()
|
||||
if direction_lower == "north":
|
||||
emoji = "⬆️"
|
||||
compass_directions["north"] = InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
)
|
||||
elif direction_lower == "south":
|
||||
emoji = "⬇️"
|
||||
compass_directions["south"] = InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
)
|
||||
elif direction_lower == "east":
|
||||
emoji = "➡️"
|
||||
compass_directions["east"] = InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
)
|
||||
elif direction_lower == "west":
|
||||
emoji = "⬅️"
|
||||
compass_directions["west"] = InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
)
|
||||
elif direction_lower == "northeast":
|
||||
emoji = "↗️"
|
||||
compass_directions["northeast"] = InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
)
|
||||
elif direction_lower == "northwest":
|
||||
emoji = "↖️"
|
||||
compass_directions["northwest"] = InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
)
|
||||
elif direction_lower == "southeast":
|
||||
emoji = "↘️"
|
||||
compass_directions["southeast"] = InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
)
|
||||
elif direction_lower == "southwest":
|
||||
emoji = "↙️"
|
||||
compass_directions["southwest"] = InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
)
|
||||
elif direction_lower == "inside":
|
||||
emoji = "🚪"
|
||||
other_exits.append(InlineKeyboardButton(
|
||||
f"{emoji} Enter {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
))
|
||||
elif direction_lower == "outside":
|
||||
emoji = "🚪"
|
||||
other_exits.append(InlineKeyboardButton(
|
||||
f"{emoji} Exit to {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
))
|
||||
elif direction_lower == "down":
|
||||
emoji = "⬇️"
|
||||
other_exits.append(InlineKeyboardButton(
|
||||
f"{emoji} Descend to {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
))
|
||||
elif direction_lower == "up":
|
||||
emoji = "⬆️"
|
||||
other_exits.append(InlineKeyboardButton(
|
||||
f"{emoji} Ascend to {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
))
|
||||
else:
|
||||
# Generic fallback for any other direction
|
||||
emoji = "🔀"
|
||||
other_exits.append(InlineKeyboardButton(
|
||||
f"{emoji} {destination.name} (⚡{stamina_cost})",
|
||||
callback_data=f"move:{destination_id}"
|
||||
))
|
||||
|
||||
# Build compass layout
|
||||
# Row 1: Northwest, North, Northeast
|
||||
top_row = []
|
||||
if "northwest" in compass_directions:
|
||||
top_row.append(compass_directions["northwest"])
|
||||
if "north" in compass_directions:
|
||||
top_row.append(compass_directions["north"])
|
||||
if "northeast" in compass_directions:
|
||||
top_row.append(compass_directions["northeast"])
|
||||
if top_row:
|
||||
keyboard.append(top_row)
|
||||
|
||||
# Row 2: West and/or East
|
||||
middle_row = []
|
||||
if "west" in compass_directions:
|
||||
middle_row.append(compass_directions["west"])
|
||||
if "east" in compass_directions:
|
||||
middle_row.append(compass_directions["east"])
|
||||
if middle_row:
|
||||
keyboard.append(middle_row)
|
||||
|
||||
# Row 3: Southwest, South, Southeast
|
||||
bottom_row = []
|
||||
if "southwest" in compass_directions:
|
||||
bottom_row.append(compass_directions["southwest"])
|
||||
if "south" in compass_directions:
|
||||
bottom_row.append(compass_directions["south"])
|
||||
if "southeast" in compass_directions:
|
||||
bottom_row.append(compass_directions["southeast"])
|
||||
if bottom_row:
|
||||
keyboard.append(bottom_row)
|
||||
|
||||
# Add other exits (inside, outside, up, down, etc.)
|
||||
for exit_button in other_exits:
|
||||
keyboard.append([exit_button])
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back", callback_data="main_menu")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
async def inspect_keyboard(location_id: str, dropped_items: list, wandering_enemies: list = None) -> InlineKeyboardMarkup:
|
||||
from bot import database
|
||||
from data.npcs import NPCS
|
||||
|
||||
keyboard = []
|
||||
location = game_world.get_location(location_id)
|
||||
|
||||
# Show wandering enemies first if present (in pairs, emoji only)
|
||||
if wandering_enemies:
|
||||
row = []
|
||||
for enemy in wandering_enemies:
|
||||
npc_def = NPCS.get(enemy['npc_id'])
|
||||
if npc_def:
|
||||
button = InlineKeyboardButton(
|
||||
f"⚠️ {npc_def.emoji} {npc_def.name}",
|
||||
callback_data=f"attack_wandering:{enemy['id']}"
|
||||
)
|
||||
row.append(button)
|
||||
if len(row) == 2:
|
||||
keyboard.append(row)
|
||||
row = []
|
||||
if row: # Add remaining enemy if odd number
|
||||
keyboard.append(row)
|
||||
if wandering_enemies:
|
||||
keyboard.append([InlineKeyboardButton("--- Environment ---", callback_data="no_op")])
|
||||
|
||||
# Show interactables in pairs when text is short enough
|
||||
if location:
|
||||
row = []
|
||||
for instance_id, interactable in location.interactables.items():
|
||||
label = interactable.name
|
||||
# Check if ANY action is available (not on cooldown)
|
||||
has_available_action = False
|
||||
for action_id in interactable.actions.keys():
|
||||
cooldown_key = f"{instance_id}:{action_id}"
|
||||
if await database.get_cooldown(cooldown_key) == 0:
|
||||
has_available_action = True
|
||||
break
|
||||
if not has_available_action and len(interactable.actions) > 0:
|
||||
label += " ⏳"
|
||||
|
||||
# Include location_id in callback data for efficient lookup
|
||||
button = InlineKeyboardButton(label, callback_data=f"inspect:{location_id}:{instance_id}")
|
||||
|
||||
# If text is short (< 20 chars), try to pair it
|
||||
if len(label) < 20:
|
||||
row.append(button)
|
||||
if len(row) == 2:
|
||||
keyboard.append(row)
|
||||
row = []
|
||||
else:
|
||||
# Long text, add any pending row first, then add this one alone
|
||||
if row:
|
||||
keyboard.append(row)
|
||||
row = []
|
||||
keyboard.append([button])
|
||||
|
||||
# Add remaining button if odd number
|
||||
if row:
|
||||
keyboard.append(row)
|
||||
|
||||
# Show player corpse bags
|
||||
player_corpses = await database.get_player_corpses_in_location(location_id)
|
||||
if player_corpses:
|
||||
keyboard.append([InlineKeyboardButton("--- Fallen survivors ---", callback_data="no_op")])
|
||||
row = []
|
||||
for corpse in player_corpses:
|
||||
button = InlineKeyboardButton(
|
||||
f"🎒 {corpse['player_name']}'s bag",
|
||||
callback_data=f"loot_player_corpse:{corpse['id']}"
|
||||
)
|
||||
row.append(button)
|
||||
if len(row) == 2:
|
||||
keyboard.append(row)
|
||||
row = []
|
||||
if row:
|
||||
keyboard.append(row)
|
||||
|
||||
# Show NPC corpses
|
||||
npc_corpses = await database.get_npc_corpses_in_location(location_id)
|
||||
if npc_corpses:
|
||||
if not player_corpses: # Only add separator if not already added
|
||||
keyboard.append([InlineKeyboardButton("--- Corpses ---", callback_data="no_op")])
|
||||
row = []
|
||||
for corpse in npc_corpses:
|
||||
from data.npcs import NPCS
|
||||
npc_def = NPCS.get(corpse['npc_id'])
|
||||
if npc_def:
|
||||
button = InlineKeyboardButton(
|
||||
f"{npc_def.emoji} {npc_def.name}",
|
||||
callback_data=f"scavenge_npc_corpse:{corpse['id']}"
|
||||
)
|
||||
row.append(button)
|
||||
if len(row) == 2:
|
||||
keyboard.append(row)
|
||||
row = []
|
||||
if row:
|
||||
keyboard.append(row)
|
||||
|
||||
if dropped_items:
|
||||
keyboard.append([InlineKeyboardButton("--- Items on the ground ---", callback_data="no_op")])
|
||||
row = []
|
||||
for item in dropped_items:
|
||||
item_def = ITEMS.get(item['item_id'], {})
|
||||
emoji = item_def.get('emoji', '❔')
|
||||
quantity_text = f" x{item['quantity']}" if item['quantity'] > 1 else ""
|
||||
button = InlineKeyboardButton(
|
||||
f"{emoji} {item_def.get('name', 'Unknown')}{quantity_text}",
|
||||
callback_data=f"pickup_menu:{item['id']}"
|
||||
)
|
||||
row.append(button)
|
||||
if len(row) == 2:
|
||||
keyboard.append(row)
|
||||
row = []
|
||||
if row:
|
||||
keyboard.append(row)
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back", callback_data="main_menu")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
def pickup_options_keyboard(item_id: int, item_name: str, quantity: int) -> InlineKeyboardMarkup:
|
||||
"""Create pickup options keyboard with x1, x5, x10, and All options."""
|
||||
keyboard = []
|
||||
|
||||
if quantity == 1:
|
||||
# Just show a single "Pick" button for single items
|
||||
keyboard.append([InlineKeyboardButton("📦 Pick", callback_data=f"pickup:{item_id}:1")])
|
||||
else:
|
||||
# Build pickup row with available options
|
||||
pickup_row = [InlineKeyboardButton("📦 Pick x1", callback_data=f"pickup:{item_id}:1")]
|
||||
|
||||
if quantity >= 5:
|
||||
pickup_row.append(InlineKeyboardButton("📦 Pick x5", callback_data=f"pickup:{item_id}:5"))
|
||||
if quantity >= 10:
|
||||
pickup_row.append(InlineKeyboardButton("📦 Pick x10", callback_data=f"pickup:{item_id}:10"))
|
||||
|
||||
# Split into rows if more than 2 buttons
|
||||
if len(pickup_row) > 2:
|
||||
keyboard.append(pickup_row[:2])
|
||||
keyboard.append(pickup_row[2:])
|
||||
else:
|
||||
keyboard.append(pickup_row)
|
||||
|
||||
# Add "Pick All" option
|
||||
keyboard.append([InlineKeyboardButton(f"📦 Pick All ({quantity})", callback_data=f"pickup:{item_id}:all")])
|
||||
|
||||
# Back button
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back", callback_data="inspect_area")])
|
||||
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
async def actions_keyboard(location_id: str, instance_id: str) -> InlineKeyboardMarkup:
|
||||
from bot import database
|
||||
keyboard = []
|
||||
|
||||
location = game_world.get_location(location_id)
|
||||
|
||||
if location:
|
||||
interactable = location.get_interactable(instance_id)
|
||||
if interactable:
|
||||
for action_id, action in interactable.actions.items():
|
||||
cooldown_key = f"{instance_id}:{action_id}"
|
||||
cooldown = await database.get_cooldown(cooldown_key)
|
||||
label = action.label
|
||||
# Add stamina cost to the label
|
||||
if action.stamina_cost > 0:
|
||||
label += f" (⚡{action.stamina_cost})"
|
||||
if cooldown > 0:
|
||||
label += " ⏳"
|
||||
keyboard.append([InlineKeyboardButton(label, callback_data=f"action:{location_id}:{instance_id}:{action_id}")])
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back", callback_data=f"inspect_area_menu:{location_id}")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
# ... (inventory_keyboard, inventory_item_actions_keyboard are unchanged) ...
|
||||
def inventory_keyboard(inventory_items: list) -> InlineKeyboardMarkup:
|
||||
keyboard = []
|
||||
if inventory_items:
|
||||
# Categorize and sort items
|
||||
# Group items by item_id and equipped status to handle stacking properly
|
||||
item_groups = {}
|
||||
|
||||
for item in inventory_items:
|
||||
item_def = ITEMS.get(item['item_id'], {})
|
||||
item_type = item_def.get('type', 'resource')
|
||||
item_name = item_def.get('name', 'Unknown')
|
||||
is_equipped = item.get('is_equipped', False)
|
||||
|
||||
# Create a unique key for grouping: item_id + equipped status
|
||||
group_key = (item['item_id'], is_equipped)
|
||||
|
||||
if group_key not in item_groups:
|
||||
item_groups[group_key] = {
|
||||
'name': item_name,
|
||||
'def': item_def,
|
||||
'type': item_type,
|
||||
'is_equipped': is_equipped,
|
||||
'items': []
|
||||
}
|
||||
item_groups[group_key]['items'].append(item)
|
||||
|
||||
# Categorize groups
|
||||
equipped = []
|
||||
consumables = []
|
||||
weapons = []
|
||||
equipment = []
|
||||
resources = []
|
||||
quest_items = []
|
||||
|
||||
for group_key, group_data in item_groups.items():
|
||||
item_name = group_data['name']
|
||||
item_def = group_data['def']
|
||||
item_type = group_data['type']
|
||||
is_equipped = group_data['is_equipped']
|
||||
items_list = group_data['items']
|
||||
|
||||
# Calculate total quantity and weight/volume for this group
|
||||
total_quantity = sum(itm['quantity'] for itm in items_list)
|
||||
weight_per_item = item_def.get('weight', 0)
|
||||
volume_per_item = item_def.get('volume', 0)
|
||||
total_weight = weight_per_item * total_quantity
|
||||
total_volume = volume_per_item * total_quantity
|
||||
|
||||
# Use the first item's ID for the callback (they're all the same item type)
|
||||
first_item_id = items_list[0]['id']
|
||||
|
||||
# Create item data tuple: (name, item_def, first_item_id, quantity, weight, volume, is_equipped)
|
||||
item_tuple = (item_name, item_def, first_item_id, total_quantity, total_weight, total_volume, is_equipped)
|
||||
|
||||
# Only equipped items go to equipped section
|
||||
if is_equipped:
|
||||
equipped.append(item_tuple)
|
||||
elif item_type == 'consumable':
|
||||
consumables.append(item_tuple)
|
||||
elif item_type == 'weapon':
|
||||
weapons.append(item_tuple)
|
||||
elif item_type == 'equipment':
|
||||
equipment.append(item_tuple)
|
||||
elif item_type == 'quest':
|
||||
quest_items.append(item_tuple)
|
||||
else:
|
||||
resources.append(item_tuple)
|
||||
|
||||
# Sort each category alphabetically by name
|
||||
equipped.sort(key=lambda x: x[0])
|
||||
consumables.sort(key=lambda x: x[0])
|
||||
weapons.sort(key=lambda x: x[0])
|
||||
equipment.sort(key=lambda x: x[0])
|
||||
resources.sort(key=lambda x: x[0])
|
||||
quest_items.sort(key=lambda x: x[0])
|
||||
|
||||
# Build keyboard sections
|
||||
def add_section(section_name, items_list):
|
||||
if items_list:
|
||||
keyboard.append([InlineKeyboardButton(f"--- {section_name} ---", callback_data="no_op")])
|
||||
row = []
|
||||
for item_name, item_def, item_id, quantity, weight, volume, is_equipped in items_list:
|
||||
emoji = item_def.get('emoji', '❔')
|
||||
quantity_text = f" x{quantity}" if quantity > 1 else ""
|
||||
equipped_marker = " ✓" if is_equipped else ""
|
||||
# Round to 2 decimals
|
||||
weight_vol_text = f" ({weight:.2f}kg, {volume:.2f}vol)" if quantity > 0 else ""
|
||||
|
||||
button = InlineKeyboardButton(
|
||||
f"{emoji} {item_name}{quantity_text}{equipped_marker}{weight_vol_text}",
|
||||
callback_data=f"inventory_item:{item_id}"
|
||||
)
|
||||
row.append(button)
|
||||
if len(row) == 2:
|
||||
keyboard.append(row)
|
||||
row = []
|
||||
# Add remaining item if odd number
|
||||
if row:
|
||||
keyboard.append(row)
|
||||
|
||||
# Add sections in order
|
||||
add_section("Equipped", equipped)
|
||||
add_section("Consumables", consumables)
|
||||
add_section("Weapons", weapons)
|
||||
add_section("Equipment", equipment)
|
||||
add_section("Resources", resources)
|
||||
add_section("Quest Items", quest_items)
|
||||
|
||||
if not keyboard:
|
||||
keyboard.append([InlineKeyboardButton("--- Inventory is empty ---", callback_data="no_op")])
|
||||
else:
|
||||
keyboard.append([InlineKeyboardButton("--- Inventory is empty ---", callback_data="no_op")])
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back", callback_data="main_menu")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
def inventory_item_actions_keyboard(item_db_id: int, item_def: dict, is_equipped: bool = False, quantity: int = 1) -> InlineKeyboardMarkup:
|
||||
keyboard = []
|
||||
|
||||
# Use button for consumables
|
||||
if item_def.get('type') == 'consumable':
|
||||
keyboard.append([InlineKeyboardButton("➡️ Use Item", callback_data=f"inventory_use:{item_db_id}")])
|
||||
|
||||
# Equip/Unequip button for weapons and equipment
|
||||
if item_def.get('type') in ["weapon", "equipment"]:
|
||||
if is_equipped:
|
||||
keyboard.append([InlineKeyboardButton("❌ Unequip", callback_data=f"inventory_unequip:{item_db_id}")])
|
||||
else:
|
||||
keyboard.append([InlineKeyboardButton("✅ Equip", callback_data=f"inventory_equip:{item_db_id}")])
|
||||
|
||||
# Drop buttons - simplified for single items
|
||||
if quantity == 1:
|
||||
# Just show a single "Drop" button
|
||||
keyboard.append([InlineKeyboardButton("🗑️ Drop", callback_data=f"inventory_drop:{item_db_id}:all")])
|
||||
else:
|
||||
# Show x1, x5, x10 options based on quantity
|
||||
drop_row = [InlineKeyboardButton("🗑️ Drop x1", callback_data=f"inventory_drop:{item_db_id}:1")]
|
||||
if quantity >= 5:
|
||||
drop_row.append(InlineKeyboardButton("🗑️ Drop x5", callback_data=f"inventory_drop:{item_db_id}:5"))
|
||||
if quantity >= 10:
|
||||
drop_row.append(InlineKeyboardButton("🗑️ Drop x10", callback_data=f"inventory_drop:{item_db_id}:10"))
|
||||
|
||||
# Split into rows if more than 2 buttons
|
||||
if len(drop_row) > 2:
|
||||
keyboard.append(drop_row[:2])
|
||||
keyboard.append(drop_row[2:])
|
||||
else:
|
||||
keyboard.append(drop_row)
|
||||
|
||||
# Add "Drop All" option
|
||||
keyboard.append([InlineKeyboardButton(f"🗑️ Drop All ({quantity})", callback_data=f"inventory_drop:{item_db_id}:all")])
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back to Inventory", callback_data="inventory_menu")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
async def combat_keyboard(player_id: int) -> InlineKeyboardMarkup:
|
||||
"""Create combat action keyboard."""
|
||||
from bot import database
|
||||
keyboard = []
|
||||
|
||||
# Attack option
|
||||
keyboard.append([InlineKeyboardButton("⚔️ Attack", callback_data="combat_attack")])
|
||||
|
||||
# Flee option
|
||||
keyboard.append([InlineKeyboardButton("🏃 Try to Flee", callback_data="combat_flee")])
|
||||
|
||||
# Use item option (show consumables)
|
||||
inventory_items = await database.get_inventory(player_id)
|
||||
consumables = [item for item in inventory_items if ITEMS.get(item['item_id'], {}).get('type') == 'consumable']
|
||||
|
||||
if consumables:
|
||||
keyboard.append([InlineKeyboardButton("💊 Use Item", callback_data="combat_use_item_menu")])
|
||||
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
async def combat_items_keyboard(player_id: int) -> InlineKeyboardMarkup:
|
||||
"""Show consumable items during combat."""
|
||||
from bot import database
|
||||
keyboard = []
|
||||
|
||||
inventory_items = await database.get_inventory(player_id)
|
||||
consumables = [item for item in inventory_items if ITEMS.get(item['item_id'], {}).get('type') == 'consumable']
|
||||
|
||||
if consumables:
|
||||
keyboard.append([InlineKeyboardButton("--- Select item to use ---", callback_data="no_op")])
|
||||
for item in consumables:
|
||||
item_def = ITEMS.get(item['item_id'], {})
|
||||
emoji = item_def.get('emoji', '❔')
|
||||
keyboard.append([InlineKeyboardButton(
|
||||
f"{emoji} {item_def.get('name', 'Unknown')} x{item['quantity']}",
|
||||
callback_data=f"combat_use_item:{item['id']}"
|
||||
)])
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back to Combat", callback_data="combat_back")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
def corpse_keyboard(corpse_id: int, corpse_type: str) -> InlineKeyboardMarkup:
|
||||
"""Create keyboard for interacting with corpses."""
|
||||
keyboard = []
|
||||
|
||||
if corpse_type == "player":
|
||||
keyboard.append([InlineKeyboardButton("🎒 Loot Bag", callback_data=f"loot_player_corpse:{corpse_id}")])
|
||||
else: # NPC corpse
|
||||
keyboard.append([InlineKeyboardButton("🔪 Scavenge Corpse", callback_data=f"scavenge_npc_corpse:{corpse_id}")])
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back", callback_data="inspect_area")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
def player_corpse_loot_keyboard(corpse_id: int, items: list) -> InlineKeyboardMarkup:
|
||||
"""Show items in a player corpse bag."""
|
||||
keyboard = []
|
||||
|
||||
if items:
|
||||
keyboard.append([InlineKeyboardButton("--- Take items ---", callback_data="no_op")])
|
||||
for i, item_data in enumerate(items):
|
||||
item_def = ITEMS.get(item_data['item_id'], {})
|
||||
emoji = item_def.get('emoji', '❔')
|
||||
keyboard.append([InlineKeyboardButton(
|
||||
f"{emoji} Take {item_def.get('name', 'Unknown')} x{item_data['quantity']}",
|
||||
callback_data=f"take_corpse_item:{corpse_id}:{i}"
|
||||
)])
|
||||
else:
|
||||
keyboard.append([InlineKeyboardButton("--- Bag is empty ---", callback_data="no_op")])
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back", callback_data="inspect_area")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
def npc_corpse_scavenge_keyboard(corpse_id: int, loot_items: list) -> InlineKeyboardMarkup:
|
||||
"""Show scavenging options for NPC corpse."""
|
||||
keyboard = []
|
||||
|
||||
if loot_items:
|
||||
keyboard.append([InlineKeyboardButton("--- Scavenge for materials ---", callback_data="no_op")])
|
||||
for i, loot_data in enumerate(loot_items):
|
||||
item_def = ITEMS.get(loot_data['item_id'], {})
|
||||
emoji = item_def.get('emoji', '❔')
|
||||
|
||||
label = f"{emoji} {item_def.get('name', 'Unknown')}"
|
||||
if loot_data.get('required_tool'):
|
||||
tool_def = ITEMS.get(loot_data['required_tool'], {})
|
||||
label += f" (needs {tool_def.get('name', 'tool')})"
|
||||
|
||||
keyboard.append([InlineKeyboardButton(
|
||||
label,
|
||||
callback_data=f"scavenge_corpse_item:{corpse_id}:{i}"
|
||||
)])
|
||||
else:
|
||||
keyboard.append([InlineKeyboardButton("--- Nothing left to scavenge ---", callback_data="no_op")])
|
||||
|
||||
keyboard.append([InlineKeyboardButton("⬅️ Back", callback_data="inspect_area")])
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
def spend_points_keyboard() -> InlineKeyboardMarkup:
|
||||
"""Create keyboard for spending stat points."""
|
||||
keyboard = [
|
||||
[
|
||||
InlineKeyboardButton("❤️ Max HP (+10)", callback_data="spend_point:max_hp"),
|
||||
InlineKeyboardButton("⚡ Stamina (+5)", callback_data="spend_point:max_stamina")
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton("💪 Strength (+1)", callback_data="spend_point:strength"),
|
||||
InlineKeyboardButton("🏃 Agility (+1)", callback_data="spend_point:agility")
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton("💚 Endurance (+1)", callback_data="spend_point:endurance"),
|
||||
InlineKeyboardButton("🧠 Intellect (+1)", callback_data="spend_point:intellect")
|
||||
],
|
||||
[InlineKeyboardButton("⬅️ Back to Profile", callback_data="profile")]
|
||||
]
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
Reference in New Issue
Block a user