Merge pull request 'bfs' (#19) from bfs into master

Reviewed-on: s464965/WMICraft#19
This commit is contained in:
Angelika Iskra 2022-04-11 13:49:11 +02:00
commit 05fcfc76f3
31 changed files with 546 additions and 318 deletions

120
algorithms/bfs.py Normal file
View File

@ -0,0 +1,120 @@
from __future__ import annotations
from typing import List
from common.constants import ACTION, Direction, ROWS, COLUMNS
class State:
def __init__(self, row, column, direction):
self.row = row
self.column = column
self.direction = direction
class Node:
def __init__(self, state, parent=None, action=None):
self.state = state
self.parent = parent
self.action = action
def goal_test(goal_list, state: State):
if (state.row, state.column) in goal_list:
return True
return False
def get_successors(state: State, map):
successors = list()
state_left = State(state.row, state.column, state.direction.left())
successors.append((ACTION.get("rotate_left"), state_left))
state_right = State(state.row, state.column, state.direction.right())
successors.append((ACTION.get("rotate_right"), state_right))
target = go(state.row, state.column, state.direction)
if is_valid_move(map, target[0], target[1]):
state_go = State(target[0], target[1], state.direction)
successors.append((ACTION.get("go"), state_go))
return successors
def graphsearch(initial_state: State, map, goal_list, fringe: List[Node] = None, explored: List[Node] = None):
# fringe and explored initialization
if fringe is None:
fringe = list()
if explored is None:
explored = list()
explored_states = set()
fringe_states = set()
# root Node
fringe.append(Node(initial_state))
fringe_states.add((initial_state.row, initial_state.column, initial_state.direction))
while True:
# fringe empty -> solution not found
if not any(fringe):
print("Brak rozwiazania")
return []
# get first element from fringe
element = fringe.pop(0)
fringe_states.remove((element.state.row, element.state.column, element.state.direction))
# if solution was found, prepare and return actions sequence
if goal_test(goal_list, element.state):
actions_sequence = [element.action]
parent = element.parent
while parent is not None:
# root's action will be None, don't add it
if parent.action is not None:
actions_sequence.append(parent.action)
parent = parent.parent
actions_sequence.reverse()
return actions_sequence
# add current node to explored (prevents infinite cycles)
explored.append(element)
explored_states.add((element.state.row, element.state.column, element.state.direction))
# loop through every possible next action
for successor in get_successors(element.state, map):
# make sure not to fall into a cycle
successor_state = (successor[1].row, successor[1].column, successor[1].direction)
if successor_state not in fringe_states and successor_state not in explored_states:
# create new Node and add it at the end of fringe
new_node = Node(state=successor[1],
parent=element,
action=successor[0])
fringe.append(new_node)
fringe_states.add((new_node.state.row, new_node.state.column, new_node.state.direction))
# TEMPORARY METHOD
def go(row, column, direction):
target = tuple()
if direction == Direction.RIGHT:
target = row, column + 1
elif direction == Direction.LEFT:
target = row, column - 1
elif direction == Direction.UP:
target = row - 1, column
elif direction == Direction.DOWN:
target = row + 1, column
return target
def is_valid_move(map, target_row, target_column):
if 0 <= target_row < ROWS and 0 <= target_column < COLUMNS and map[target_row][target_column] == ' ':
return True
return False

View File

@ -1,35 +1,63 @@
from enum import Enum
GAME_TITLE = 'WMICraft' GAME_TITLE = 'WMICraft'
WINDOW_HEIGHT = 800 WINDOW_HEIGHT = 800
WINDOW_WIDTH = 1360 WINDOW_WIDTH = 1360
FPS_COUNT = 60
TURN_INTERVAL = 1000
GRID_CELL_PADDING = 5 GRID_CELL_PADDING = 5
GRID_CELL_WIDTH = 36 GRID_CELL_SIZE = 36
GRID_CELL_HEIGHT = 36
ROWS = 19 ROWS = 19
COLUMNS = 24 COLUMNS = 24
BORDER_WIDTH = 10 BORDER_WIDTH = 10
BORDER_RADIUS = 5 BORDER_RADIUS = 5
FPS_COUNT = 60
KNIGHTS_SPAWN_WIDTH = 4 KNIGHTS_SPAWN_WIDTH = 4
KNIGHTS_SPAWN_HEIGHT = 7 KNIGHTS_SPAWN_HEIGHT = 7
LEFT_KNIGHTS_SPAWN_FIRST_ROW = 6 LEFT_KNIGHTS_SPAWN_FIRST_ROW = 6
LEFT_KNIGHTS_SPAWN_FIRST_COL = 0 LEFT_KNIGHTS_SPAWN_FIRST_COL = 0
RIGHT_KNIGHTS_SPAWN_FIRST_ROW = 6 RIGHT_KNIGHTS_SPAWN_FIRST_ROW = 6
RIGHT_KNIGHTS_SPAWN_FIRST_COL = 20 RIGHT_KNIGHTS_SPAWN_FIRST_COL = 20
NBR_OF_WATER = 10
NBR_OF_TREES = 16
CASTLE_SPAWN_WIDTH = 6 CASTLE_SPAWN_WIDTH = 6
CASTLE_SPAWN_HEIGHT = 5 CASTLE_SPAWN_HEIGHT = 5
CASTLE_SPAWN_FIRST_ROW = 7 CASTLE_SPAWN_FIRST_ROW = 7
CASTLE_SPAWN_FIRST_COL = 9 CASTLE_SPAWN_FIRST_COL = 9
NBR_OF_WATER = 16
NBR_OF_TREES = 20
NBR_OF_MONSTERS = 2
TILES = [ TILES = [
'grass1.png', 'grass1.png',
'grass2.png', 'grass2.png',
'grass3.png', 'grass3.png',
# 'grass4.png', 'grass4.png',
# 'grass5.png',
# 'grass6.png',
'sand.png', 'sand.png',
'water.png', 'water.png',
'grass_with_tree.jpg', 'grass_with_tree.jpg',
] ]
class Direction(Enum):
UP = 0
RIGHT = 1
DOWN = 2
LEFT = 3
def right(self):
v = (self.value + 1) % 4
return Direction(v)
def left(self):
v = (self.value - 1) % 4
return Direction(v)
ACTION = {
"rotate_left": -1,
"rotate_right": 1,
"go": 0,
}

View File

@ -1,4 +1,5 @@
import pygame import pygame
from common.constants import GRID_CELL_PADDING, GRID_CELL_SIZE, COLUMNS, ROWS
def draw_text(text, color, surface, x, y, text_size=30, is_bold=False): def draw_text(text, color, surface, x, y, text_size=30, is_bold=False):
@ -10,3 +11,37 @@ def draw_text(text, color, surface, x, y, text_size=30, is_bold=False):
textrect = textobj.get_rect() textrect = textobj.get_rect()
textrect.topleft = (x, y) textrect.topleft = (x, y)
surface.blit(textobj, textrect) surface.blit(textobj, textrect)
def print_numbers():
display_surface = pygame.display.get_surface()
font = pygame.font.SysFont('Arial', 16)
for row_index in range(ROWS):
for col_index in range(COLUMNS):
x = (GRID_CELL_PADDING + GRID_CELL_SIZE) * col_index + GRID_CELL_PADDING + 7
y = (GRID_CELL_PADDING + GRID_CELL_SIZE) * row_index + GRID_CELL_PADDING + 16
display_surface.blit(font.render(f'[{col_index}, {row_index}]', True, (255, 0, 0)), (x, y))
pygame.display.update()
# parse array index to screen x or y coordinate
def parse_cord(cord):
return (GRID_CELL_PADDING + GRID_CELL_SIZE) * cord + GRID_CELL_PADDING + 7
def castle_neighbors(map, castle_bottom_right_row, castle_bottom_right_col):
neighbors = []
for row_add in range(-2, 2):
new_row = castle_bottom_right_row + row_add
if 0 <= new_row <= len(map) - 1:
for col_add in range(-2, 2):
new_col = castle_bottom_right_col + col_add
if 0 <= new_col <= len(map) - 1:
if (new_col == castle_bottom_right_col - 1 and new_row == castle_bottom_right_row - 1) \
or (new_col == castle_bottom_right_col and new_row == castle_bottom_right_row - 1) \
or (new_col == castle_bottom_right_col - 1 and new_row == castle_bottom_right_row) \
or (new_col == castle_bottom_right_col and new_row == castle_bottom_right_row):
continue
neighbors.append((new_col, new_row))
return neighbors

View File

@ -1,28 +0,0 @@
import pygame
from logic.exceptions import FieldNotWalkable
class Field(pygame.sprite.Sprite):
def __init__(self, texture_path, converted_texture, img, rect, row=0, column=0):
super().__init__()
self.texture_path = texture_path
self.converted_texture = converted_texture
self.image = img
self.row = row
self.column = column
self.rect = rect
self.busy = False
self.busy_detection()
def busy_detection(self):
if self.texture_path == 'water.png' or self.texture_path == 'grass_with_tree.jpg':
self.busy = True
def put_on(self, obj): # ustawia objekt na srodku pola
if not self.busy:
obj.rect = obj.rect.clamp(self.rect)
self.busy = True
obj.update()
else:
raise FieldNotWalkable

View File

@ -2,19 +2,14 @@ import sys
import pygame import pygame
from common.colors import FONT_DARK, WHITE
from common.constants import * from common.constants import *
from common.helpers import draw_text from common.helpers import print_numbers
from logic.knights_queue import KnightsQueue from logic.level import Level
from models.castle import Castle
from models.knight import Knight
from models.monster import Monster
from ui.logs import Logs from ui.logs import Logs
from ui.screens.credits import Credits from ui.screens.credits import Credits
from ui.screens.main_menu import MainMenu
from ui.screens.options import Options from ui.screens.options import Options
from ui.stats import Stats from ui.stats import Stats
from .grid import Grid
from .spawner import Spawner
class Game: class Game:
@ -26,99 +21,31 @@ class Game:
self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
self.clock = pygame.time.Clock() self.clock = pygame.time.Clock()
self.tiles = []
for tile_path in TILES:
converted_tile = pygame.image.load('resources/textures/' + tile_path).convert_alpha()
self.tiles.append((tile_path, converted_tile))
self.bg = pygame.image.load("./resources/textures/bg.jpg") self.bg = pygame.image.load("./resources/textures/bg.jpg")
self.screens = {'credits': Credits(self.screen, self.clock), 'options': Options(self.screen, self.clock)} self.screens = {'credits': Credits(self.screen, self.clock), 'options': Options(self.screen, self.clock)}
click = False self.level = Level(self.screen)
def main_menu(self): def main_menu(self):
while True: menu = MainMenu(self.screen, self.clock, self.bg,
self.screen.blit(self.bg, (0, 0)) self.game,
self.screens['options'].display_screen,
pygame.draw.rect(self.screen, (255, 255, 255), pygame.Rect(800, 100, 400, 500), 0, 5) self.screens['credits'].display_screen)
draw_text('MAIN MENU', FONT_DARK, self.screen, 850, 150, 30, True) menu.display_screen()
mx, my = pygame.mouse.get_pos()
button_1 = pygame.Rect(850, 250, 300, 50)
button_2 = pygame.Rect(850, 350, 300, 50)
button_3 = pygame.Rect(850, 450, 300, 50)
if button_1.collidepoint((mx, my)):
if click:
self.game()
if button_2.collidepoint((mx, my)):
if click:
self.screens['options'].display_screen()
if button_3.collidepoint((mx, my)):
if click:
self.screens['credits'].display_screen()
pygame.draw.rect(self.screen, (0, 191, 255), button_1, 0, 4)
draw_text('PLAY', WHITE, self.screen, 870, 255)
pygame.draw.rect(self.screen, (0, 191, 255), button_2, 0, 4)
draw_text('OPTIONS', WHITE, self.screen, 870, 355)
pygame.draw.rect(self.screen, (0, 191, 255), button_3, 0, 4)
draw_text('CREDITS', WHITE, self.screen, 870, 455)
click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
click = True
pygame.display.update()
self.clock.tick(60)
def game(self): def game(self):
running = True
grid = Grid(self.tiles)
stats = Stats() stats = Stats()
logs = Logs() logs = Logs()
knights_sprite_group = pygame.sprite.Group() # setup clock for rounds
monsters_sprite_group = pygame.sprite.Group() NEXT_TURN = pygame.USEREVENT + 1
castle_sprite_group = pygame.sprite.Group() pygame.time.set_timer(NEXT_TURN, TURN_INTERVAL)
knights_left = self.generate_knights_team(knights_sprite_group) # create level
knights_right = self.generate_knights_team(knights_sprite_group) self.level.create_map()
knights_queue = KnightsQueue(knights_left, knights_right)
spawn_left_team = Spawner(grid, knights_left, KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT,
LEFT_KNIGHTS_SPAWN_FIRST_ROW, LEFT_KNIGHTS_SPAWN_FIRST_COL)
spawn_right_team = Spawner(grid, knights_right, KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT,
RIGHT_KNIGHTS_SPAWN_FIRST_ROW,
RIGHT_KNIGHTS_SPAWN_FIRST_COL)
spawn_left_team.spawn()
spawn_right_team.spawn()
spawned_monsters = self.generate_monster(monsters_sprite_group)
monster_spawn = Spawner(grid, spawned_monsters, spawn_where_possible=True)
monster_spawn.spawn()
spawned_castle = self.generate_castle(castle_sprite_group)
castle_spawn = Spawner(grid, [spawned_castle], CASTLE_SPAWN_WIDTH, CASTLE_SPAWN_HEIGHT,
CASTLE_SPAWN_FIRST_ROW, CASTLE_SPAWN_FIRST_COL)
castle_spawn.spawn()
#grid.put_on_tile(0, 0, knights_left[0])
print_numbers_flag = False
running = True
while running: while running:
self.screen.blit(self.bg, (0, 0)) self.screen.blit(self.bg, (0, 0))
@ -129,37 +56,18 @@ class Game:
if event.type == pygame.KEYDOWN: if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: if event.key == pygame.K_ESCAPE:
running = False running = False
if event.key == 110: # clicked n letter on keyboard
print_numbers_flag = not print_numbers_flag
if event.type == NEXT_TURN: # is called every 'TURN_INTERVAL' milliseconds
self.level.handle_turn()
grid.update(self.screen)
stats.draw(self.screen) stats.draw(self.screen)
logs.draw(self.screen) logs.draw(self.screen)
knights_sprite_group.draw(self.screen) self.level.update()
monsters_sprite_group.draw(self.screen)
castle_sprite_group.draw(self.screen) if print_numbers_flag:
print_numbers()
pygame.display.update() pygame.display.update()
self.clock.tick(FPS_COUNT) self.clock.tick(FPS_COUNT)
@staticmethod
def generate_knights_team(knights_sprite_group):
knights = []
for i in range(4):
knight = Knight("./resources/textures/knight.png")
knights.append(knight)
knights_sprite_group.add(knight)
return knights
@staticmethod
def generate_monster(monsters_sprite_group):
monsters = []
for i in range(2):
monster = Monster()
monsters.append(monster)
monsters_sprite_group.add(monster)
return monsters
@staticmethod
def generate_castle(castle_sprite_group):
castle = Castle()
castle_sprite_group.add(castle)
return castle

View File

@ -1,86 +0,0 @@
import random
import numpy as np
import pygame
from common.constants import ROWS, COLUMNS, GRID_CELL_PADDING, GRID_CELL_WIDTH, GRID_CELL_HEIGHT, BORDER_WIDTH, \
BORDER_RADIUS, NBR_OF_TREES, NBR_OF_WATER
from .exceptions import FieldNotWalkable
from .field import Field
class Grid:
def __init__(self, textures):
self.textures = textures
self.grid = []
self.free_fields = []
self.busy_fields = []
podkladka = np.zeros((ROWS, COLUMNS))
for drzewa in range(0, NBR_OF_TREES):
rzad = random.randint(0, ROWS - 1)
kolumna = random.randint(0, COLUMNS - 1)
if podkladka[rzad][kolumna] == 0:
podkladka[rzad][kolumna] = 1
else:
drzewa = drzewa - 1
for i in range(0, NBR_OF_WATER):
rzad = random.randint(0, ROWS - 1)
kolumna = random.randint(0, COLUMNS - 1)
if podkladka[rzad][kolumna] == 0:
podkladka[rzad][kolumna] = 2
else:
drzewa = drzewa - 1
for row in range(ROWS):
self.grid.append([])
for column in range(COLUMNS):
box_rect = [(GRID_CELL_PADDING + GRID_CELL_WIDTH) * column + GRID_CELL_PADDING + 7,
(GRID_CELL_PADDING + GRID_CELL_HEIGHT) * row + GRID_CELL_PADDING + 7,
GRID_CELL_WIDTH,
GRID_CELL_HEIGHT]
if podkladka[row][column] == 1:
texture_path, converted_texture = self.textures[-1]
elif podkladka[row][column] == 2:
texture_path, converted_texture = self.textures[-2]
else:
texture_path, converted_texture = self.get_random_texture()
field = Field(
texture_path,
converted_texture,
pygame.transform.scale(converted_texture,
(GRID_CELL_WIDTH,
GRID_CELL_HEIGHT)),
pygame.Rect(box_rect),
row=row,
column=column
)
if field.busy:
self.busy_fields.append(field)
else:
self.free_fields.append(field)
self.grid[row].append(pygame.sprite.Group(field))
def update(self, screen):
self.draw(screen)
def get_random_texture(self):
texture_index = random.randint(0, len(self.textures) - 3)
return self.textures[texture_index]
def get_tile(self, row, column):
return pygame.sprite.Group.sprites(self.grid[row][column])[0] # iteruj kolumny i wiersze od 0
def put_on_tile(self, row, column, obj): # iteruj kolumny i wiersze od 0
try:
self.get_tile(row, column).put_on(obj)
except FieldNotWalkable:
print("Field busy")
def draw(self, screen):
bg_width = (GRID_CELL_PADDING + GRID_CELL_WIDTH) * COLUMNS + BORDER_WIDTH
bg_height = (GRID_CELL_PADDING + GRID_CELL_HEIGHT) * ROWS + BORDER_WIDTH
pygame.draw.rect(screen, (255, 255, 255), pygame.Rect(5, 5, bg_width, bg_height), 0, BORDER_RADIUS)
for fields_row in self.grid:
for fields in fields_row:
fields.draw(screen)

144
logic/level.py Normal file
View File

@ -0,0 +1,144 @@
import random
import pygame
from algorithms.bfs import graphsearch, State
from common.constants import *
from common.helpers import castle_neighbors
from logic.knights_queue import KnightsQueue
from logic.spawner import Spawner
from models.castle import Castle
from models.knight import Knight
from models.monster import Monster
from models.tile import Tile
class Level:
def __init__(self, screen):
self.screen = screen
# sprite group setup
self.sprites = pygame.sprite.Group()
self.map = [[' ' for x in range(COLUMNS)] for y in range(ROWS)]
self.list_knights_blue = []
self.list_knights_red = []
self.list_monsters = []
self.list_castles = []
self.knights_queue = None
def create_map(self):
self.generate_map()
self.setup_base_tiles()
self.setup_objects()
self.knights_queue = KnightsQueue(self.list_knights_blue, self.list_knights_red)
def generate_map(self):
spawner = Spawner(self.map)
spawner.spawn_where_possible(['w' for x in range(NBR_OF_WATER)])
spawner.spawn_where_possible(['t' for x in range(NBR_OF_TREES)])
spawner.spawn_in_area(['k_b' for x in range(4)], LEFT_KNIGHTS_SPAWN_FIRST_ROW, LEFT_KNIGHTS_SPAWN_FIRST_COL,
KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT)
spawner.spawn_in_area(['k_r' for x in range(4)], RIGHT_KNIGHTS_SPAWN_FIRST_ROW, RIGHT_KNIGHTS_SPAWN_FIRST_COL,
KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT)
spawner.spawn_in_area(['c'], CASTLE_SPAWN_FIRST_ROW, CASTLE_SPAWN_FIRST_COL, CASTLE_SPAWN_WIDTH,
CASTLE_SPAWN_HEIGHT, 2)
spawner.spawn_where_possible(['m' for x in range(NBR_OF_MONSTERS)])
def setup_base_tiles(self):
textures = []
for texture_path in TILES:
converted_texture = pygame.image.load('resources/textures/' + texture_path).convert_alpha()
converted_texture = pygame.transform.scale(converted_texture, (40, 40))
textures.append((texture_path, converted_texture))
for row_index, row in enumerate(self.map):
for col_index, col in enumerate(row):
# add base tiles, e.g. water, tree, grass
if col == "w":
texture_index = 5
texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites, 'w')
elif col == "t":
texture_index = 6
texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites, 't')
else:
texture_index = random.randint(0, 4)
texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites)
def setup_objects(self):
castle_count = 0 # TODO: find some smarter method to print castle
for row_index, row in enumerate(self.map):
print(row)
for col_index, col in enumerate(row):
# add objects, e.g. knights, monsters, castle
if col == "k_b":
knight = Knight((col_index, row_index), self.sprites, "blue")
self.map[row_index][col_index] = knight
self.list_knights_blue.append(knight)
elif col == "k_r":
knight = Knight((col_index, row_index), self.sprites, "red")
self.map[row_index][col_index] = knight
self.list_knights_red.append(knight)
elif col == "m":
monster = Monster((col_index, row_index), self.sprites)
self.map[row_index][col_index] = monster
self.list_monsters.append(monster)
elif col == "c":
castle_count += 1
if castle_count == 4:
castle = Castle((col_index, row_index), self.sprites)
self.map[row_index][col_index] = castle
self.list_castles.append(castle)
def handle_turn(self):
print("next turn")
current_knight = self.knights_queue.dequeue_knight()
knight_pos_x = current_knight.position[0]
knight_pos_y = current_knight.position[1]
state = State(knight_pos_y, knight_pos_x, current_knight.direction)
castle_cords = (self.list_castles[0].position[0], self.list_castles[0].position[1])
goal_list = castle_neighbors(self.map, castle_cords[0], castle_cords[1]) # list of castle neighbors
action_list = graphsearch(state, self.map, goal_list)
print(action_list)
if len(action_list) == 0:
return
next_action = action_list.pop(0)
if next_action == ACTION.get("rotate_left"):
current_knight.rotate_left()
elif next_action == ACTION.get("rotate_right"):
current_knight.rotate_right()
elif next_action == ACTION.get("go"):
current_knight.step_forward()
self.map[knight_pos_y][knight_pos_x] = ' '
# update knight on map
if current_knight.direction.name == 'UP':
self.map[knight_pos_y - 1][knight_pos_x] = current_knight
elif current_knight.direction.name == 'RIGHT':
self.map[knight_pos_y][knight_pos_x + 1] = current_knight
elif current_knight.direction.name == 'DOWN':
self.map[knight_pos_y + 1][knight_pos_x] = current_knight
elif current_knight.direction.name == 'LEFT':
self.map[knight_pos_y][knight_pos_x - 1] = current_knight
def update(self):
bg_width = (GRID_CELL_PADDING + GRID_CELL_SIZE) * COLUMNS + BORDER_WIDTH
bg_height = (GRID_CELL_PADDING + GRID_CELL_SIZE) * ROWS + BORDER_WIDTH
pygame.draw.rect(self.screen, (255, 255, 255), pygame.Rect(5, 5, bg_width, bg_height), 0, BORDER_RADIUS)
# update and draw the game
self.sprites.draw(self.screen)

