| import gradio as gr |
| import random |
| import time |
|
|
| |
| MONSTERS = [ |
| {"id":"slime","name":"Slime","base_hp":30,"xp":10,"gold":5, "arise":0.7}, |
| {"id":"goblin","name":"Goblin","base_hp":45,"xp":18,"gold":12, "arise":0.5}, |
| {"id":"elf","name":"Elf","base_hp":60,"xp":28,"gold":20, "arise":0.35}, |
| {"id":"dragon","name":"Dragon","base_hp":160,"xp":220,"gold":150, "arise":0.05}, |
| {"id":"king","name":"King","base_hp":220,"xp":330,"gold":240, "arise":0.08}, |
| {"id":"beru","name":"Beru","base_hp":80,"xp":70,"gold":60, "arise":0.25}, |
| {"id":"sjw","name":"Shadow Walker","base_hp":120,"xp":120,"gold":90, "arise":0.12}, |
| ] |
|
|
| SEEDS = [ |
| {"id":"basic","name":"Hạt Thường","cost":10,"grow_time":3,"mut_rate":0.08}, |
| {"id":"spark","name":"Hạt Sét","cost":30,"grow_time":4,"mut_rate":0.18}, |
| {"id":"glow","name":"Hạt Lấp Lánh","cost":60,"grow_time":5,"mut_rate":0.32}, |
| ] |
|
|
| CARDS_POOL = [ |
| {"name":"Flame Edge","desc":"Tăng dam +6","apply": lambda s: s.update({"attack": s["attack"]+6})}, |
| {"name":"Swift Step","desc":"Tăng tốc độ, +1 atk","apply": lambda s: s.update({"attack": s["attack"]+1})}, |
| {"name":"Nature's Guard","desc":"Khi HP<30% = +25% shield","apply": lambda s: s.update({"guard": True})}, |
| {"name":"Mana Bloom","desc":"Tăng mana regen","apply": lambda s: s.update({"mana_regen": s.get("mana_regen",0.0)+0.5})}, |
| ] |
|
|
| |
| def choose_monster_for_floor(floor): |
| pool = [] |
| for m in MONSTERS: |
| weight = 1.0 |
| if m["id"] in ("slime","goblin"): |
| weight = max(1.0, 6 - floor//2) |
| else: |
| weight = 1.0 + floor/6 |
| pool.append((m, weight)) |
| total = sum(w for _,w in pool) |
| r = random.random()*total |
| upto = 0 |
| for m,w in pool: |
| if upto + w >= r: |
| return dict(m) |
| upto += w |
| return dict(MONSTERS[0]) |
|
|
| def fmt_status(state): |
| p = state["player"] |
| return f"Cấp {p['level']} · HP {p['hp']}/{p['hp_max']} · XP {p['xp']}/{p['xp_needed']} · Vàng {p['gold']} · Pet: {p['pet']['name'] if p['pet'] else 'Không'}" |
|
|
| def add_log(state, text): |
| state["log"].append(f"[{time.strftime('%H:%M:%S')}] {text}") |
| if len(state["log"]) > 200: |
| state["log"].pop(0) |
|
|
| def default_state(): |
| return { |
| "player": { |
| "level":1, "xp":0, "hp_max":100, "hp":100, "attack":10, "gold":50, |
| "hp_regen":0.0, "mana":50, "mana_max":50, |
| "xp_needed":100, "skills":[], "pet": None |
| }, |
| "dungeon": {"in_dungeon": False, "dungeon_name": None, "floor": 0, "monster": None}, |
| "farm": {"plots": []}, |
| "shop": {"seeds": SEEDS}, |
| "log": [], |
| "inventory": [], |
| "pending_cards": [] |
| } |
|
|
| |
| def start_dungeon(state, dungeon_name): |
| state["dungeon"]["in_dungeon"] = True |
| state["dungeon"]["dungeon_name"] = dungeon_name |
| state["dungeon"]["floor"] = 1 |
| m = choose_monster_for_floor(1) |
| m["hp"] = m["base_hp"] + state["player"]["level"]*3 |
| state["dungeon"]["monster"] = m |
| add_log(state, f"Vào phó bản {dungeon_name} - Tầng 1. Thử thách: {m['name']}") |
| return fmt_status(state), "\n".join(state["log"][-10:]) |
|
|
| def attack(state): |
| if not state["dungeon"]["in_dungeon"]: |
| add_log(state, "Bạn chưa vào phó bản.") |
| return fmt_status(state), "\n".join(state["log"][-10:]) |
| m = state["dungeon"]["monster"] |
| p = state["player"] |
| pet_bonus = p["pet"]["attack"]//2 if p["pet"] else 0 |
| dmg = random.randint(max(1,p["attack"]-3), p["attack"]+3) + pet_bonus |
| m["hp"] -= dmg |
| add_log(state, f"Tấn công {m['name']} gây {dmg} sát thương. (Pet +{pet_bonus})") |
| if m["hp"] <= 0: |
| xp = m["xp"] + state["dungeon"]["floor"]*2 |
| gold = m["gold"] + state["dungeon"]["floor"] |
| p["xp"] += xp |
| p["gold"] += gold |
| add_log(state, f"{m['name']} bị hạ! +{xp} XP, +{gold} vàng.") |
| if state["dungeon"]["floor"] == 10 and random.random() < m.get("arise", 0.0): |
| pet = {"name": f"Pet {m['name']}", "attack": max(3, m["base_hp"]//10), "tier":"common"} |
| p["pet"] = pet |
| add_log(state, f"✨ {m['name']} đã arise! Trở thành pet: {pet['name']}") |
| if state["dungeon"]["floor"] >= 10: |
| add_log(state, f"Hoàn thành phó bản {state['dungeon']['dungeon_name']}! Về thị trấn.") |
| state["dungeon"] = {"in_dungeon": False, "dungeon_name": None, "floor": 0, "monster": None} |
| p["hp"] = min(p["hp_max"], p["hp"] + 20) |
| else: |
| state["dungeon"]["floor"] += 1 |
| m2 = choose_monster_for_floor(state["dungeon"]["floor"]) |
| m2["hp"] = m2["base_hp"] + p["level"]*3 + state["dungeon"]["floor"]*2 |
| state["dungeon"]["monster"] = m2 |
| add_log(state, f"Tầng {state['dungeon']['floor']}: Quái {m2['name']}") |
| while p["xp"] >= p["xp_needed"]: |
| p["xp"] -= p["xp_needed"] |
| p["level"] += 1 |
| p["hp_max"] += 15 |
| p["attack"] += 2 |
| p["hp"] = p["hp_max"] |
| p["xp_needed"] = int(p["xp_needed"] * 1.4) |
| add_log(state, f"⭐ Lên cấp {p['level']}! Chọn 1 thẻ bài.") |
| return fmt_status(state), "\n".join(state["log"][-12:]) |
| else: |
| mdmg = random.randint(max(1, m["xp"]//6), max(1, m["xp"]//4)) + int(state["dungeon"]["floor"]/2) |
| p["hp"] -= mdmg |
| add_log(state, f"{m['name']} phản công gây {mdmg} sát thương!") |
| if p["hp"] <= 0: |
| add_log(state, "⚠️ Bạn đã tử vong! Mất 10% vàng.") |
| p["hp"] = int(p["hp_max"]*0.6) |
| p["gold"] = max(0, int(p["gold"]*0.9)) |
| state["dungeon"] = {"in_dungeon": False, "dungeon_name": None, "floor": 0, "monster": None} |
| return fmt_status(state), "\n".join(state["log"][-12:]) |
|
|
| def choose_card_flow(state): |
| pick = random.sample(CARDS_POOL, 3) |
| state["pending_cards"] = pick |
| add_log(state, " |
| |