File size: 20,168 Bytes
5aece18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c77b5ff
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
import gradio as gr
import random
import copy
from typing import List, Tuple, Dict, Any

# 角色屬性基類
class 角色屬性:
    def __init__(self, 職業, 陣營, 血量, 魔力值, 消耗魔量, 一般攻擊, 大絕招, 防禦, 角色等級):
        self.__Profession = 職業
        self.__campaign = 陣營
        self.__hp = 血量
        self.__max_hp = 血量  # 保存最大血量
        self.__mp = 魔力值
        self.__max_mp = 魔力值  # 保存最大魔力
        self.__expend_mp = 消耗魔量
        self.__normal_attack = 一般攻擊
        self.__ultimate_attack = 大絕招
        self.__defense = 防禦
        self.__level = 角色等級

    def getProfession(self):
        return self.__Profession

    def getCampaign(self):
        return self.__campaign

    def getHp(self):
        return self.__hp

    def getMaxHp(self):
        return self.__max_hp

    def setHp(self, hp):
        self.__hp = max(0, hp)

    def getMp(self):
        return self.__mp

    def getMaxMp(self):
        return self.__max_mp

    def setMp(self, mp):
        self.__mp = max(0, mp)

    def getNormalAttack(self):
        return self.__normal_attack

    def getUltimateAttack(self):
        return self.__ultimate_attack

    def getDefense(self):
        return self.__defense

    def getExpendMp(self):
        return self.__expend_mp

    def reset_stats(self):
        """重置角色狀態"""
        self.__hp = self.__max_hp
        self.__mp = self.__max_mp

# 角色類別
class 海靈術士(角色屬性):
    def __init__(self, *args):
        super().__init__(*args)

    def normal_attack(self):
        return self.getNormalAttack(), "使出「水刃擊」!"

    def ultimate_attack(self):
        return self.getUltimateAttack(), "使出「海嘯術」!"

class 烈焰武士(角色屬性):
    def __init__(self, *args):
        super().__init__(*args)

    def normal_attack(self):
        return self.getNormalAttack(), "使出「火焰斬」!"

    def ultimate_attack(self):
        return self.getUltimateAttack(), "使出「烈焰風暴」!"

class 鋼鐵守衛(角色屬性):
    def __init__(self, *args):
        super().__init__(*args)

    def normal_attack(self):
        return self.getNormalAttack(), "使出「重錘打擊」!"

    def ultimate_attack(self):
        return self.getUltimateAttack(), "使出「鋼鐵壁壘」!"

class 森林牧者(角色屬性):
    def __init__(self, *args):
        super().__init__(*args)

    def normal_attack(self):
        return self.getNormalAttack(), "使出「藤蔓鞭擊」!"

    def ultimate_attack(self):
        return self.getUltimateAttack(), "使出「自然恩賜」!"

class 大地咒師(角色屬性):
    def __init__(self, *args):
        super().__init__(*args)

    def normal_attack(self):
        return self.getNormalAttack(), "使出「岩石投擲」!"

    def ultimate_attack(self):
        return self.getUltimateAttack(), "使出「大地震擊」!"

# 屬性相剋設定
campaign_advantage = {
    "水": "火",  # 水剋火
    "火": "木",  # 火剋木
    "木": "土",  # 木剋土
    "土": "金",  # 土剋金
    "金": "水"   # 金剋水
}

def 對戰時屬性相剋比較(攻擊者, 防守者, 攻擊力):
    """計算屬性相剋後的攻擊力"""
    攻擊者陣營 = 攻擊者.getCampaign()
    防守者陣營 = 防守者.getCampaign()
    
    if campaign_advantage[攻擊者陣營] == 防守者陣營:
        return 攻擊力 * 1.2, f"{攻擊者.getProfession()}{防守者.getProfession()} 有屬性克制!攻擊力提升 20%!"
    elif campaign_advantage[防守者陣營] == 攻擊者陣營:
        return 攻擊力 * 0.8, f"{攻擊者.getProfession()}{防守者.getProfession()} 屬性克制!攻擊力降低 20%!"
    else:
        return 攻擊力, ""