View File

@ -1,36 +1,33 @@
import random import random
from common.constants import GRID_CELL_PADDING, GRID_CELL_WIDTH, GRID_CELL_HEIGHT from common.constants import COLUMNS, ROWS
class Spawner: class Spawner:
def __init__(self, grid, objs_to_spawn_list: list, spawn_area_width=0, spawn_area_height=0, spawn_area_pos_row=0, def __init__(self, map):
spawn_area_pos_column=0, spawn_where_possible=False): # kolumny i wiersze liczymy od 0; self.map = map
self.objs_to_spawn_list = objs_to_spawn_list
self.spawn_area_width = spawn_area_width
self.spawn_area_height = spawn_area_height
self.spawn_area_pos_row = spawn_area_pos_row
self.spawn_area_pos_column = spawn_area_pos_column
self.grid = grid
self.spawn_where_possible = spawn_where_possible
def spawn(self): def __is_free_field(self, field):
if self.spawn_where_possible: return field == ' '
possible_fields = self.grid.free_fields
else:
possible_fields = []
for field in self.grid.free_fields:
if (self.spawn_area_pos_column <= field.column < (self.spawn_area_width + self.spawn_area_pos_column)) \
and (self.spawn_area_pos_row <= field.row < (self.spawn_area_height + self.spawn_area_pos_row)):
possible_fields.append(field)
for obj in self.objs_to_spawn_list: def spawn_in_area(self, objects: list, spawn_area_pos_row=0, spawn_area_pos_column=0, spawn_area_width=0,
ran_index = random.randint(0, len(possible_fields)-1) spawn_area_height=0, size=1):
ran_field = possible_fields[ran_index] spawned_objects_count = 0
ran_field.busy = True
obj.rect = obj.rect.clamp(ran_field) while spawned_objects_count != len(objects):
self.grid.busy_fields.append(ran_field) x = random.randint(0, spawn_area_height) + spawn_area_pos_row
if ran_field in self.grid.free_fields: y = random.randint(0, spawn_area_width) + spawn_area_pos_column
self.grid.free_fields.remove(ran_field) if x < ROWS-1 and y < COLUMNS-1 and self.__is_free_field(self.map[x][y]):
if ran_field in possible_fields: for i in range(size):
possible_fields.remove(ran_field) for j in range(size):
obj.update() self.map[x-i][y-j] = objects[spawned_objects_count]
spawned_objects_count += 1
def spawn_where_possible(self, objects: list):
spawned_objects_count = 0
while spawned_objects_count != len(objects):
x = random.randint(0, ROWS-1)
y = random.randint(0, COLUMNS-1)
if self.__is_free_field(self.map[x][y]):
self.map[x][y] = objects[spawned_objects_count]
spawned_objects_count += 1

