import gradio as gr
import random
import time
from typing import Dict, List, Tuple
# Game state storage
game_rooms = {}
class Player:
def __init__(self, name: str):
self.name = name
self.hp = 100
self.max_hp = 100
self.attack = 15
self.potions = 3
self.gold = 0
self.x = 0
self.y = 0
self.level = 1
self.exp = 0
class Room:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
self.monster = None
self.treasure = None
self.explored = False
self.generate_content()
def generate_content(self):
rand = random.randint(1,100)
if rand <= 50:
monsters = [
("Goblin", 30, 8, 10, 15),
("Orc", 50, 12, 20, 25),
("Skeleton", 40, 10, 15, 20),
("Dark Mage", 35, 15, 25, 30),
("Dragon Whelp", 60, 18, 30, 40)
]
name, hp, atk, gold, exp = random.choice(monsters)
self.monster = {"name": name, "hp": hp, "max_hp": hp, "attack": atk, "gold": gold, "exp": exp}
elif rand <= 75:
self.treasure = random.randint(10, 40)
elif rand <= 90:
self.treasure = "potion"
elif rand <= 95:
self.treasure = "weapon"
class GameRoom:
def __init__(self, room_id: str):
self.room_id = room_id
self.players: Dict[str, Player] = {}
self.grid: Dict[Tuple[int, int], Room] = {}
self.current_player = ""
self.turn_count = 0
self.battle_active = False
self.current_monster = None
self.log = ["๐ฐ Game dimulai! Gabung sekarang!"]
# Generate dungeon grid (7x7 for more exploration)
for x in range(7):
for y in range(7):
self.grid[(x, y)] = Room(x, y)
# Place exit
exit_x, exit_y = 6, 6
self.grid[(exit_x, exit_y)].treasure = "exit"
# Place special rooms
self.grid[(3, 3)].treasure = "boss"
self.grid[(0, 6)].treasure = "artifact"
def add_player(self, player_name: str) -> str:
if player_name in self.players:
return f"{player_name} sudah ada dalam room!"
if len(self.players) >= 4:
return "Room penuh! Maksimal 4 pemain."
self.players[player_name] = Player(player_name)
self.log.append(f"๐ฎ {player_name} bergabung dalam petualangan!")
if not self.current_player:
self.current_player = player_name
self.log.append(f"๐ {player_name} memulai petualangan!")
return f"๐ {player_name} berhasil bergabung!"
def get_player_status(self, player_name: str) -> str:
if player_name not in self.players:
return "Pemain tidak ditemukan!"
p = self.players[player_name]
# Create HP bar
hp_percent = (p.hp / p.max_hp) * 100
hp_bar = self.create_progress_bar(hp_percent, "โค๏ธ")
exp_bar = self.create_progress_bar(p.exp, "๐")
return f"""
๐น {p.name}
{hp_bar}
โ๏ธ Attack: {p.attack}
๐งช Potions: {p.potions}
๐ฐ Gold: {p.gold}
๐ฏ Level: {p.level}
{exp_bar}
๐ Position: ({p.x}, {p.y})
"""
def create_progress_bar(self, percent, label):
width = min(percent, 100)
color = "#2ecc71" if percent > 70 else "#f39c12" if percent > 30 else "#e74c3c"
return f"""
"""
def move_player(self, player_name: str, direction: str) -> Tuple[bool, str]:
if player_name != self.current_player:
return False, "โ Bukan giliranmu!"
if player_name not in self.players:
return False, "โ Pemain tidak ada!"
if self.battle_active:
return False, "โ Sedang dalam pertarungan!"
p = self.players[player_name]
new_x, new_y = p.x, p.y
if direction == "utara" and p.y > 0:
new_y -= 1
elif direction == "selatan" and p.y < 6:
new_y += 1
elif direction == "barat" and p.x > 0:
new_x -= 1
elif direction == "timur" and p.x < 6:
new_x += 1
else:
return False, "โ Tidak bisa bergerak ke arah itu!"
p.x, p.y = new_x, new_y
room = self.grid[(new_x, new_y)]
if not room.explored:
room.explored = True
self.log.append(f"๐ {p.name} menemukan ruangan ({new_x}, {new_y})")
if room.monster:
self.battle_active = True
self.current_monster = room.monster
return True, self.create_battle_alert(f"โ๏ธ {room.monster['name']} muncul!", f"โค๏ธ HP: {room.monster['hp']} | โ๏ธ Attack: {room.monster['attack']}")
elif room.treasure:
if room.treasure == "potion":
p.potions += 1
self.log.append(f"๐งช {p.name} menemukan ramuan penyembuh!")
return True, self.create_success_alert("๐งช Kamu menemukan ramuan penyembuh!")
elif room.treasure == "weapon":
p.attack += 5
self.log.append(f"โ๏ธ {p.name} menemukan senjata legendaris! Attack +5")
return True, self.create_success_alert("โ๏ธ Kamu menemukan senjata legendaris! Attack +5")
elif room.treasure == "exit":
self.log.append(f"๐ {p.name} menemukan pintu keluar! Kemenangan!")
return True, self.create_victory_alert("๐ Selamat! Kamu menemukan pintu keluar!")
elif room.treasure == "boss":
boss = {"name": "Dragon Lord", "hp": 100, "max_hp": 100, "attack": 25, "gold": 100, "exp": 100}
self.battle_active = True
self.current_monster = boss
return True, self.create_battle_alert("๐ BOSS BESAR! Dragon Lord muncul!", f"โค๏ธ HP: {boss['hp']} | โ๏ธ Attack: {boss['attack']}")
elif room.treasure == "artifact":
p.max_hp += 25
p.hp = p.max_hp
self.log.append(f"๐ {p.name} menemukan artifact! Max HP +25")
return True, self.create_success_alert("๐ Kamu menemukan artifact kuno! Max HP +25")
else:
p.gold += room.treasure
self.log.append(f"๐ฐ {p.name} menemukan {room.treasure} emas!")
return True, self.create_success_alert(f"๐ฐ Kamu menemukan {room.treasure} emas!")
return True, "๐ Bergerak ke ruangan yang sudah dijelajahi."
def create_battle_alert(self, title, content):
return f"""
"""
def create_success_alert(self, message):
return f"""
{message}
"""
def create_victory_alert(self, message):
return f"""
๐ {message} ๐
"""
def battle_action(self, player_name: str, action: str) -> Tuple[bool, str]:
if player_name != self.current_player:
return False, "โ Bukan giliranmu!"
if not self.battle_active:
return False, "โ Tidak ada pertarungan!"
p = self.players[player_name]
m = self.current_monster
if action == "attack":
dmg = random.randint(p.attack-3, p.attack+5)
m["hp"] -= dmg
msg = f"โ๏ธ {p.name} menyerang {m['name']}!
๐ฅ Damage: {dmg}
"
if m["hp"] <= 0:
p.gold += m["gold"]
p.exp += m["exp"]
msg += f"
๐ {m['name']} dikalahkan!
๐ฐ +{m['gold']} emas | ๐ +{m['exp']} EXP
"
# Level up check
if p.exp >= 100:
p.level += 1
p.exp = 0
p.max_hp += 10
p.hp = p.max_hp
p.attack += 3
msg += f"
๐ LEVEL UP! Sekarang Level {p.level}!
"
self.battle_active = False
self.current_monster = None
self.log.append(f"๐ฏ {p.name} mengalahkan {m['name']}!")
else:
mdmg = random.randint(m["attack"]-2, m["attack"]+3)
p.hp -= mdmg
msg += f"
๐ฉธ {m['name']} menyerang balik!
๐ Damage: {mdmg}
"
self.log.append(f"โ๏ธ Pertarungan {p.name} vs {m['name']}!")
if p.hp <= 0:
self.log.append(f"๐ {p.name} tewas dalam pertarungan!")
return True, self.create_battle_alert("๐ Kamu tewas dalam pertempuran!", "Game over!")
msg += "
"
elif action == "potion":
if p.potions > 0:
heal = random.randint(25, 40)
p.hp = min(p.hp + heal, p.max_hp)
p.potions -= 1
msg = f"๐งช {p.name} menggunakan ramuan!
๐ +{heal} HP
"
mdmg = random.randint(m["attack"]-2, m["attack"]+3)
p.hp -= mdmg
msg += f"
๐ฉธ {m['name']} menyerang!
๐ Damage: {mdmg}
"
if p.hp <= 0:
self.log.append(f"๐ {p.name} tewas setelah menggunakan ramuan!")
return True, self.create_battle_alert("๐ Kamu tewas dalam pertempuran!", "Game over!")
msg += "
"
else:
return False, "โ Tidak punya ramuan!"
elif action == "flee":
flee_chance = random.randint(1, 100)
if flee_chance > 50:
self.battle_active = False
self.current_monster = None
msg = self.create_success_alert("๐โโ๏ธ Kamu berhasil melarikan diri!")
self.log.append(f"๐โโ๏ธ {p.name} melarikan diri dari pertarungan!")
else:
mdmg = random.randint(m["attack"]-2, m["attack"]+3)
p.hp -= mdmg
msg = f"โ Gagal melarikan diri!
๐ฉธ {m['name']} menyerang! Damage: {mdmg}
"
if p.hp <= 0:
self.log.append(f"๐ {p.name} tewas saat mencoba melarikan diri!")
return True, self.create_battle_alert("๐ Kamu tewas dalam pertempuran!", "Game over!")
return True, msg
def end_turn(self, player_name: str) -> Tuple[bool, str]:
if player_name != self.current_player:
return False, "โ Bukan giliranmu!"
player_names = list(self.players.keys())
if len(player_names) <= 1:
return False, "โ Butuh minimal 2 pemain!"
current_idx = player_names.index(player_name)
next_idx = (current_idx + 1) % len(player_names)
self.current_player = player_names[next_idx]
self.turn_count += 1
# Heal 5 HP at turn start
p = self.players[self.current_player]
old_hp = p.hp
p.hp = min(p.hp + 5, p.max_hp)
heal_amount = p.hp - old_hp
self.log.append(f"๐ Giliran {self.current_player} (+{heal_amount} HP)")
return True, f"""
โ
Giliran {self.current_player}!
+{heal_amount} HP regenerasi
"""
def create_dungeon_map_html(self) -> str:
html = """
๐บ๏ธ Peta Dungeon
"""
for y in range(7):
for x in range(7):
room = self.grid[(x, y)]
players_here = [p for p in self.players.values() if p.x == x and p.y == y]
room_class = "room "
room_text = "?"
room_title = f"Position: ({x}, {y})"
if players_here:
room_class += "player-room"
room_text = "๐ค"
room_title += f"\nPlayers: {', '.join([p.name for p in players_here])}"
elif room.treasure == "exit":
room_class += "exit-room"
room_text = "๐ช"
room_title += "\nEXIT"
elif room.treasure == "boss":
room_class += "boss-room"
room_text = "๐"
room_title += "\nBOSS ROOM"
elif room.treasure == "artifact":
room_class += "artifact-room"
room_text = "๐"
room_title += "\nARTIFACT"
elif room.monster and not room.explored:
room_class += "monster-room"
room_text = "๐น"
room_title += f"\nMonster: {room.monster['name']}"
elif room.treasure and not room.explored and room.treasure not in ["exit", "boss", "artifact"]:
if room.treasure == "potion":
room_class += "potion-room"
room_text = "๐งช"
room_title += "\nPotion"
elif room.treasure == "weapon":
room_class += "weapon-room"
room_text = "โ๏ธ"
room_title += "\nWeapon"
else:
room_class += "treasure-room"
room_text = "๐ฐ"
room_title += f"\nGold: {room.treasure}"
elif room.explored:
room_class += "explored-room"
room_text = "โ"
room_title += "\nExplored"
else:
room_class += "unexplored-room"
room_text = "?"
room_title += "\nUnexplored"
html += f"""
{room_text}
"""
html += """
๐ค = Pemain
๐ช = Exit
๐ = Boss
๐น = Monster
๐ฐ = Treasure
๐งช = Potion
"""
return html
def get_all_players_html(self) -> str:
if not self.players:
return "Belum ada pemain
"
html = """
๐ฅ Tim Petualang
"""
for name, p in self.players.items():
current = "๐" if name == self.current_player else "๐ค"
hp_percent = (p.hp / p.max_hp) * 100
hp_color = "#2ecc71" if hp_percent > 70 else "#f39c12" if hp_percent > 30 else "#e74c3c"
html += f"""
{current} {name}
Lvl {p.level}
โค๏ธ {p.hp}/{p.max_hp} | โ๏ธ {p.attack} | ๐ฐ {p.gold}
"""
html += "
"
return html
def get_logs_html(self) -> str:
logs = self.log[-8:] if self.log else ["Tidak ada aktivitas"]
html = """
๐ Catatan Petualangan
"""
for log in logs:
html += f"""
{log}
"""
html += "
"
return html
# Gradio interface dengan tema yang lebih keren
def create_interface():
with gr.Blocks(
title="Dungeon Crawler RPG",
theme=gr.themes.Soft(primary_hue="red", secondary_hue="slate"),
css="""
.dungeon-title {
text-align: center;
background: linear-gradient(45deg, #8B4513, #D2691E);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: bold;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.game-container {
border: 2px solid #8B4513;
border-radius: 10px;
padding: 15px;
background: #f5f5dc;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
"""
) as demo:
gr.Markdown("""
๐ฐ Dungeon Crawler RPG
Petualangan Multiplayer - Jelajahi dungeon, kalahkan monster, dan temukan harta karun!
""")
with gr.Row():
with gr.Column(scale=1):
with gr.Group():
gr.Markdown("### ๐ฎ Masuk Game")
room_id = gr.Textbox(
label="๐ Room ID",
placeholder="Masukkan Room ID (contoh: dragon_lair)",
info="Buat room baru atau gabung yang sudah ada"
)
player_name = gr.Textbox(
label="๐ค Nama Petualang",
placeholder="Nama pahlawan kamu",
info="Gunakan nama yang keren!"
)
join_btn = gr.Button("๐ช Masuk Dungeon", variant="primary", size="lg")
with gr.Column(scale=2):
with gr.Group():
gr.Markdown("### ๐ Status Petualang")
status_box = gr.HTML(label="Status", value="Bergabunglah untuk memulai...
")
with gr.Row():
with gr.Column(scale=2):
with gr.Group():
gr.Markdown("### ๐บ๏ธ Peta Dungeon")
map_box = gr.HTML(label="Peta")
with gr.Column(scale=1):
with gr.Group():
gr.Markdown("### ๐ฅ Tim Petualang")
players_box = gr.HTML(label="Pemain")
with gr.Group():
gr.Markdown("### ๐ Kontrol Game")
turn_info = gr.HTML(label="Giliran", value="Menunggu pemain...
")
with gr.Row():
refresh_btn = gr.Button("๐ Refresh", variant="secondary")
end_turn_btn = gr.Button("โญ๏ธ Akhiri Giliran", variant="primary")
with gr.Row():
with gr.Column(scale=1):
with gr.Group():
gr.Markdown("### ๐ฏ Aksi Petualangan")
with gr.Row():
move_choice = gr.Radio(
choices=["โฌ๏ธ Utara", "โฌ๏ธ Selatan", "โฌ
๏ธ Barat", "โก๏ธ Timur"],
label="๐ถ Arah Gerakan",
value="โฌ๏ธ Utara"
)
move_btn = gr.Button("๐ฏ Jelajahi", variant="primary", size="lg")
with gr.Column(scale=1):
with gr.Group():
gr.Markdown("### โ๏ธ Aksi Pertempuran")
battle_choice = gr.Radio(
choices=["โ๏ธ Serang", "๐งช Gunakan Ramuan", "๐โโ๏ธ Kabur"],
label="Pilih Strategi",
value="โ๏ธ Serang"
)
battle_btn = gr.Button("โก Eksekusi", variant="primary", size="lg")
with gr.Row():
with gr.Group():
gr.Markdown("### ๐ Catatan Petualangan")
log_box = gr.HTML(label="Activity Log")
# Event handlers
def join_room(room_id_str, name_str):
if not room_id_str or not name_str:
return (
"โ Room ID dan nama harus diisi!",
"Bergabunglah untuk memulai...
",
"Peta akan muncul di sini...
",
"Belum ada pemain
",
"Menunggu pemain...
",
"Log akan muncul di sini...
"
)
if room_id_str not in game_rooms:
game_rooms[room_id_str] = GameRoom(room_id_str)
room = game_rooms[room_id_str]
result = room.add_player(name_str)
status = room.get_player_status(name_str)
turn = f"๐ Giliran: {room.current_player}
"
map_html = room.create_dungeon_map_html()
players = room.get_all_players_html()
logs = room.get_logs_html()
return result, status, map_html, players, turn, logs
def handle_move(room_id_str, name_str, direction):
direction = direction.replace("โฌ๏ธ ", "").replace("โฌ๏ธ ", "").replace("โฌ
๏ธ ", "").replace("โก๏ธ ", "").lower()
if not room_id_str or not name_str:
return "โ Room ID dan nama harus diisi!", "", "", ""
if room_id_str not in game_rooms:
return "โ Room tidak ditemukan!", "", "", ""
room = game_rooms[room_id_str]
success, msg = room.move_player(name_str, direction)
status = room.get_player_status(name_str)
map_html = room.create_dungeon_map_html()
logs = room.get_logs_html()
return msg, status, map_html, logs
def handle_battle(room_id_str, name_str, action):
action_map = {
"โ๏ธ Serang": "attack",
"๐งช Gunakan Ramuan": "potion",
"๐โโ๏ธ Kabur": "flee"
}
action = action_map.get(action, "attack")
if not room_id_str or not name_str:
return "โ Room ID dan nama harus diisi!", "", "", ""
if room_id_str not in game_rooms:
return "โ Room tidak ditemukan!", "", "", ""
room = game_rooms[room_id_str]
success, msg = room.battle_action(name_str, action)
status = room.get_player_status(name_str)
map_html = room.create_dungeon_map_html()
logs = room.get_logs_html()
return msg, status, map_html, logs
def handle_end_turn(room_id_str, name_str):
if not room_id_str or not name_str:
return "โ Room ID dan nama harus diisi!", "", "", "", ""
if room_id_str not in game_rooms:
return "โ Room tidak ditemukan!", "", "", "", ""
room = game_rooms[room_id_str]
success, msg = room.end_turn(name_str)
turn = f"๐ Giliran: {room.current_player}
"
map_html = room.create_dungeon_map_html()
players = room.get_all_players_html()
logs = room.get_logs_html()
return msg, turn, map_html, players, logs
def refresh_all(room_id_str, name_str):
if not room_id_str or not name_str:
return "", "", "", ""
if room_id_str not in game_rooms:
return "", "", "", ""
room = game_rooms[room_id_str]
status = room.get_player_status(name_str)
turn = f"๐ Giliran: {room.current_player}
"
map_html = room.create_dungeon_map_html()
logs = room.get_logs_html()
return status, turn, map_html, logs
# Event handlers
join_btn.click(
fn=join_room,
inputs=[room_id, player_name],
outputs=[status_box, status_box, map_box, players_box, turn_info, log_box]
)
move_btn.click(
fn=handle_move,
inputs=[room_id, player_name, move_choice],
outputs=[status_box, status_box, map_box, log_box]
)
battle_btn.click(
fn=handle_battle,
inputs=[room_id, player_name, battle_choice],
outputs=[status_box, status_box, map_box, log_box]
)
end_turn_btn.click(
fn=handle_end_turn,
inputs=[room_id, player_name],
outputs=[status_box, turn_info, map_box, players_box, log_box]
)
refresh_btn.click(
fn=refresh_all,
inputs=[room_id, player_name],
outputs=[status_box, turn_info, map_box, log_box]
)
return demo
# Create and launch the interface
if __name__ == "__main__":
demo = create_interface()
demo.launch(share=True)