# 遊戲狀態管理
class GameState:
    def __init__(self):
        self.reset_game()
        
    def reset_game(self):
        # 初始化所有角色
        self.all_characters = {
            "海靈術士": 海靈術士("海靈術士", "水", 3500, 5000, 1500, 200, 2000, 0.20, 0),
            "烈焰武士": 烈焰武士("烈焰武士", "火", 4000, 3000, 1200, 500, 3000, 0.10, 0),
            "鋼鐵守衛": 鋼鐵守衛("鋼鐵守衛", "金", 5000, 2000, 800, 350, 1800, 0.50, 0),
            "森林牧者": 森林牧者("森林牧者", "木", 3000, 4500, 1000, 250, 1500, 0.15, 0),
            "大地咒師": 大地咒師("大地咒師", "土", 4500, 3500, 1000, 300, 2500, 0.30, 0)
        }
        
        self.player_team = []
        self.computer_team = []
        self.current_player_character = None
        self.current_computer_character = None
        self.battle_log = []
        self.game_phase = "選擇角色"  # 選擇角色, 選擇出戰角色, 戰鬥中, 遊戲結束
        self.selected_count = 0

# 全域遊戲狀態
game_state = GameState()

def get_character_info(character):
    """獲取角色資訊字串"""
    return f"""
    職業: {character.getProfession()}
    陣營: {character.getCampaign()}
    HP: {character.getHp()}/{character.getMaxHp()}
    MP: {character.getMp()}/{character.getMaxMp()}
    普攻: {character.getNormalAttack()}
    絕招: {character.getUltimateAttack()}
    防禦: {character.getDefense()}
    """

def get_team_status():
    """獲取隊伍狀態"""
    player_info = "玩家隊伍:\n"
    for i, char in enumerate(game_state.player_team, 1):
        status = "💀" if char.getHp() <= 0 else "⚔️" if char == game_state.current_player_character else "🛡️"
        player_info += f"{i}. {status} {char.getProfession()} (HP: {char.getHp()}/{char.getMaxHp()}, MP: {char.getMp()}/{char.getMaxMp()})\n"
    
    computer_info = "電腦隊伍:\n"
    for i, char in enumerate(game_state.computer_team, 1):
        status = "💀" if char.getHp() <= 0 else "⚔️" if char == game_state.current_computer_character else "🛡️"
        computer_info += f"{i}. {status} {char.getProfession()} (HP: {char.getHp()}/{char.getMaxHp()}, MP: {char.getMp()}/{char.getMaxMp()})\n"
    
    return player_info + "\n" + computer_info

def select_character(character_name):
    """選擇角色"""
    if game_state.game_phase != "選擇角色":
        return "現在不是選擇角色的時間!", get_team_status(), ""
    
    if game_state.selected_count >= 3:
        return "已經選擇了3個角色!", get_team_status(), ""
    
    if any(char.getProfession() == character_name for char in game_state.player_team):
        return f"已經選擇過{character_name}了!", get_team_status(), ""
    
    # 複製角色避免共用狀態
    selected_char = copy.deepcopy(game_state.all_characters[character_name])
    game_state.player_team.append(selected_char)
    game_state.selected_count += 1
    
    message = f"選擇了{character_name}!({game_state.selected_count}/3)"
    
    # 如果選擇了3個角色,自動進入下一階段
    if game_state.selected_count == 3:
        # 電腦隨機選擇3個角色
        computer_choices = random.sample(list(game_state.all_characters.keys()), 3)
        game_state.computer_team = [copy.deepcopy(game_state.all_characters[name]) for name in computer_choices]
        
        message += f"\n\n電腦選擇了: {', '.join(computer_choices)}"
        message += "\n\n請選擇出戰角色!"
        game_state.game_phase = "選擇出戰角色"
        
        return message, get_team_status(), "選擇出戰角色"
    
    return message, get_team_status(), ""

