dungeon_game / game.py
broadfield-dev's picture
Update game.py
a70ed2f verified
import random
from flask_socketio import emit
class Game:
def __init__(self):
self.map_size = 50 # Much larger dungeon
self.visible_size = 10 # 10x10 visible area on screen
self.door_pos = [48, 48] # Door to next level at far end - set before generating maze
self.terrain = self.generate_maze()
self.player_pos = [1, 1] # Start near top-left
self.player_health = 10
self.player_attack = 2
self.inventory = []
self.monsters = self.generate_monsters()
self.items = self.generate_items()
self.level = 1
def generate_maze(self):
# Simple recursive backtracking maze generation
terrain = [['w' for _ in range(self.map_size)] for _ in range(self.map_size)]
def carve(x, y):
terrain[y][x] = 'f'
directions = [(0, 2), (2, 0), (-2, 0), (0, -2)] # Right, Down, Left, Up (2 steps for corridors)
random.shuffle(directions)
for dx, dy in directions:
nx, ny = x + dx, y + dy
if (0 <= nx < self.map_size and 0 <= ny < self.map_size and
terrain[ny][nx] == 'w'):
mid_x, mid_y = x + dx // 2, y + dy // 2
terrain[mid_y][mid_x] = 'f' # Carve path
carve(nx, ny)
# Start carving from (1,1) to avoid edges
carve(1, 1)
# Ensure player start and door positions are accessible
terrain[1][1] = 'f'
if hasattr(self, 'door_pos'): # Safety check
terrain[self.door_pos[1]][self.door_pos[0]] = 'd' # 'd' for door
else:
print("Warning: door_pos not set, using default [48, 48]")
terrain[48][48] = 'd' # Fallback if door_pos is not set
return terrain
def generate_monsters(self):
monsters = {}
for _ in range(10): # 10 enemies (e.g., 5 goblins, 5 skeletons)
while True:
x, y = random.randint(2, self.map_size - 3), random.randint(2, self.map_size - 3)
if self.terrain[y][x] == 'f' and (x, y) not in monsters and (x, y) != tuple(self.player_pos):
monster_type = random.choice(['goblin', 'skeleton'])
health = 5 if monster_type == 'goblin' else 6
attack = 1 if monster_type == 'goblin' else 2
monsters[(x, y)] = {'type': monster_type, 'health': health, 'attack': attack}
break
return monsters
def generate_items(self):
items = {}
item_types = ['potion', 'sword']
for _ in range(5): # 5 items (e.g., 3 potions, 2 swords)
while True:
x, y = random.randint(2, self.map_size - 3), random.randint(2, self.map_size - 3)
if self.terrain[y][x] == 'f' and (x, y) not in items and (x, y) not in self.monsters:
items[(x, y)] = random.choice(item_types)
break
return items
def get_visible_area(self):
print("Generating visible area for player at:", self.player_pos) # Debug log
px, py = self.player_pos
half_size = self.visible_size // 2
start_x = max(0, px - half_size)
start_y = max(0, py - half_size)
end_x = min(self.map_size, px + half_size + 1)
end_y = min(self.map_size, py + half_size + 1)
visible_terrain = [['' for _ in range(self.visible_size)] for _ in range(self.visible_size)]
visible_entities = [['' for _ in range(self.visible_size)] for _ in range(self.visible_size)]
for y in range(start_y, end_y):
for x in range(start_x, end_x):
grid_y = y - (py - half_size)
grid_x = x - (px - half_size)
if 0 <= grid_y < self.visible_size and 0 <= grid_x < self.visible_size:
visible_terrain[grid_y][grid_x] = self.terrain[y][x] if self.terrain[y][x] else 'f' # Default to 'f'
if (x, y) == tuple(self.player_pos):
visible_entities[grid_y][grid_x] = 'player'
elif (x, y) in self.monsters:
visible_entities[grid_y][grid_x] = self.monsters[(x, y)]['type']
elif (x, y) in self.items:
visible_entities[grid_y][grid_x] = self.items[(x, y)]
elif (x, y) == tuple(self.door_pos):
visible_entities[grid_y][grid_x] = 'door'
print("Visible terrain:", visible_terrain) # Debug log
print("Visible entities:", visible_entities) # Debug log
return {'terrain': visible_terrain, 'entities': visible_entities}
def get_game_state(self):
visible = self.get_visible_area()
state = {
'terrain': visible['terrain'],
'entities': visible['entities'],
'health': self.player_health,
'inventory': self.inventory,
'attack': self.player_attack,
'level': self.level
}
print("Game state:", state) # Debug log
return state
def is_valid_position(self, x, y):
return (0 <= x < self.map_size and 0 <= y < self.map_size and
self.terrain[y][x] != 'w')
def move_player(self, direction):
dx, dy = {'up': (0, -1), 'down': (0, 1), 'left': (-1, 0), 'right': (1, 0)}[direction]
new_x, new_y = self.player_pos[0] + dx, self.player_pos[1] + dy
if self.is_valid_position(new_x, new_y):
self.player_pos = [new_x, new_y]
pos_tuple = tuple(self.player_pos)
# Check for monster
if pos_tuple in self.monsters:
self.handle_combat(pos_tuple)
# Check for item
elif pos_tuple in self.items:
item = self.items.pop(pos_tuple)
self.inventory.append(item)
if item == 'sword':
self.player_attack += 1
emit('message', 'Picked up a sword! Attack increased.')
else:
emit('message', f'Picked up a {item}!')
# Check for door
elif pos_tuple == tuple(self.door_pos):
self.level += 1
self.reset_level()
emit('message', f'Entered Level {self.level}! New dungeon awaits.')
def reset_level(self):
self.door_pos = [48, 48] # Reset door position
self.terrain = self.generate_maze()
self.player_pos = [1, 1]
self.player_health = 10
self.monsters = self.generate_monsters()
self.items = self.generate_items()
def handle_combat(self, pos):
from flask_socketio import emit
monster = self.monsters[pos]
monster_health = monster['health']
while monster_health > 0 and self.player_health > 0:
monster_health -= self.player_attack
emit('message', f"You attack the {monster['type']} for {self.player_attack} damage.")
if monster_health <= 0:
emit('message', f"You defeated the {monster['type']}!")
del self.monsters[pos]
break
self.player_health -= monster['attack']
emit('message', f"The {monster['type']} attacks you for {monster['attack']} damage.")
if self.player_health <= 0:
emit('message', 'Game over! You were defeated.')
break