608 lines
27 KiB
Python
608 lines
27 KiB
Python
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 logic
|
|
from bot.api_client import api_client
|
|
|
|
keyboard = []
|
|
location = game_world.get_location(current_location_id)
|
|
player = await api_client.get_player(player_id)
|
|
inventory = await api_client.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.api_client import api_client
|
|
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 api_client.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 api_client.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 api_client.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.api_client import api_client
|
|
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 api_client.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.api_client import api_client
|
|
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 api_client.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")])
|
|
|
|
# Profile button (no effect on turn, just info)
|
|
keyboard.append([InlineKeyboardButton("👤 Profile", callback_data="profile")])
|
|
|
|
return InlineKeyboardMarkup(keyboard)
|
|
|
|
async def combat_items_keyboard(player_id: int) -> InlineKeyboardMarkup:
|
|
"""Show consumable items during combat."""
|
|
from bot.api_client import api_client
|
|
keyboard = []
|
|
|
|
inventory_items = await api_client.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)
|
|
|