def select_battle_character(player_choice):
    """選擇出戰角色"""
    if game_state.game_phase != "選擇出戰角色":
        return "現在不是選擇出戰角色的時間!", get_team_status(), "", gr.update(), gr.update()
    
    try:
        choice_index = int(player_choice) - 1
        if choice_index < 0 or choice_index >= len(game_state.player_team):
            return "無效的選擇!", get_team_status(), "", gr.update(), gr.update()
        
        selected_char = game_state.player_team[choice_index]
        if selected_char.getHp() <= 0:
            return "該角色已經陣亡!", get_team_status(), "", gr.update(), gr.update()
        
        game_state.current_player_character = selected_char
        
        # 電腦隨機選擇一個存活的角色
        alive_computer_chars = [char for char in game_state.computer_team if char.getHp() > 0]
        if alive_computer_chars:
            game_state.current_computer_character = random.choice(alive_computer_chars)
        
        game_state.game_phase = "戰鬥中"
        
        battle_message = f"玩家派出 {game_state.current_player_character.getProfession()}!\n"
        battle_message += f"電腦派出 {game_state.current_computer_character.getProfession()}!\n"
        battle_message += "戰鬥開始!請選擇攻擊方式:"
        
        return battle_message, get_team_status(), "", gr.update(visible=True), gr.update(visible=True)
    
    except ValueError:
        return "請輸入有效的數字!", get_team_status(), "", gr.update(), gr.update()

