Spaces:
Running
Running
import gradio as gr | |
import random | |
import json | |
import base64 | |
import io | |
import os | |
from PIL import Image, ImageDraw, ImageFont | |
from google import genai | |
from google.genai import types | |
API_KEY = os.environ.get("GOOGLE_API_KEY") | |
client = genai.Client(api_key=API_KEY) | |
class AIAdventureGame: | |
def __init__(self): | |
"""初始化遊戲系統""" | |
self.player = None | |
self.backpack = [] | |
self.game_history = [] | |
self.current_scene = "" | |
self.current_riddle = None # 新增:當前謎語 | |
self.current_answer = None # 新增:當前謎語答案 | |
def create_character(self, name): | |
"""創建玩家角色""" | |
if not name.strip(): | |
name = "冒險者" | |
self.player = { | |
"name": name, | |
"HP": 100, | |
"max_HP": 100, | |
"Attack": 15, | |
"Defense": 10, | |
"Gold": 50, | |
"level": 1, | |
"exp": 0 | |
} | |
self.backpack = ["治療藥水"] | |
self.game_history = [f"歡迎,勇敢的{name}!你的冒險即將開始!"] | |
return self.get_status_text(), self.get_history_text() | |
def get_status_text(self): | |
"""獲取玩家狀態文字""" | |
if not self.player: | |
return "請先創建角色" | |
status = f""" | |
【角色狀態】 | |
姓名: {self.player['name']} (等級 {self.player['level']}) | |
生命值: {self.player['HP']}/{self.player['max_HP']} | |
攻擊力: {self.player['Attack']} | |
防禦力: {self.player['Defense']} | |
金幣: {self.player['Gold']} | |
經驗值: {self.player['exp']}/100 | |
【背包物品】 | |
{', '.join(self.backpack) if self.backpack else '空的'} | |
""" | |
return status.strip() | |
def get_history_text(self): | |
"""獲取遊戲歷史文字""" | |
return "\n".join(self.game_history[-10:]) # 只顯示最近 10 筆記錄 | |
def generate_ai_scene(self, scene_type="explore"): | |
"""使用AI生成場景描述""" | |
try: | |
prompts = { | |
"explore": f"你是一個RPG遊戲的敘述者。{self.player['name']}正在進行冒險探索。請生成一個有趣的探索場景描述(50-100字),包含環境、氛圍和可能的發現。場景應該適合等級{self.player['level']}的冒險者。", | |
"battle": f"生成一個戰鬥場景的開場描述(30-50字),{self.player['name']}遭遇了危險。", | |
"treasure": f"生成一個發現寶藏的場景描述(30-50字),{self.player['name']}找到了好東西。", | |
"trap": f"生成一個陷阱場景描述(30-50字),{self.player['name']}遇到了危險。" | |
} | |
response = client.models.generate_content( | |
model="gemini-2.0-flash-001", | |
contents=prompts.get(scene_type, prompts["explore"]), | |
config=types.GenerateContentConfig( | |
safety_settings=[ | |
types.SafetySetting( | |
category=types.HarmCategory.HARM_CATEGORY_HATE_SPEECH, | |
threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, | |
), | |
types.SafetySetting( | |
category=types.HarmCategory.HARM_CATEGORY_HARASSMENT, | |
threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, | |
), | |
types.SafetySetting( | |
category=types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, | |
threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, | |
), | |
] | |
) | |
) | |
return response.text.strip() | |
except Exception as e: | |
# 如果AI生成失敗,使用預設文字 | |
default_scenes = { | |
"explore": "你走在一條蜿蜒的小徑上,陽光透過樹葉灑下斑駁的光影。", | |
"battle": "突然,一個敵人從陰影中跳出來!", | |
"treasure": "你在角落發現了一個閃閃發光的寶箱!", | |
"trap": "糟糕!你踩到了一個隱藏的陷阱!" | |
} | |
return default_scenes.get(scene_type, default_scenes["explore"]) | |
def generate_ai_monster(self): | |
"""使用AI生成怪物""" | |
try: | |
level = self.player['level'] | |
prompt = f"""生成一個適合等級{level}冒險者的RPG怪物,以JSON格式回應: | |
{{ | |
"name": "怪物名稱", | |
"description": "30字以內的描述", | |
"HP": 基礎生命值, | |
"Attack": 攻擊力, | |
"Defense": 防禦力, | |
"weakness": "弱點或特殊說明" | |
}} | |
怪物強度應該與玩家等級匹配。""" | |
response = client.models.generate_content( | |
model="gemini-2.0-flash-001", | |
contents=prompt | |
) | |
monster_data = json.loads(response.text.strip()) | |
# 根據等級調整數值 | |
level_multiplier = 1 + (level - 1) * 0.3 | |
monster_data["HP"] = int(monster_data["HP"] * level_multiplier) | |
monster_data["Attack"] = int(monster_data["Attack"] * level_multiplier) | |
monster_data["Defense"] = int(monster_data["Defense"] * level_multiplier) | |
return monster_data | |
except Exception as e: | |
# 預設怪物 | |
monsters = [ | |
{"name": "哥布林", "description": "綠色的小惡魔", "HP": 40, "Attack": 8, "Defense": 3, "weakness": "害怕光亮"}, | |
{"name": "骷髏戰士", "description": "不死的戰士", "HP": 60, "Attack": 12, "Defense": 8, "weakness": "神聖攻擊有效"}, | |
{"name": "野狼", "description": "兇猛的野獸", "HP": 45, "Attack": 15, "Defense": 5, "weakness": "火焰傷害加倍"} | |
] | |
return random.choice(monsters) | |
def generate_scene_image(self, scene_description): | |
"""使用Gemini 2.0 Flash生成場景插畫""" | |
try: | |
# 根據場景描述構建詳細的圖像生成提示詞 | |
scene_keywords = self.extract_scene_keywords(scene_description) | |
image_prompt = f""" | |
請生成一幅精美的奇幻角色扮演遊戲插畫,場景描述:{scene_description} | |
畫風與規格: | |
- 奇幻冒險遊戲美術風格 | |
- 色彩鮮豔、充滿魔幻氛圍 | |
- 動漫/漫畫啟發的插畫手法 | |
- 專業級數位繪畫品質 | |
- 電影級光影與構圖 | |
- 16:9 橫向畫面比例 | |
- 具備景深與大氣透視效果 | |
場景元素(**必須包含**): | |
{scene_keywords} | |
情緒設定:充滿冒險與魔法感 | |
品質要求:專業遊戲美術插畫 | |
**嚴格禁止出現**: | |
- 任何文字、字母、單詞、字型 | |
- 任何符號、標誌、圖示 | |
- 浮水印、簽名、商標、Logo | |
- 標題、橫幅、介面元素及任何文字相關內容 | |
""" | |
# 使用 Gemini 2.0 Flash 生成圖像 | |
print("🎨 正在生成AI插畫...") | |
response = client.models.generate_content( | |
model="gemini-2.0-flash-preview-image-generation", | |
contents=image_prompt, | |
config=types.GenerateContentConfig( | |
response_modalities=['TEXT', 'IMAGE'] | |
) | |
) | |
# 處理生成的圖像 | |
if self.extract_image_from_response(response): | |
print("✅ AI插畫生成成功!") | |
return self.extract_image_from_response(response) | |
else: | |
print("⚠️ 無法從AI回應中提取圖像,使用備用圖像") | |
return self.create_fallback_image(scene_description) | |
except Exception as e: | |
print(f"🚫 AI圖像生成失敗: {str(e)}") | |
print("📝 使用手繪風格備用圖像") | |
return self.create_fallback_image(scene_description) | |
def extract_scene_keywords(self, scene_description): | |
"""從場景描述中提取關鍵詞來優化圖像生成""" | |
keywords = [] | |
prompt = f"""你是一位一名遊戲美術設計助理。請根據以下的場景描述,提取所有關鍵的視覺與氛圍特徵,讓它們既具體又豐富,符合奇幻冒險遊戲插畫的需求。 | |
- 請至少列出 5~8 個要素,每個要素都要簡潔地點出畫面中可被強化或突出的細節。 | |
- 請用「- 要素: 描述」的格式輸出,不要有其他多餘文字或說明。 | |
場景描述: | |
{scene_description}""" | |
response = client.models.generate_content( | |
model="gemini-2.0-flash-001", | |
contents=prompt | |
) | |
keywords = response.text | |
return keywords | |
def extract_image_from_response(self, response): | |
"""從Gemini回應中提取圖像""" | |
try: | |
# 檢查回應是否包含圖像數據 | |
if hasattr(response, 'parts') and response.parts: | |
for part in response.parts: | |
if hasattr(part, 'inline_data') and part.inline_data: | |
# 解碼base64圖像數據 | |
if hasattr(part.inline_data, 'data'): | |
image_data = part.inline_data.data | |
img = Image.open(io.BytesIO(image_data)) | |
# 調整圖像大小以適合界面 | |
img = img.resize((512, 384), Image.Resampling.LANCZOS) | |
return img | |
# 嘗試其他可能的圖像數據格式 | |
if hasattr(response, 'candidates') and response.candidates: | |
for candidate in response.candidates: | |
if hasattr(candidate, 'content') and candidate.content: | |
for part in candidate.content.parts: | |
if hasattr(part, 'inline_data') and part.inline_data: | |
image_data = part.inline_data.data | |
img = Image.open(io.BytesIO(image_data)) | |
img = img.resize((512, 384), Image.Resampling.LANCZOS) | |
return img | |
return None | |
except Exception as e: | |
print(f"圖像提取錯誤: {str(e)}") | |
return None | |
def create_fallback_image(self, scene_description): | |
"""創建備用場景圖片(當AI圖像生成失敗時使用)""" | |
try: | |
# 創建一個更精美的手繪風格場景圖片 | |
img = Image.new('RGB', (512, 384), color=(135, 206, 250)) # 天空藍背景 | |
draw = ImageDraw.Draw(img) | |
# 根據場景描述選擇不同的繪製風格 | |
if any(word in scene_description for word in ["森林", "樹", "叢林", "樹木"]): | |
# 森林場景 | |
self.draw_forest_scene(draw, img.size) | |
elif any(word in scene_description for word in ["山", "岩石", "峭壁", "高原"]): | |
# 山地場景 | |
self.draw_mountain_scene(draw, img.size) | |
elif any(word in scene_description for word in ["洞穴", "地下", "洞窟", "岩洞"]): | |
# 洞穴場景 | |
self.draw_cave_scene(draw, img.size) | |
elif any(word in scene_description for word in ["城堡", "建築", "廢墟", "遺跡"]): | |
# 建築場景 | |
self.draw_castle_scene(draw, img.size) | |
else: | |
# 默認草原場景 | |
self.draw_default_scene(draw, img.size) | |
# 添加裝飾性文字 | |
try: | |
font = ImageFont.load_default() | |
# 在圖片底部添加半透明背景 | |
draw.rectangle([0, 340, 512, 384], fill=(0, 0, 0, 128)) | |
draw.text((10, 350), f"🎨 冒險場景: {scene_description[:30]}...", | |
fill=(255, 255, 255), font=font) | |
except: | |
pass | |
return img | |
except Exception as e: | |
# 最終備用方案:純色背景 | |
img = Image.new('RGB', (512, 384), color=(100, 149, 237)) | |
draw = ImageDraw.Draw(img) | |
draw.text((10, 10), "場景生成中...", fill=(255, 255, 255)) | |
return img | |
def draw_forest_scene(self, draw, size): | |
"""繪製森林場景""" | |
width, height = size | |
# 繪製多層樹木 | |
tree_positions = [(80, 250), (200, 230), (320, 270), (450, 240)] | |
tree_colors = [(34, 139, 34), (0, 128, 0), (46, 125, 50), (27, 94, 32)] | |
for i, (x, y) in enumerate(tree_positions): | |
color = tree_colors[i % len(tree_colors)] | |
# 樹冠 | |
draw.ellipse([x-30, y-50, x+30, y], fill=color) | |
# 樹幹 | |
draw.rectangle([x-8, y, x+8, y+50], fill=(101, 67, 33)) | |
# 繪製草地 | |
draw.rectangle([0, height-80, width, height], fill=(76, 175, 80)) | |
# 添加小草效果 | |
for i in range(20): | |
x = random.randint(0, width) | |
y = random.randint(height-50, height-10) | |
draw.line([(x, y), (x+2, y-8)], fill=(27, 94, 32), width=2) | |
def draw_mountain_scene(self, draw, size): | |
"""繪製山地場景""" | |
width, height = size | |
# 繪製遠山 | |
draw.polygon([(0, height//2), (width//4, height//4), (width//2, height//3), | |
(3*width//4, height//5), (width, height//3), (width, height), (0, height)], | |
fill=(105, 105, 105)) | |
# 繪製近山 | |
draw.polygon([(0, 2*height//3), (width//3, height//2), (2*width//3, 3*height//5), | |
(width, 2*height//3), (width, height), (0, height)], | |
fill=(69, 90, 100)) | |
# 山頂積雪 | |
draw.polygon([(width//4, height//4), (width//4-20, height//4+20), | |
(width//4+20, height//4+20)], fill=(255, 255, 255)) | |
def draw_cave_scene(self, draw, size): | |
"""繪製洞穴場景""" | |
width, height = size | |
# 黑暗背景 | |
draw.rectangle([0, 0, width, height], fill=(30, 30, 30)) | |
# 洞穴入口 | |
draw.ellipse([width//4, height//3, 3*width//4, 2*height//3], fill=(60, 60, 60)) | |
draw.ellipse([width//3, height//2-20, 2*width//3, height//2+60], fill=(20, 20, 20)) | |
# 岩石 | |
for i in range(5): | |
x = random.randint(0, width-50) | |
y = random.randint(height-100, height-20) | |
draw.ellipse([x, y, x+40, y+30], fill=(80, 80, 80)) | |
def draw_castle_scene(self, draw, size): | |
"""繪製城堡場景""" | |
width, height = size | |
# 城堡主體 | |
castle_x = width//3 | |
castle_width = width//3 | |
castle_height = height//2 | |
draw.rectangle([castle_x, height-castle_height, castle_x+castle_width, height], | |
fill=(139, 69, 19)) | |
# 城堡塔樓 | |
for i in range(3): | |
tower_x = castle_x + i * castle_width//3 | |
draw.rectangle([tower_x, height-castle_height-40, tower_x+30, height-castle_height], | |
fill=(160, 82, 45)) | |
# 塔頂 | |
draw.polygon([(tower_x, height-castle_height-40), | |
(tower_x+15, height-castle_height-60), | |
(tower_x+30, height-castle_height-40)], fill=(139, 0, 0)) | |
def draw_default_scene(self, draw, size): | |
"""繪製默認草原場景""" | |
width, height = size | |
# 草地 | |
draw.rectangle([0, 2*height//3, width, height], fill=(76, 175, 80)) | |
# 遠處的丘陵 | |
draw.ellipse([-50, height//2, width//3, 2*height//3], fill=(46, 125, 50)) | |
draw.ellipse([2*width//3, height//2+20, width+50, 2*height//3+30], fill=(46, 125, 50)) | |
# 雲朵 | |
cloud_positions = [(100, 50), (300, 80), (450, 40)] | |
for x, y in cloud_positions: | |
for i in range(3): | |
draw.ellipse([x+i*15-10, y-5, x+i*15+25, y+15], fill=(255, 255, 255)) | |
def explore(self): | |
"""探索功能""" | |
if not self.player or self.player['HP'] <= 0: | |
return "請先創建角色或角色已死亡", "", None | |
# 生成隨機事件 | |
event = random.choice(["battle", "treasure", "trap", "peaceful"]) | |
scene_description = self.generate_ai_scene("explore") | |
result_text = f"📍 {scene_description}\n\n" | |
if event == "battle": | |
monster = self.generate_ai_monster() | |
battle_scene = self.generate_ai_scene("battle") | |
scene_description += f"\n\n{battle_scene}" | |
result_text += f"⚔️ {battle_scene}\n" | |
result_text += f"你遇到了: {monster['name']}\n" | |
result_text += f"描述: {monster['description']}\n" | |
result_text += f"弱點: {monster['weakness']}\n" | |
result_text += f"生命值: {monster['HP']}, 攻擊: {monster['Attack']}, 防禦: {monster['Defense']}\n\n" | |
# 執行戰鬥 | |
battle_result = self.battle(monster) | |
result_text += f"戰鬥結果: {battle_result}" | |
elif event == "treasure": | |
treasure_scene = self.generate_ai_scene("treasure") | |
scene_description += f"\n\n{treasure_scene}" | |
result_text += f"💰 {treasure_scene}\n" | |
treasure_type = random.choice(["gold", "healing_potion", "equipment"]) # Use healing_potion key | |
if treasure_type == "gold": | |
gold_amount = random.randint(20, 50) | |
self.player["Gold"] += gold_amount | |
result_text += f"你獲得了 {gold_amount} 金幣!" | |
elif treasure_type == "healing_potion": # Use healing_potion key | |
self.backpack.append("治療藥水") # Keep Chinese name in backpack | |
result_text += "你獲得了一瓶治療藥水!" | |
else: | |
if random.choice([True, False]): | |
self.player["Attack"] += random.randint(1, 3) | |
result_text += f"你找到了一把武器!攻擊力增加!" | |
else: | |
self.player["Defense"] += random.randint(1, 2) | |
result_text += f"你找到了一件防具!防禦力增加!" | |
elif event == "trap": | |
trap_scene = self.generate_ai_scene("trap") | |
scene_description += f"\n\n{trap_scene}" | |
result_text += f"💀 {trap_scene}\n" | |
damage = random.randint(10, 25) | |
self.player["HP"] = max(0, self.player["HP"] - damage) | |
result_text += f"你受到了 {damage} 點傷害!" | |
else: # peaceful | |
result_text += "🌸 這裡很寧靜,你恢復了一些體力。" | |
self.player["HP"] = min(self.player["max_HP"], self.player["HP"] + 5) | |
# 獲得經驗值 | |
exp_gain = random.randint(5, 15) | |
self.player["exp"] += exp_gain | |
result_text += f"\n\n✨ 獲得 {exp_gain} 經驗值!" | |
# 檢查升級 | |
if self.player["exp"] >= 100: | |
self.level_up() | |
result_text += f"\n🎉 恭喜升級!現在是等級 {self.player['level']}!" | |
self.game_history.append(result_text) | |
# 生成場景圖片 | |
scene_image = self.generate_scene_image(scene_description) | |
return result_text, self.get_status_text(), scene_image | |
def battle(self, monster): | |
"""戰鬥系統""" | |
monster_hp = monster["HP"] | |
result = "" | |
while self.player["HP"] > 0 and monster_hp > 0: | |
# 玩家攻擊 | |
player_damage = max(self.player["Attack"] - monster["Defense"], 1) | |
monster_hp -= player_damage | |
result += f"你對{monster['name']}造成了{player_damage}點傷害!\n" | |
if monster_hp <= 0: | |
gold_reward = random.randint(15, 30) | |
exp_reward = random.randint(20, 40) | |
self.player["Gold"] += gold_reward | |
self.player["exp"] += exp_reward | |
result += f"🎉 你擊敗了{monster['name']}!\n" | |
result += f"獲得 {gold_reward} 金幣和 {exp_reward} 經驗值!" | |
return result | |
# 怪物攻擊 | |
monster_damage = max(monster["Attack"] - self.player["Defense"], 1) | |
self.player["HP"] -= monster_damage | |
result += f"{monster['name']}對你造成了{monster_damage}點傷害!\n" | |
if self.player["HP"] <= 0: | |
result += "💀 你被打敗了!遊戲結束。" | |
return result | |
# 簡化戰鬥,避免無限循環 | |
if random.random() < 0.3: # 30%機率快速結束戰鬥 | |
if random.choice([True, False]): | |
result += "你找到機會逃脫了!" | |
return result | |
return result | |
def level_up(self): | |
"""升級系統""" | |
self.player["level"] += 1 | |
self.player["exp"] = 0 | |
self.player["max_HP"] += 20 | |
self.player["HP"] = self.player["max_HP"] # 升級時回滿血 | |
self.player["Attack"] += random.randint(2, 5) | |
self.player["Defense"] += random.randint(1, 3) | |
def use_potion(self): | |
"""使用藥水 (僅處理治療藥水)""" | |
if not self.player: | |
return "請先創建角色", "" | |
potion_name_in_backpack = "治療藥水" # 背包中藥水的中文名稱 | |
if potion_name_in_backpack in self.backpack: | |
heal_amount = 40 | |
self.player["HP"] = min(self.player["max_HP"], self.player["HP"] + heal_amount) | |
self.backpack.remove(potion_name_in_backpack) | |
result = f"使用治療藥水,恢復了{heal_amount}點生命值!" | |
self.game_history.append(result) | |
return result, self.get_status_text() | |
else: | |
return "背包中沒有治療藥水!", self.get_status_text() | |
def shop(self, item_type): | |
"""商店系統""" | |
if not self.player: | |
return "請先創建角色", "" | |
shop_items = { | |
"healing_potion": {"name": "治療藥水", "price": 15, "effect": "恢復40點生命值"}, | |
"max_hp_potion": {"name": "生命藥水", "price": 30, "effect": "最大生命值+20"}, # 新增 | |
"bomb": {"name": "炸彈", "price": 60, "effect": "攻擊力+5"}, # 新增 | |
"shield": {"name": "盾牌", "price": 50, "effect": "防禦力+3"} # 新增 | |
} | |
if item_type not in shop_items: | |
return "無效的物品選擇", self.get_status_text() | |
item = shop_items[item_type] | |
if self.player["Gold"] >= item["price"]: | |
self.player["Gold"] -= item["price"] | |
result = f"成功購買{item['name']}!花費{item['price']}金幣。" | |
if item_type == "healing_potion": | |
self.backpack.append("治療藥水") # 背包中存中文名稱 | |
elif item_type == "max_hp_potion": | |
self.player["max_HP"] += 20 | |
self.player["HP"] = self.player["max_HP"] # 購買時回滿血到新的最大值 | |
result += " 最大生命值提升!" | |
elif item_type == "bomb": | |
self.backpack.append("炸彈") | |
self.player["Attack"] += 5 | |
result += " 攻擊力提升!" | |
elif item_type == "shield": | |
self.backpack.append("盾牌") | |
self.player["Defense"] += 3 | |
result += " 防禦力提升!" | |
self.game_history.append(result) | |
return result, self.get_status_text() | |
else: | |
return f"金幣不足!需要{item['price']}金幣。", self.get_status_text() | |
def generate_riddle(self): | |
"""使用AI生成一個謎語及其答案""" | |
if not self.player: | |
# Return Chinese message for game history/result consistency | |
return "請先創建角色" | |
try: | |
prompt = """請生成一個適合國小中年級學生、具有奇幻冒險遊戲風格的簡單謎語,並提供其答案。以JSON格式回應: | |
{{ | |
"riddle": "謎語內容", | |
"answer": "謎語答案" | |
}} | |
謎語應該有趣、簡單易懂,答案是單詞或短語。""" | |
response = client.models.generate_content( | |
model="gemini-2.0-flash-001", | |
contents=prompt | |
) | |
raw_text = response.text.strip() | |
# 尋找 JSON 物件的開始和結束位置 | |
json_start = raw_text.find('{') | |
json_end = raw_text.rfind('}') | |
if json_start != -1 and json_end != -1 and json_end > json_start: | |
# 提取 JSON 字串 | |
json_string = raw_text[json_start : json_end + 1] | |
riddle_data = json.loads(json_string) | |
else: | |
# 如果找不到有效的 JSON,拋出錯誤讓外層捕捉 | |
print(f"🚫 AI 回應未包含有效的 JSON 物件: {raw_text}") | |
raise ValueError("AI 回應未包含有效的 JSON 物件。") | |
self.current_riddle = riddle_data.get("riddle", "AI未能生成謎語。") | |
self.current_answer = riddle_data.get("answer", "").strip().lower() # 答案轉小寫以便不區分大小寫比較 | |
result = f"謎語商人給你出了一個謎語:\n{self.current_riddle}" | |
self.game_history.append(result) | |
return self.current_riddle # 返回謎語文本給UI顯示 | |
except Exception as e: | |
print(f"🚫 AI謎語生成失敗: {str(e)}") | |
self.current_riddle = "AI未能生成謎語。請稍後再試。" | |
self.current_answer = None | |
result = "謎語生成失敗。" | |
self.game_history.append(result) | |
return self.current_riddle # 返回錯誤文本給UI顯示 | |
def solve_riddle(self, user_answer): | |
"""檢查謎語答案並給予獎勵""" | |
if not self.player: | |
return "請先創建角色", "", "" # Return Chinese message for game history/result consistency | |
if self.current_riddle is None or self.current_answer is None: | |
result = "目前沒有謎語可供解答。" | |
self.game_history.append(result) | |
return result, self.get_status_text(), self.get_history_text() | |
result = "" | |
if user_answer.strip().lower() == self.current_answer: | |
gold_reward = random.randint(30, 70) # 猜對謎語的獎勵金幣 | |
self.player["Gold"] += gold_reward | |
result = f"✅ 恭喜你答對了!答案是「{self.current_answer}」。你獲得了 {gold_reward} 金幣!" | |
self.current_riddle = None # 答對後清除當前謎語 | |
self.current_answer = None | |
else: | |
result = f"❌ 答案錯誤。正確答案是「{self.current_answer}」。請再試一次或獲取新的謎語。" | |
# 答錯不清除謎語,可以重試 | |
self.game_history.append(result) | |
return result, self.get_status_text(), self.get_history_text() | |
# 建立遊戲 | |
game = AIAdventureGame() | |
# 定義 Gradio 介面 (Translate UI elements) | |
def create_character_ui(name): | |
"""UI handler for character creation""" | |
status, history = game.create_character(name) | |
# The game history and status text content is in Chinese, as per original code style. | |
# Only the UI labels are English. | |
return status, history, None | |
def explore_ui(): | |
"""UI handler for exploration""" | |
result, status, image = game.explore() | |
history = game.get_history_text() | |
# result, status, history content is in Chinese. | |
return result, status, history, image | |
def use_potion_ui(): | |
"""UI handler for using potion""" | |
result, status = game.use_potion() | |
history = game.get_history_text() | |
# result, status, history content is in Chinese. | |
return result, status, history | |
# UI handlers for shop items (update names and calls) | |
def buy_healing_potion_ui(): | |
"""UI handler for buying healing potion""" | |
result, status = game.shop("healing_potion") | |
history = game.get_history_text() | |
return result, status, history | |
def buy_bomb_ui(): | |
"""UI handler for buying bomb""" | |
result, status = game.shop("bomb") | |
history = game.get_history_text() | |
return result, status, history | |
def buy_shield_ui(): | |
"""UI handler for buying shield""" | |
result, status = game.shop("shield") | |
history = game.get_history_text() | |
return result, status, history | |
def buy_max_hp_potion_ui(): | |
"""UI handler for buying max HP potion""" | |
result, status = game.shop("max_hp_potion") | |
history = game.get_history_text() | |
return result, status, history | |
# UI handlers for riddle | |
def get_riddle_ui(): | |
"""UI handler to get a new riddle""" | |
riddle_text = game.generate_riddle() | |
status = game.get_status_text() | |
history = game.get_history_text() | |
# riddle_text, status, history content is in Chinese. | |
return riddle_text, "", status, history # Output riddle text, clear answer input, update status/history | |
def submit_riddle_ui(user_answer): | |
"""UI handler to submit riddle answer""" | |
result, status, history = game.solve_riddle(user_answer) | |
# result, status, history content is in Chinese. | |
return result, "", status, history # Output result, clear answer input, update status/history | |
# 建立 Gradio 介面 (Translate UI elements and add new ones) | |
with gr.Blocks(title="AI Adventure Game", theme=gr.themes.Soft()) as app: # Translate title | |
gr.Markdown(""" | |
# 🎮 AI Adventure Game | |
Welcome to the AI-powered adventure game! Here, every exploration is a unique experience. | |
**Game Features:** | |
- 🤖 AI-generated dynamic storylines | |
- 🎨 AI-created unique monsters | |
- 🖼️ Automatically generated scene images | |
- ⚔️ Rich combat and leveling system | |
**Instructions:** | |
1. Enter your character name to create your adventurer. | |
2. Click "Start Exploring" to begin your adventure. | |
3. Use the shop to buy equipment and supplies. | |
4. Use potions during combat to restore health. | |
""") # Translate Markdown | |
with gr.Row(): | |
with gr.Column(scale=2): | |
# Character Creation (Translate label and placeholder) | |
gr.Markdown("## 🧙♂️ Character Creation") | |
name_input = gr.Textbox(label="Enter Character Name", placeholder="Enter your adventurer's name") | |
create_btn = gr.Button("Create Character", variant="primary") # Translate button | |
# Game Actions (Translate label and buttons) | |
gr.Markdown("## 🎯 Game Actions") | |
with gr.Row(): | |
explore_btn = gr.Button("🗺️ Start Exploring", variant="primary") | |
potion_btn = gr.Button("🧪 Use Healing Potion", variant="secondary") # Translate button | |
# Shop (Translate label and buttons, add new items) | |
gr.Markdown("## 🛒 Shop") | |
with gr.Row(): | |
buy_healing_potion_btn = gr.Button("Buy Healing Potion (15 Gold)") # Translate button | |
buy_max_hp_potion_btn = gr.Button("Buy Max HP Potion (30 Gold)") # New button | |
with gr.Row(): | |
buy_bomb_btn = gr.Button("Buy Bomb (60 Gold)") # New button | |
buy_shield_btn = gr.Button("Buy Shield (50 Gold)") # New button | |
# Riddle Challenge (New section) | |
gr.Markdown("## 🤔 Riddle Challenge") | |
riddle_text_output = gr.Textbox(label="Riddle", lines=3, interactive=False, placeholder="Click 'Get Riddle' to receive a challenge.") # New textbox | |
riddle_answer_input = gr.Textbox(label="Your Answer", placeholder="Enter your answer here.") # New textbox | |
with gr.Row(): | |
get_riddle_btn = gr.Button("❓ Get Riddle") # New button | |
submit_riddle_btn = gr.Button("✅ Submit Answer", variant="primary") # New button | |
# Scene Image (Translate label) | |
scene_image = gr.Image(label="Adventure Scene", height=300) | |
with gr.Column(scale=1): | |
# Character Status (Translate label) | |
status_text = gr.Textbox( | |
label="📊 Character Status", | |
lines=12, | |
interactive=False, | |
value="Please create a character first." # Translate initial value | |
) | |
# Game Result and History (Translate labels and placeholders) | |
with gr.Row(): | |
with gr.Column(): | |
result_text = gr.Textbox( | |
label="🎮 Game Result", | |
lines=8, | |
interactive=False, | |
placeholder="Game results will appear here..." | |
) | |
with gr.Column(): | |
history_text = gr.Textbox( | |
label="📜 Adventure Log", | |
lines=8, | |
interactive=False, | |
placeholder="Your adventure story will be logged here..." | |
) | |
# Bind events (Update button names and add new bindings) | |
create_btn.click( | |
create_character_ui, | |
inputs=[name_input], | |
outputs=[status_text, history_text, scene_image] | |
) | |
explore_btn.click( | |
explore_ui, | |
outputs=[result_text, status_text, history_text, scene_image] | |
) | |
potion_btn.click( | |
use_potion_ui, | |
outputs=[result_text, status_text, history_text] | |
) | |
# Bind new shop buttons | |
buy_healing_potion_btn.click( # Updated button name | |
buy_healing_potion_ui, | |
outputs=[result_text, status_text, history_text] | |
) | |
buy_bomb_btn.click( # New binding | |
buy_bomb_ui, | |
outputs=[result_text, status_text, history_text] | |
) | |
buy_shield_btn.click( # New binding | |
buy_shield_ui, | |
outputs=[result_text, status_text, history_text] | |
) | |
buy_max_hp_potion_btn.click( # New binding | |
buy_max_hp_potion_ui, | |
outputs=[result_text, status_text, history_text] | |
) | |
# Bind riddle buttons | |
get_riddle_btn.click( # New binding | |
get_riddle_ui, | |
outputs=[riddle_text_output, riddle_answer_input, status_text, history_text] # Output riddle text, clear answer input, update status/history | |
) | |
submit_riddle_btn.click( # New binding | |
submit_riddle_ui, | |
inputs=[riddle_answer_input], | |
outputs=[result_text, riddle_answer_input, status_text, history_text] # Output result, clear answer input, update status/history | |
) | |
app.launch(show_error=True) |