View File

@ -1,15 +1,14 @@
import pygame.image import pygame.image
from common.helpers import parse_cord
class Castle(pygame.sprite.Sprite): class Castle(pygame.sprite.Sprite):
images = [] def __init__(self, position, group):
super().__init__(group)
def __init__(self): self.image = pygame.image.load("./resources/textures/castle.png").convert_alpha()
super().__init__()
self.image = pygame.image.load("./resources/textures/castle.png")
self.image = pygame.transform.scale(self.image, (78, 78)) self.image = pygame.transform.scale(self.image, (78, 78))
self.images.append(self.image) self.position = position
self.rect = self.image.get_rect() position_in_px = (parse_cord(position[0]), parse_cord(position[1]))
castle_list = pygame.sprite.Group() self.rect = self.image.get_rect(center=position_in_px)
self.health = 80 self.health = 80

View File

@ -1,17 +1,58 @@
import pygame.image import pygame.image
import random import random
class Knight(pygame.sprite.Sprite): from common.constants import GRID_CELL_SIZE, Direction
def __init__(self, img): from common.helpers import parse_cord
super().__init__()
self.images = []
self.image = pygame.image.load("./resources/textures/knight.png")
self.image = pygame.transform.scale(self.image, (40, 40))
self.images.append(self.image)
self.rect = self.image.get_rect()
def load_knight_textures():
random_index = random.randint(1, 4)
states = [
pygame.image.load(f'resources/textures/knight_{random_index}_up.png').convert_alpha(), # up = 0
pygame.image.load(f'resources/textures/knight_{random_index}_right.png').convert_alpha(), # right = 1
pygame.image.load(f'resources/textures/knight_{random_index}_down.png').convert_alpha(), # down = 2
pygame.image.load(f'resources/textures/knight_{random_index}_left.png').convert_alpha(), # left = 3
]
return states
class Knight(pygame.sprite.Sprite):
def __init__(self, position, group, team):
super().__init__(group)
self.direction = Direction.DOWN
self.states = load_knight_textures()
self.image = self.states[self.direction.value]
self.position = position
position_in_px = (parse_cord(position[0]), parse_cord(position[1]))
self.rect = self.image.get_rect(topleft=position_in_px)
self.team = team
self.health = random.randint(7, 12) self.health = random.randint(7, 12)
self.attack = random.randint(4, 7) self.attack = random.randint(4, 7)
self.defense = random.randint(1,4) self.defense = random.randint(1, 4)
self.points = 1 self.points = 1
def rotate_left(self):
self.direction = self.direction.left()
self.image = self.states[self.direction.value]
def rotate_right(self):
self.direction = self.direction.right()
self.image = self.states[self.direction.value]
def step_forward(self):
if self.direction.name == 'UP':
self.position = (self.position[0], self.position[1] - 1)
self.rect.y = self.rect.y - GRID_CELL_SIZE - 5
elif self.direction.name == 'RIGHT':
self.position = (self.position[0] + 1, self.position[1])
self.rect.x = self.rect.x + GRID_CELL_SIZE + 5
elif self.direction.name == 'DOWN':
self.position = (self.position[0], self.position[1] + 1)
self.rect.y = self.rect.y + GRID_CELL_SIZE + 5
elif self.direction.name == 'LEFT':
self.position = (self.position[0] - 1, self.position[1])
self.rect.x = self.rect.x - GRID_CELL_SIZE - 5