def battle_action(action):
    """戰鬥行動"""
    if game_state.game_phase != "戰鬥中":
        return "現在不是戰鬥時間!", get_team_status(), gr.update(), gr.update()
    
    if not game_state.current_player_character or not game_state.current_computer_character:
        return "戰鬥角色未設定!", get_team_status(), gr.update(), gr.update()
    
    battle_log = []
    
    # 玩家攻擊
    player_char = game_state.current_player_character
    computer_char = game_state.current_computer_character
    
    # 玩家選擇攻擊方式
    if action == "普通攻擊":
        attack_power, attack_desc = player_char.normal_attack()
        battle_log.append(f"玩家{player_char.getProfession()} {attack_desc}")
    else:  # 大絕招
        if player_char.getMp() >= player_char.getExpendMp():
            attack_power, attack_desc = player_char.ultimate_attack()
            player_char.setMp(player_char.getMp() - player_char.getExpendMp())
            battle_log.append(f"玩家{player_char.getProfession()} {attack_desc} (消耗MP: {player_char.getExpendMp()})")
        else:
            attack_power, attack_desc = player_char.normal_attack()
            battle_log.append(f"玩家{player_char.getProfession()} MP不足,改用普通攻擊!{attack_desc}")
    
    # 計算屬性相剋
    final_attack, element_msg = 對戰時屬性相剋比較(player_char, computer_char, attack_power)
    if element_msg:
        battle_log.append(element_msg)
    
    # 計算傷害
    defense_value = computer_char.getDefense() * attack_power
    final_damage = max(0, final_attack - defense_value)
    computer_char.setHp(computer_char.getHp() - final_damage)
    
    battle_log.append(f"造成 {final_attack:.0f} 傷害,被防禦擋下 {defense_value:.0f},實際傷害 {final_damage:.0f}")
    battle_log.append(f"電腦{computer_char.getProfession()} 剩餘HP: {computer_char.getHp()}")
    
    # 檢查電腦角色是否陣亡
    if computer_char.getHp() <= 0:
        battle_log.append(f"電腦{computer_char.getProfession()} 陣亡!")
        game_state.current_computer_character = None
        
        # 檢查電腦是否還有存活角色
        alive_computer_chars = [char for char in game_state.computer_team if char.getHp() > 0]
        if not alive_computer_chars:
            battle_log.append("🎉 恭喜!玩家獲得勝利! 🎉")
            game_state.game_phase = "遊戲結束"
            return "\n".join(battle_log), get_team_status(), gr.update(visible=False), gr.update(visible=False)
        else:
            battle_log.append("請選擇下一個出戰角色!")
            game_state.game_phase = "選擇出戰角色"
            return "\n".join(battle_log), get_team_status(), gr.update(visible=False), gr.update(visible=False)
    
    # 電腦反擊
    battle_log.append(f"\n電腦{computer_char.getProfession()} 反擊!")
    
    # 電腦隨機選擇攻擊方式
    computer_action = random.choice(["普通攻擊", "大絕招"])
    if computer_action == "普通攻擊":
        attack_power, attack_desc = computer_char.normal_attack()
        battle_log.append(f"電腦{computer_char.getProfession()} {attack_desc}")
    else:
        if computer_char.getMp() >= computer_char.getExpendMp():
            attack_power, attack_desc = computer_char.ultimate_attack()
            computer_char.setMp(computer_char.getMp() - computer_char.getExpendMp())
            battle_log.append(f"電腦{computer_char.getProfession()} {attack_desc} (消耗MP: {computer_char.getExpendMp()})")
        else:
            attack_power, attack_desc = computer_char.normal_attack()
            battle_log.append(f"電腦{computer_char.getProfession()} MP不足,改用普通攻擊!{attack_desc}")
    
    # 計算電腦攻擊的屬性相剋
    final_attack, element_msg = 對戰時屬性相剋比較(computer_char, player_char, attack_power)
    if element_msg:
        battle_log.append(element_msg)
    
    # 計算對玩家的傷害
    defense_value = player_char.getDefense() * attack_power
    final_damage = max(0, final_attack - defense_value)
    player_char.setHp(player_char.getHp() - final_damage)
    
    battle_log.append(f"造成 {final_attack:.0f} 傷害,被防禦擋下 {defense_value:.0f},實際傷害 {final_damage:.0f}")
    battle_log.append(f"玩家{player_char.getProfession()} 剩餘HP: {player_char.getHp()}")
    
    # 檢查玩家角色是否陣亡
    if player_char.getHp() <= 0:
        battle_log.append(f"玩家{player_char.getProfession()} 陣亡!")
        game_state.current_player_character = None
        
        # 檢查玩家是否還有存活角色
        alive_player_chars = [char for char in game_state.player_team if char.getHp() > 0]
        if not alive_player_chars:
            battle_log.append("💀 遊戲結束!電腦獲得勝利! 💀")
            game_state.game_phase = "遊戲結束"
            return "\n".join(battle_log), get_team_status(), gr.update(visible=False), gr.update(visible=False)
        else:
            battle_log.append("請選擇下一個出戰角色!")
            game_state.game_phase = "選擇出戰角色"
            return "\n".join(battle_log), get_team_status(), gr.update(visible=False), gr.update(visible=False)
    
    battle_log.append("\n請選擇下一個行動:")
    return "\n".join(battle_log), get_team_status(), gr.update(), gr.update()

def restart_game():
    """重新開始遊戲"""
    game_state.reset_game()
    return "遊戲重新開始!請選擇3個角色組成你的隊伍:", "", "選擇角色", gr.update(visible=False), gr.update(visible=False)

