"""Explorer Tab — location exploration, NPC encounters, quest tracking, actions."""
import random
import gradio as gr
from core.world import WorldGraph
from core.player import load_player, save_player
from core.model_manager import ModelManager
from ui.i18n import t
def _get_location_choices() -> list:
"""Get list of location names for the dropdown."""
world = WorldGraph()
return [loc.name for loc in world.get_entities_by_type("location")]
def _render_location(location_name: str) -> str:
if not location_name:
return f"""
"""
world = WorldGraph()
player = load_player()
locations = world.get_entities_by_type("location")
location = next((l for l in locations if l.name == location_name), None)
if not location:
return f"{t('exp_not_found')}
"
player.current_location_id = location.id
save_player(player)
all_npcs = world.get_entities_by_type("npc")
npcs_here = [n for n in all_npcs if n.location_id == location.id]
all_quests = world.get_entities_by_type("quest")
quests_here = [q for q in all_quests if q.location_id == location.id and not player.is_quest_completed(q.id)]
neighbors = world.get_neighbors(location.id, "connects_to")
connected = [n.name for n in neighbors if n.name != location_name]
html = f"""
{location.name}
{location.description}
"""
if npcs_here:
html += f'
{t("exp_encounter")}
'
for npc in npcs_here:
dialog = npc.dialog or "..."
html += f"""
"""
if quests_here:
html += f'
{t("exp_quests")}
'
for q in quests_here:
html += f"""
{q.name}
{q.description[:100]}...
"""
if connected:
html += f'
{t("exp_travel")}
'
html += '
'
for loc_name in connected:
html += f"""
{loc_name}
"""
html += "
"
html += "
"
return html
def explore_location(location_name: str) -> tuple:
if not location_name:
return t("exp_select_first"), ""
world = WorldGraph()
player = load_player()
manager = ModelManager()
locations = world.get_entities_by_type("location")
location = next((l for l in locations if l.name == location_name), None)
if not location:
return t("exp_not_exist"), ""
outcomes = [t("exp_outcome_1"), t("exp_outcome_2"), t("exp_outcome_3"), t("exp_outcome_4"), t("exp_outcome_5")]
outcome = outcomes[0] # Default, LLM overrides
prompt = f"你在{location.name}探索。{location.description[:100]}\n\n请描述一个有趣的发现(物品或线索),50字以内。用中文回答。"
gen = manager.generate_fn
discovery = gen(prompt) or outcome
level_msg = player.add_xp(15)
item_found = None
if random.random() > 0.5:
item_names = [t("item_key"), t("item_crystal"), t("item_scroll"), t("item_potion"), t("item_dust"), t("item_shard")]
item_name = random.choice(item_names)
player.add_item(item_name, discovery[:30], "misc")
item_found = item_name
save_player(player)
result = f"{t('exp_discovery')}: {discovery}"
if item_found:
result += f"\n{t('exp_item_found')}: {item_found} (+15 XP)"
if level_msg:
result += f"\n{level_msg}"
return result, _render_location(location_name)
def rest_action(location_name: str) -> tuple:
player = load_player()
player.heal(hp=30, mp=20)
save_player(player)
return f"{t('exp_rest_result')} (HP:{player.hp}/{player.max_hp}, MP:{player.mp}/{player.max_mp})", _render_location(location_name)
def create_explorer_tab() -> tuple:
"""Create the Explorer tab."""
with gr.Row():
location_dropdown = gr.Dropdown(
label=t("exp_select_label"),
choices=_get_location_choices(),
interactive=True,
)
location_display = gr.HTML(
value=f"""
"""
)
with gr.Row():
explore_btn = gr.Button(t("exp_explore"), variant="primary")
rest_btn = gr.Button(t("exp_rest"), variant="secondary")
action_result = gr.HTML("")
# Location change -> render
location_dropdown.change(
fn=_render_location,
inputs=[location_dropdown],
outputs=[location_display],
)
# Explore
explore_btn.click(
fn=explore_location,
inputs=[location_dropdown],
outputs=[action_result, location_display],
)
# Rest
rest_btn.click(
fn=rest_action,
inputs=[location_dropdown],
outputs=[action_result, location_display],
)
return location_dropdown, location_display, action_result