What a mess

This commit is contained in:
Joan
2025-11-07 15:27:13 +01:00
parent 0b79b3ae59
commit 33cc9586c2
130 changed files with 29819 additions and 1175 deletions

View File

@@ -7,7 +7,8 @@ import json
import random
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import ContextTypes
from . import database, keyboards, logic
from . import keyboards, logic
from .api_client import api_client
from .utils import format_stat_bar
from data.world_loader import game_world
from data.items import ITEMS
@@ -19,9 +20,43 @@ logger = logging.getLogger(__name__)
# UTILITY FUNCTIONS
# ============================================================================
async def get_player_status_text(telegram_id: int) -> str:
"""Generate player status text with location and stats."""
player = await database.get_player(telegram_id)
async def check_and_redirect_if_in_combat(query, user_id: int, player: dict) -> bool:
"""
Check if player is in combat and redirect to combat view if so.
Returns True if player is in combat (and was redirected), False otherwise.
"""
combat_data = await api_client.get_combat(user_id)
if combat_data:
from data.npcs import NPCS
npc_def = NPCS.get(combat_data['npc_id'])
message = f"⚔️ You're in combat with {npc_def.emoji} {npc_def.name}!\n"
message += format_stat_bar("Your HP", "❤️", player['hp'], player['max_hp']) + "\n"
message += format_stat_bar("Enemy HP", npc_def.emoji, combat_data['npc_hp'], combat_data['npc_max_hp']) + "\n\n"
message += "🎯 Your turn!" if combat_data['turn'] == 'player' else "⏳ Enemy's turn..."
keyboard = await keyboards.combat_keyboard(user_id)
from .handlers import send_or_edit_with_image
await send_or_edit_with_image(
query,
text=message,
reply_markup=keyboard,
image_path=npc_def.image_url if npc_def else None
)
await query.answer("⚔️ You're in combat! Finish or flee first.", show_alert=True)
return True
return False
async def get_player_status_text(player_id: int) -> str:
"""Generate player status text with location and stats.
Args:
player_id: The unique database ID of the player (not telegram_id)
"""
from .api_client import api_client
player = await api_client.get_player_by_id(player_id)
if not player:
return "Could not find player data."
@@ -29,7 +64,9 @@ async def get_player_status_text(telegram_id: int) -> str:
if not location:
return "Error: Player is in an unknown location."
inventory = await database.get_inventory(telegram_id)
# Get inventory from API
inv_result = await api_client.get_inventory(player_id)
inventory = inv_result.get('inventory', [])
weight, volume = logic.calculate_inventory_load(inventory)
max_weight, max_volume = logic.get_player_capacity(inventory, player)
@@ -61,11 +98,15 @@ async def get_player_status_text(telegram_id: int) -> str:
async def handle_inspect_area(query, user_id: int, player: dict, data: list = None):
"""Handle inspect area action - show NPCs and interactables in current location."""
# Check if player is in combat and redirect if so
if await check_and_redirect_if_in_combat(query, user_id, player):
return
await query.answer()
location_id = player['location_id']
location = game_world.get_location(location_id)
dropped_items = await database.get_dropped_items_in_location(location_id)
wandering_enemies = await database.get_wandering_enemies_in_location(location_id)
dropped_items = await api_client.get_dropped_items_in_location(location_id)
wandering_enemies = await api_client.get_wandering_enemies_in_location(location_id)
keyboard = await keyboards.inspect_keyboard(location_id, dropped_items, wandering_enemies)
image_path = location.image_path if location else None
@@ -85,7 +126,7 @@ async def handle_attack_wandering(query, user_id: int, player: dict, data: list)
await query.answer()
# Get the enemy from database
wandering_enemies = await database.get_wandering_enemies_in_location(player['location_id'])
wandering_enemies = await api_client.get_wandering_enemies_in_location(player['location_id'])
enemy_data = next((e for e in wandering_enemies if e['id'] == enemy_db_id), None)
if not enemy_data:
@@ -93,8 +134,8 @@ async def handle_attack_wandering(query, user_id: int, player: dict, data: list)
# Refresh inspect menu
location_id = player['location_id']
location = game_world.get_location(location_id)
dropped_items = await database.get_dropped_items_in_location(location_id)
wandering_enemies = await database.get_wandering_enemies_in_location(location_id)
dropped_items = await api_client.get_dropped_items_in_location(location_id)
wandering_enemies = await api_client.get_wandering_enemies_in_location(location_id)
keyboard = await keyboards.inspect_keyboard(location_id, dropped_items, wandering_enemies)
image_path = location.image_path if location else None
@@ -110,7 +151,7 @@ async def handle_attack_wandering(query, user_id: int, player: dict, data: list)
npc_id = enemy_data['npc_id']
# Remove enemy from wandering table (they're now in combat)
await database.remove_wandering_enemy(enemy_db_id)
await api_client.remove_wandering_enemy(enemy_db_id)
from data.npcs import NPCS
from bot import combat
@@ -143,6 +184,10 @@ async def handle_attack_wandering(query, user_id: int, player: dict, data: list)
async def handle_inspect_interactable(query, user_id: int, player: dict, data: list):
"""Handle inspecting an interactable object."""
# Check if player is in combat and redirect if so
if await check_and_redirect_if_in_combat(query, user_id, player):
return
location_id, instance_id = data[1], data[2]
location = game_world.get_location(location_id)
@@ -159,7 +204,7 @@ async def handle_inspect_interactable(query, user_id: int, player: dict, data: l
all_on_cooldown = True
for action_id in interactable.actions.keys():
cooldown_key = f"{instance_id}:{action_id}"
if await database.get_cooldown(cooldown_key) == 0:
if await api_client.get_cooldown(cooldown_key) == 0:
all_on_cooldown = False
break
@@ -185,9 +230,13 @@ async def handle_inspect_interactable(query, user_id: int, player: dict, data: l
async def handle_action(query, user_id: int, player: dict, data: list):
"""Handle performing an action on an interactable object."""
# Check if player is in combat and redirect if so
if await check_and_redirect_if_in_combat(query, user_id, player):
return
location_id, instance_id, action_id = data[1], data[2], data[3]
cooldown_key = f"{instance_id}:{action_id}"
cooldown = await database.get_cooldown(cooldown_key)
cooldown = await api_client.get_cooldown(cooldown_key)
if cooldown > 0:
await query.answer("Someone got to it just before you!", show_alert=False)
@@ -207,13 +256,13 @@ async def handle_action(query, user_id: int, player: dict, data: list):
await query.answer()
# Set cooldown
await database.set_cooldown(cooldown_key)
await api_client.set_cooldown(cooldown_key)
# Resolve action
outcome = logic.resolve_action(player, action_obj)
new_stamina = player['stamina'] - action_obj.stamina_cost
new_hp = player['hp'] - outcome.damage_taken
await database.update_player(user_id, {"stamina": new_stamina, "hp": new_hp})
await api_client.update_player(user_id, {"stamina": new_stamina, "hp": new_hp})
# Build detailed action result
result_details = [f"<i>{outcome.text}</i>"]
@@ -232,7 +281,7 @@ async def handle_action(query, user_id: int, player: dict, data: list):
can_add, reason = await logic.can_add_item_to_inventory(user_id, item_id, quantity)
if can_add:
await database.add_item_to_inventory(user_id, item_id, quantity)
await api_client.add_item_to_inventory(user_id, item_id, quantity)
item_def = ITEMS.get(item_id, {})
emoji = item_def.get('emoji', '')
item_name = item_def.get('name', item_id)
@@ -285,6 +334,10 @@ async def handle_main_menu(query, user_id: int, player: dict, data: list = None)
async def handle_move_menu(query, user_id: int, player: dict, data: list = None):
"""Show movement options menu."""
# Check if player is in combat and redirect if so
if await check_and_redirect_if_in_combat(query, user_id, player):
return
await query.answer()
location = game_world.get_location(player['location_id'])
location_image = location.image_path if location else None
@@ -300,31 +353,24 @@ async def handle_move_menu(query, user_id: int, player: dict, data: list = None)
async def handle_move(query, user_id: int, player: dict, data: list):
"""Handle player movement to a new location."""
# Check if player is in combat and redirect if so
if await check_and_redirect_if_in_combat(query, user_id, player):
return
destination_id = data[1]
from_location = game_world.get_location(player['location_id'])
to_location = game_world.get_location(destination_id)
# Use API to move player
from .api_client import api_client
result = await api_client.move_player(player['id'], destination_id)
if not from_location or not to_location:
await query.answer("Invalid location!", show_alert=True)
if not result.get('success'):
await query.answer(result.get('message', 'Cannot move there!'), show_alert=True)
return
# Calculate stamina cost
inventory = await database.get_inventory(user_id)
stamina_cost = logic.calculate_travel_stamina_cost(player, inventory, from_location, to_location)
await query.answer(result.get('message', 'Moving...'), show_alert=False)
if player['stamina'] < stamina_cost:
await query.answer(f"Too tired to travel! Need {stamina_cost} stamina.", show_alert=True)
return
# Deduct stamina and update location
new_stamina = player['stamina'] - stamina_cost
await database.update_player(user_id, {"location_id": destination_id, "stamina": new_stamina})
await query.answer(f"⚡️ -{stamina_cost} stamina", show_alert=False)
# Refresh player data
player = await database.get_player(user_id)
# Refresh player data from API using unique id
player = await api_client.get_player_by_id(user_id)
# Check for random NPC encounter
from data.npcs import NPCS, get_random_npc_for_location, get_location_encounter_rate