View File

@ -1,47 +1,39 @@
import pygame.image import pygame.image
import random import random
from common.helpers import parse_cord
monster_images = [
pygame.image.load("./resources/textures/dragon2.png"),
pygame.image.load("./resources/textures/birb.png"),
pygame.image.load("./resources/textures/wolfart.png"),
pygame.image.load("./resources/textures/goblin.png"),
]
class Monster(pygame.sprite.Sprite): class Monster(pygame.sprite.Sprite):
monster_images = [] def __init__(self, position, group):
super().__init__(group)
def __init__(self): self.image = random.choice(monster_images)
super().__init__() self.image = pygame.transform.scale(self.image, (40, 40))
position_in_px = (parse_cord(position[0]), parse_cord(position[1]))
self.monster_image1 = pygame.image.load("./resources/textures/dragon2.png") self.rect = self.image.get_rect(topleft=position_in_px)
self.monster_image1 = pygame.transform.scale(self.monster_image1, (40, 40))
self.monster_image2 = pygame.image.load("./resources/textures/birb.png")
self.monster_image2 = pygame.transform.scale(self.monster_image2, (40, 40))
self.monster_image3 = pygame.image.load("./resources/textures/wolfart.png")
self.monster_image3 = pygame.transform.scale(self.monster_image3, (40, 40))
self.monster_image4 = pygame.image.load("./resources/textures/goblin.png")
self.monster_image4 = pygame.transform.scale(self.monster_image4, (40, 40))
self.monster_images.append(self.monster_image1)
self.monster_images.append(self.monster_image2)
self.monster_images.append(self.monster_image3)
self.monster_images.append(self.monster_image4)
#self.images.append(self.image1)
self.image = random.choice(self.monster_images)
self.rect = self.image.get_rect()
monster_list = pygame.sprite.Group()
self.health = random.randrange(15, 25) self.health = random.randrange(15, 25)
self.attack = random.randrange(2, 10) self.attack = random.randrange(2, 10)
if self.image == self.monster_images[0]: if self.image == monster_images[0]:
self.health = 20 self.health = 20
self.attack = 9 self.attack = 9
self.points = 10 self.points = 10
elif self.image == self.monster_images[1]: elif self.image == monster_images[1]:
self.health = 15 self.health = 15
self.attack = 7 self.attack = 7
self.points = 7 self.points = 7
elif self.image == self.monster_images[2]: elif self.image == monster_images[2]:
self.health = 10 self.health = 10
self.attack = 4 self.attack = 4
self.points = 4 self.points = 4
elif self.image == self.monster_images[3]: elif self.image == monster_images[3]:
self.health = 7 self.health = 7
self.attack = 2 self.attack = 2
self.points = 2 self.points = 2