# 創建Gradio界面
def create_interface():
    with gr.Blocks(title="決鬥生死戰", theme=gr.themes.Soft()) as demo:
        gr.HTML("""
        <div style="text-align: center; padding: 20px;">
            <h1 style="color: #2196F3; font-size: 3em; margin-bottom: 10px;">⚔️ 決鬥生死戰 ⚔️</h1>
            <p style="font-size: 1.2em; color: #666;">選擇你的戰士,擊敗入侵的敵軍!</p>
            <p style="color: #888;">💧水剋🔥火 | 🔥火剋🌳木 | 🌳木剋🌍土 | 🌍土剋⚡金 | ⚡金剋💧水</p>
        </div>
        """)
        
        with gr.Row():
            with gr.Column(scale=2):
                battle_output = gr.Textbox(
                    label="戰鬥訊息", 
                    value="歡迎來到決鬥生死戰!請選擇3個角色組成你的隊伍:",
                    lines=15,
                    interactive=False
                )
                
                with gr.Row():
                    normal_attack_btn = gr.Button("🗡️ 普通攻擊", visible=False, variant="secondary")
                    ultimate_attack_btn = gr.Button("💥 大絕招", visible=False, variant="primary")
                
                battle_choice = gr.Textbox(
                    label="選擇出戰角色 (輸入數字 1-3)", 
                    placeholder="輸入數字選擇角色...",
                    visible=False
                )
                
                with gr.Row():
                    submit_choice = gr.Button("確認選擇", visible=False)
                    restart_btn = gr.Button("🔄 重新開始", variant="secondary")
            
            with gr.Column(scale=1):
                team_status = gr.Textbox(
                    label="隊伍狀態", 
                    lines=12,
                    interactive=False
                )
                
                gr.HTML("""
                <div style="padding: 10px; border: 1px solid #ddd; border-radius: 5px; margin: 10px 0;">
                    <h3>🏆 角色介紹</h3>
                    <p><strong>💧海靈術士</strong> - 高魔力水系法師</p>
                    <p><strong>🔥烈焰武士</strong> - 高攻擊火系戰士</p>
                    <p><strong>⚡鋼鐵守衛</strong> - 高血量金系坦克</p>
                    <p><strong>🌳森林牧者</strong> - 平衡型木系治療師</p>
                    <p><strong>🌍大地咒師</strong> - 全能型土系法師</p>
                </div>
                """)
        
        # 角色選擇按鈕
        with gr.Row():
            char_buttons = []
            for char_name in ["海靈術士", "烈焰武士", "鋼鐵守衛", "森林牧者", "大地咒師"]:
                btn = gr.Button(f"選擇 {char_name}", variant="primary")
                char_buttons.append(btn)
        
        # 隱藏狀態
        phase_state = gr.State("選擇角色")
        
        # 事件處理
        for i, btn in enumerate(char_buttons):
            char_name = ["海靈術士", "烈焰武士", "鋼鐵守衛", "森林牧者", "大地咒師"][i]
            btn.click(
                select_character,
                inputs=[gr.State(char_name)],
                outputs=[battle_output, team_status, phase_state]
            ).then(
                lambda phase: (gr.update(visible=phase=="選擇出戰角色"), 
                              gr.update(visible=phase=="選擇出戰角色")),
                inputs=[phase_state],
                outputs=[battle_choice, submit_choice]
            )
        
        submit_choice.click(
            select_battle_character,
            inputs=[battle_choice],
            outputs=[battle_output, team_status, phase_state, normal_attack_btn, ultimate_attack_btn]
        ).then(
            lambda: gr.update(value=""),
            outputs=[battle_choice]
        )
        
        normal_attack_btn.click(
            battle_action,
            inputs=[gr.State("普通攻擊")],
            outputs=[battle_output, team_status, normal_attack_btn, ultimate_attack_btn]
        )
        
        ultimate_attack_btn.click(
            battle_action,
            inputs=[gr.State("大絕招")],
            outputs=[battle_output, team_status, normal_attack_btn, ultimate_attack_btn]
        )
        
        restart_btn.click(
            restart_game,
            outputs=[battle_output, team_status, phase_state, normal_attack_btn, ultimate_attack_btn]
        ).then(
            lambda: (gr.update(visible=False), gr.update(visible=False)),
            outputs=[battle_choice, submit_choice]
        )
        
        # 當階段改變時更新界面可見性
        phase_state.change(
            lambda phase: (
                gr.update(visible=phase=="選擇出戰角色"),
                gr.update(visible=phase=="選擇出戰角色")
            ),
            inputs=[phase_state],
            outputs=[battle_choice, submit_choice]
        )
    
    return demo

# 啟動應用
if __name__ == "__main__":
    demo = create_interface()
    # 針對 Hugging Face Spaces 優化的設定
    demo.launch(
        share=False,  # Hugging Face 會自動提供公開網址
        server_name="0.0.0.0",
        server_port=7860,
        show_error=True,  # 顯示錯誤訊息便於除錯
        quiet=False  # 顯示啟動訊息
    )