import tkinter as tk from config import * root = tk.Tk() root.title( "Push the Box") # sets the title of the window canvas = tk.Canvas(root, width = WIDTH, height = HEIGHT) # defines the canvas canvas.pack() # packs the canvas class Game: def __init__(self): self.walls_coords = [] # empty list where all the wall coords will be stored as tuples after reading from level.txt # variables for all images self.img_wall = tk.PhotoImage(file = "textures\wall.png") self.img_player_neutral = tk.PhotoImage(file = "textures\player_neutral.png") self.img_player_up = tk.PhotoImage(file = "textures\player_up.png") self.img_player_down = tk.PhotoImage(file = "textures\player_down.png") self.img_player_left = tk.PhotoImage(file = "textures\player_left.png") self.img_player_right = tk.PhotoImage(file = "textures\player_right.png") self.img_box = tk.PhotoImage(file = "textures\\box.png") self.img_win_location_off = tk.PhotoImage(file = "textures\win_location_off.png") self.img_win_location_on = tk.PhotoImage(file = "textures\win_location_on.png") # all x and y values for both player and the box storing how much had player and the box deviated from their starting position # after reset, player and box will be moved the opposite direction of these values to be at the start again self.x_player_change = 0 self.y_player_change = 0 self.x_box_change = 0 self.y_box_change = 0 def process_level(self): # this function will read through level.txt, process the information and draw walls, player, box and winning location on the spot where they belong self.level_layout = open("level.txt", "r", encoding = "UTF-8") # the program opens level.txt # helping variables to calculate the distance of a certain wall tile self.x_counter = 0 self.y_counter = 0 for self.y_level in self.level_layout: # reads through the every line in level.txt self.y_level = self.y_level.strip() # strips it so it's the correct format for self.x_level in self.y_level: # reads through every character in the specific line if self.x_level == "#": # if the character is "#" -> it is a wall self.x_wall = int(WIDTH/2) + ((self.x_counter - 7) * TILE_SIZE) # specifies, where the wall should be on the x axis by multiplying the counter value by TILE_SIZE so it would be placed in a grid self.y_wall = int(HEIGHT/2) + ((self.y_counter - 7) * TILE_SIZE) # and the same for the y axis self.wall = canvas.create_image(self.x_wall - PLAYER_SIZE, self.y_wall - PLAYER_SIZE,anchor = tk.NW, image = self.img_wall) # creates the wall on the position it should appear in the grid self.walls_coords.append((self.x_wall - PLAYER_SIZE, self.y_wall - PLAYER_SIZE)) # appends the wall coords as a tuple to a list walls_coords (this is for checking collisions later) elif self.x_level == "P": # if the character is "P" -> it is the player self.x_player = int(WIDTH/2) + int(((self.x_counter - 7) * TILE_SIZE)) # does exactly the same for the player for both x... self.y_player = int(HEIGHT/2) + int(((self.y_counter - 7) * TILE_SIZE)) # ...and y axes self.player = canvas.create_image(self.x_player - PLAYER_SIZE, self.y_player - PLAYER_SIZE, anchor = tk.NW, image = self.img_player_neutral) # creates the player elif self.x_level == "B": # if character is "B" -> it is the box self.x_box = int(WIDTH/2) + int(((self.x_counter - 7) * TILE_SIZE)) self.y_box = int(HEIGHT/2) + int(((self.y_counter - 7) * TILE_SIZE)) self.x_box_start = self.x_box self.y_box_start = self.y_box self.box = canvas.create_image(self.x_box - PLAYER_SIZE, self.y_box - PLAYER_SIZE, anchor = tk.NW, image = self.img_box) # creates the box elif self.x_level == "W": # if the character is "W" -> winning location self.x_win_location = int(WIDTH/2) + int(((self.x_counter - 7) * TILE_SIZE)) self.y_win_location = int(HEIGHT/2) + int(((self.y_counter - 7) * TILE_SIZE)) self.win_location = canvas.create_image(self.x_win_location - PLAYER_SIZE, self.y_win_location - PLAYER_SIZE, anchor = tk.NW, image = self.img_win_location_off) self.x_counter += 1 self.x_counter = 0 self.y_counter += 1 self.level_layout.close() # the text file closes so it would not create any further problems def draw_controls(self): # this function will draw controls self.win_text = canvas.create_text(100, 50, text = "", fill = "black", font = "Helvetica 20 bold") # empty text, that will later change to "YOU WIN!" after the player finishes the level self.controls_text_title = canvas.create_text(TILE_SIZE, HEIGHT - 6*TILE_SIZE, text = "CONTROLS:", anchor = tk.NW, fill = "black", font = "Helvetica 20 bold") # title to display controls self.controls_text = canvas.create_text(TILE_SIZE, HEIGHT - 5*TILE_SIZE, text = "W - up\nA - left\nS - down\nD - right\n\nR - reset", anchor = tk.NW, fill = "black", font = "Helvetica 18 bold") # text to display controls def actions(self, event): # this function stores all the actions, that are bound self.x, self.y = 0, 0 # the x and y values to determine how much will the player move, if a key is pressed, but it is not for movement, the player and box will move 0px in x and y direction if event.char == "w": # if the pressed key is W if ((canvas.coords(self.player)[0], canvas.coords(self.player)[1] - TILE_SIZE) in self.walls_coords) or (canvas.coords(self.player)[0] == canvas.coords(self.win_location)[0] and canvas.coords(self.player)[1] - TILE_SIZE == canvas.coords(self.win_location)[1]): # checks if player's x1; y1 is equal to any tuple in the walls_list, if the output is True, this means the player is in colision with a wall and cannot move this way, same is done for winning location pass # if collision is detected, the player doesn't move and x, y will still be equal to 0 else: # if the collision is not detected self.y = -TILE_SIZE # the player can move upwards self.y_player_change += self.y # adds the changed y value to y_player_change to be later used in reset if (canvas.coords(self.player)[1] - canvas.coords(self.box)[1]) == 40 and (canvas.coords(self.player)[0] - canvas.coords(self.box)[0]) == 0: # checks collision with the box, this checks if the box and player are in valid position to be moved if (canvas.coords(self.box)[0], canvas.coords(self.box)[1] - TILE_SIZE) in self.walls_coords: # if the box collides with the wall self.y_player_change -= self.y # it reverts the player movement so it would not count even if the player doesn't move self.y = 0 # neither the player not box move upwards else: canvas.move(self.box, self.x, self.y) # if no collision is detected, the box will move upwards self.y_box_change += self.y # box change is counted into the change to be later used in reset canvas.itemconfig(self.player, image = self.img_player_up) elif event.char == "a": # if te pressed key is A # the program, will do exactly the same process as before, but instead of calculating movement upwards, it will do it for the left side if ((canvas.coords(self.player)[0] - TILE_SIZE, canvas.coords(self.player)[1]) in self.walls_coords) or (canvas.coords(self.player)[0] - TILE_SIZE == canvas.coords(self.win_location)[0] and canvas.coords(self.player)[1] == canvas.coords(self.win_location)[1]): # wall collision pass else: self.x = -TILE_SIZE self.x_player_change +=self.x if (canvas.coords(self.player)[0] - canvas.coords(self.box)[0]) == 40 and (canvas.coords(self.player)[1] - canvas.coords(self.box)[1]) == 0: # box movement if (canvas.coords(self.box)[0] - TILE_SIZE, canvas.coords(self.box)[1]) in self.walls_coords: self.x_player_change -= self.x self.x = 0 else: canvas.move(self.box, self.x, self.y) self.x_box_change += self.x canvas.itemconfig(self.player, image = self.img_player_left) elif event.char == "s": # if the pressed key is S # calculating downward movements if ((canvas.coords(self.player)[0], canvas.coords(self.player)[1] + TILE_SIZE) in self.walls_coords) or (canvas.coords(self.player)[0] == canvas.coords(self.win_location)[0] and canvas.coords(self.player)[1] + TILE_SIZE == canvas.coords(self.win_location)[1]): # wall collision pass else: self.y = TILE_SIZE self.y_player_change += self.y if (canvas.coords(self.player)[1] - canvas.coords(self.box)[1]) == -40 and (canvas.coords(self.player)[0] - canvas.coords(self.box)[0]) == 0: # box movement if (canvas.coords(self.box)[0], canvas.coords(self.box)[1] + TILE_SIZE) in self.walls_coords: self.y_player_change -= self.y self.y = 0 else: canvas.move(self.box, self.x, self.y) self.y_box_change += self.y canvas.itemconfig(self.player, image = self.img_player_down) elif event.char == "d": # if the pressed key is D # movements for the right side if ((canvas.coords(self.player)[0] + TILE_SIZE, canvas.coords(self.player)[1]) in self.walls_coords) or (canvas.coords(self.player)[0] + TILE_SIZE == canvas.coords(self.win_location)[0] and canvas.coords(self.player)[1] == canvas.coords(self.win_location)[1]): # wall collision pass else: self.x = TILE_SIZE self.x_player_change += self.x if (canvas.coords(self.player)[0] - canvas.coords(self.box)[0]) == -40 and (canvas.coords(self.player)[1] - canvas.coords(self.box)[1]) == 0: # box movement if (canvas.coords(self.box)[0] + TILE_SIZE, canvas.coords(self.box)[1]) in self.walls_coords: self.x_player_change -= self.x self.x = 0 else: canvas.move(self.box, self.x, self.y) self.x_box_change += self.x canvas.itemconfig(self.player, image = self.img_player_right) elif event.char == "r": # if the player pressed R canvas.move(self.player, -self.x_player_change, -self.y_player_change) # the player will be moved in reversed directions of the calculated deviations from starting self.x_player_change = 0 # the x and y changes are then changed back to 0 and the new calculation can begin after the player moves again self.y_player_change = 0 canvas.itemconfig(self.player, image = self.img_player_neutral) canvas.move(self.box, -self.x_box_change, -self.y_box_change) # same is done for the box as well self.x_box_change = 0 self.y_box_change = 0 canvas.itemconfig(self.win_text, text = "") # the win text is changed to empty string, this only has impact when the player already won canvas.itemconfig(self.win_location, image = self.img_win_location_off) # win location image is changed to it's off variant if (canvas.coords(self.box)[0] == canvas.coords(self.win_location)[0]) and (canvas.coords(self.box)[1] == canvas.coords(self.win_location)[1]): # this checks if the x1; y1 of the box and x1; y1 of the winning location matches, if yes, the palyer has won canvas.itemconfig(self.win_text, text = "YOU WIN!") # empty text will be changed to "YOU WIN!" canvas.itemconfig(self.win_location, image = self.img_win_location_on) # image of win location is changed to fit the box outline, meaning, the level victory canvas.move(self.player, self.x, self.y) # this moves the player in the wanted direction, if W, A, S nor D was pressed, the player will move 0px, which means it will not move game = Game() # stores Game() into an object game.process_level() # processes the text file and draws the level game.draw_controls() # displays the controls on the screen canvas.bind_all("<Key>", game.actions) # if any key is pressed, the actions function will be called to determine what to do, or if actually do anything at all root.mainloop() # mainloop for the entire window