12
models/tile.py Normal file
View File

@ -0,0 +1,12 @@
import pygame
from common.helpers import parse_cord
class Tile(pygame.sprite.Sprite):
def __init__(self, position, image, group, tile_type=' '):
super().__init__(group)
self.image = image
position_in_px = (parse_cord(position[0]), parse_cord(position[1]))
self.rect = self.image.get_rect(topleft=position_in_px)
self.tile_type = tile_type

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,7 +1,7 @@
import pygame import pygame
from common.colors import FONT_DARK, ORANGE, WHITE from common.colors import FONT_DARK, ORANGE, WHITE
from common.constants import COLUMNS, GRID_CELL_PADDING, GRID_CELL_WIDTH, BORDER_WIDTH, BORDER_RADIUS from common.constants import COLUMNS, GRID_CELL_PADDING, GRID_CELL_SIZE, BORDER_WIDTH, BORDER_RADIUS
from common.helpers import draw_text from common.helpers import draw_text
@ -10,7 +10,7 @@ class Logs:
self.grid = [] self.grid = []
def draw(self, screen): def draw(self, screen):
x = (GRID_CELL_PADDING + GRID_CELL_WIDTH) * COLUMNS + BORDER_WIDTH + 15 x = (GRID_CELL_PADDING + GRID_CELL_SIZE) * COLUMNS + BORDER_WIDTH + 15
y = 470 y = 470
# background # background

66
ui/screens/main_menu.py Normal file
View File

@ -0,0 +1,66 @@
import sys
import pygame
from common.colors import WHITE, FONT_DARK
from common.helpers import draw_text
from ui.screens.credits import Credits
from ui.screens.options import Options
from ui.screens.screen import Screen
class MainMenu(Screen):
def __init__(self, screen, clock, bg, btn_play_action, btn_options_action, btn_credits_action):
super().__init__('main_menu', screen, clock)
self.click = False
self.bg = bg
self.btn_play_action = btn_play_action
self.btn_options_action = btn_options_action
self.btn_credits_action = btn_credits_action
def display_screen(self):
running = True
while running:
self.screen.blit(self.bg, (0, 0))
pygame.draw.rect(self.screen, (255, 255, 255), pygame.Rect(800, 100, 400, 500), 0, 5)
draw_text('MAIN MENU', FONT_DARK, self.screen, 850, 150, 30, True)
mx, my = pygame.mouse.get_pos()
button_1 = pygame.Rect(850, 250, 300, 50)
button_2 = pygame.Rect(850, 350, 300, 50)
button_3 = pygame.Rect(850, 450, 300, 50)
if button_1.collidepoint((mx, my)):
if self.click:
self.btn_play_action()
if button_2.collidepoint((mx, my)):
if self.click:
self.btn_options_action()
if button_3.collidepoint((mx, my)):
if self.click:
self.btn_credits_action()
pygame.draw.rect(self.screen, (0, 191, 255), button_1, 0, 4)
draw_text('PLAY', WHITE, self.screen, 870, 255)
pygame.draw.rect(self.screen, (0, 191, 255), button_2, 0, 4)
draw_text('OPTIONS', WHITE, self.screen, 870, 355)
pygame.draw.rect(self.screen, (0, 191, 255), button_3, 0, 4)
draw_text('CREDITS', WHITE, self.screen, 870, 455)
self.click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self.click = True
pygame.display.update()
self.clock.tick(60)

View File

@ -1,7 +1,7 @@
import pygame import pygame
from common.colors import FONT_DARK, ORANGE, WHITE, RED from common.colors import FONT_DARK, ORANGE, WHITE, RED
from common.constants import COLUMNS, GRID_CELL_PADDING, GRID_CELL_WIDTH, BORDER_WIDTH, BORDER_RADIUS from common.constants import COLUMNS, GRID_CELL_PADDING, GRID_CELL_SIZE, BORDER_WIDTH, BORDER_RADIUS
from common.helpers import draw_text from common.helpers import draw_text
@ -10,7 +10,7 @@ class Stats:
self.grid = [] self.grid = []
def draw(self, screen): def draw(self, screen):
x = (GRID_CELL_PADDING + GRID_CELL_WIDTH) * COLUMNS + BORDER_WIDTH + 15 x = (GRID_CELL_PADDING + GRID_CELL_SIZE) * COLUMNS + BORDER_WIDTH + 15
y = 5 y = 5
# background # background