--- /dev/null
+import pygame
+
+from setup import *
+
+
+two = 2 # The value of two. Do. Not. Change. It. Disastrous consequences. You have been warned.
+
+
+class Tile:
+ width = 100
+ height = 100
+ speed = 90 / 16 # since delta is 16 when fps is 60
+ colours = [(238, 230, 219),
+ (236, 224, 200),
+ (239, 178, 124),
+ (241, 152, 102),
+ (243, 125, 99),
+ (244, 96, 66),
+ (236, 205, 122),
+ (237, 203, 103),
+ (236, 200, 90),
+ (231, 194, 88),
+ (232, 189, 78),
+ ]
+
+ def __init__(self, pos):
+ self.position = pygame.Vector2(pos)
+ self.target = self.position
+ self.velocity = pygame.Vector2(0, 0)
+ self.number = two ** random.randint(1, 2) # the tile can start with 2 or 4 according to wikipedia
+ self.can_merge = True
+ self.colour = self.colours[math.floor(math.log(self.number, two)) - 1]
+ self.label = Label(self.get_rect().center, [[[f"{self.number=}", self]]], 20, Colour.BLACK)
+
+ @staticmethod
+ def get_direction(start, end):
+ out = end - start
+ if out == pygame.Vector2(0, 0):
+ return out
+ return out.normalize()
+
+ def update(self, delta, pos):
+ self.target = pos
+ self.velocity = self.get_direction(self.position, pos) * min((self.target - self.position).length(),
+ self.speed * delta) # so that we don't overshoot
+ self.position += self.velocity # We know that there will be no obstacles in the way
+ self.label.update(delta, x=self.get_rect().centerx, y=self.get_rect().centery)
+ self.colour = self.colours[math.floor(math.log(self.number, two)) - 1]
+
+ def is_good(self):
+ if self.position == self.target:
+ return True
+ return False
+
+ def get_rect(self):
+ return pygame.Rect(self.position + pygame.Vector2(5, 5),
+ (self.width - 10, self.height - 10)) # to account for the grid outlines
+
+ def draw(self, surf):
+ pygame.draw.rect(surf, self.colour, self.get_rect())
+ self.label.draw(surf)
+
+
+class Grid:
+ rows = 4
+ cols = 4
+ cell_width = 100
+ cell_height = 100
+ width = cell_width * cols
+ height = cell_height * rows
+ offset = pygame.Vector2(SCREEN_WIDTH - width, SCREEN_HEIGHT - height) / 2
+
+ def __init__(self):
+ self.grid = self.new_grid()
+ self.direction = None
+ self.prev_direction = None
+ self.just_pressed = False
+ self.can_move = True
+ for i in range(2):
+ self.add_tile()
+
+ def new_grid(self):
+ return [[None for _ in range(self.cols)] for _ in range(self.rows)]
+
+ def no_legal_move(self):
+ def in_bounds(r, c, grid, row):
+ if (0 <= r <= len(grid) - 1 and
+ 0 <= c <= len(row) - 1):
+ return True
+ return False
+
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row):
+ if val is None:
+ return False
+ elif isinstance(val, Tile):
+ for i in range(-1, 2):
+ for j in range(-1, 2):
+ if (i == 0 or j == 0) and not (
+ i == 0 and j == 0): # so that we get the up, down, left, right no diagonal or center
+ if (in_bounds(r - i, c - j, self.grid, row) and
+ isinstance(self.grid[r - i][c - j], Tile) and
+ self.grid[r - i][c - j].number == self.grid[r][c].number):
+ return False
+ return True
+
+ def get_empty(self):
+ out = []
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row):
+ if val is None:
+ out.append((r, c))
+ return out
+
+ def get_input(self):
+ pressed = pygame.key.get_pressed()
+ if pressed[pygame.K_UP]:
+ return "UP"
+ elif pressed[pygame.K_DOWN]:
+ return "DOWN"
+ elif pressed[pygame.K_LEFT]:
+ return "LEFT"
+ elif pressed[pygame.K_RIGHT]:
+ return "RIGHT"
+ return None
+
+ def update(self, delta, has_won):
+ self.prev_direction = self.direction
+ self.direction = self.get_input()
+ if self.prev_direction != self.direction and self.direction is not None:
+ self.just_pressed = True
+ else:
+ self.just_pressed = False
+
+ score_this_turn = 0
+ if self.can_move and self.just_pressed and has_won:
+ for i in range(max(self.rows, self.cols)):
+ if self.direction == "UP":
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row):
+ if not c - 1 < 0:
+ if isinstance(val, Tile):
+ target = self.grid[r][c - 1]
+ if target is None:
+ self.grid[r][c - 1] = self.grid[r][c]
+ self.grid[r][c] = None
+ elif isinstance(target,
+ Tile) and target.number == val.number and target.can_merge and val.can_merge:
+ target.number *= two
+ score_this_turn += target.number
+ target.can_merge = False
+ self.grid[r][c] = None
+
+ elif self.direction == "DOWN":
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row[::-1]):
+ if not c - 1 < 0:
+ if isinstance(val, Tile):
+ target = self.grid[r][len(self.grid[r]) - c]
+ if target is None:
+ self.grid[r][len(self.grid[r]) - c] = self.grid[r][len(self.grid[r]) - c - 1]
+ self.grid[r][len(self.grid[r]) - c - 1] = None
+ elif isinstance(target,
+ Tile) and target.number == val.number and target.can_merge and val.can_merge:
+ target.number *= two
+ score_this_turn += target.number
+ target.can_merge = False
+ self.grid[r][len(self.grid[r]) - c - 1] = None
+ elif self.direction == "LEFT":
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row):
+ if not r - 1 < 0:
+ if isinstance(val, Tile):
+ target = self.grid[r - 1][c]
+ if target is None:
+ self.grid[r - 1][c] = self.grid[r][c]
+ self.grid[r][c] = None
+ elif isinstance(target,
+ Tile) and target.number == val.number and target.can_merge and val.can_merge:
+ target.number *= two
+ score_this_turn += target.number
+ target.can_merge = False
+ self.grid[r][c] = None
+ elif self.direction == "RIGHT":
+ for r, row in enumerate(self.grid[::-1]):
+ for c, val in enumerate(row):
+ if not r - 1 < 0:
+ if isinstance(val, Tile):
+ target = self.grid[len(self.grid) - r][c]
+ if target is None:
+ self.grid[len(self.grid) - r][c] = self.grid[len(self.grid) - r - 1][c]
+ self.grid[len(self.grid) - r - 1][c] = None
+ elif isinstance(target,
+ Tile) and target.number == val.number and target.can_merge and val.can_merge:
+ target.number *= two
+ score_this_turn += target.number
+ target.can_merge = False
+ self.grid[len(self.grid) - r - 1][c] = None
+
+ if self.just_pressed and has_won:
+ self.add_tile()
+
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row):
+ if isinstance(val, Tile):
+ val.can_merge = True
+
+ self.can_move = self.find_can_move(delta)
+ return score_this_turn
+
+ def win_condition(self):
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row):
+ if isinstance(val, Tile):
+ if val.number == two ** 11: # 2 ^ 11 = 2048
+ return True
+ return False
+
+ def add_tile(self):
+ if len(self.get_empty()) > 0:
+ r, c = random.choice(self.get_empty())
+ self.grid[r][c] = Tile(self.offset + pygame.Vector2(r * self.cell_width, c * self.cell_height))
+
+ def find_can_move(self, delta):
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row):
+ if isinstance(val, Tile):
+ val.update(delta, self.offset + pygame.Vector2(r * self.cell_width, c * self.cell_height))
+ if not val.is_good():
+ return False
+ return True
+
+ def get_rect(self, x=0, y=0, w=1, h=1):
+ return pygame.Rect(self.offset + pygame.Vector2(x, y), (w, h))
+
+ def get_cell_rect(self, x, y, w=1, h=1):
+ return pygame.Rect(self.offset + pygame.Vector2(x, y), (self.cell_width * w, self.cell_height * h))
+
+ def draw(self, surf):
+ for r, row in enumerate(self.grid):
+ for c, val in enumerate(row):
+ pygame.draw.rect(surf, Colour.SQUARE_BG, self.get_cell_rect(r * self.cell_width, c * self.cell_height))
+ pygame.draw.rect(surf, Colour.SQUARE_OUTLINE,
+ self.get_cell_rect(r * self.cell_width, c * self.cell_height), 10)
+ if isinstance(val, Tile):
+ val.draw(surf)
+ pygame.draw.rect(surf, Colour.SQUARE_OUTLINE, self.get_rect(-5, -5, self.width + 10, self.height + 10), 10, 5)
+
+class Effect:
+ speed = 0.15
+
+ def __init__(self, pos, txt):
+ self.label = Label(pos, txt, 15, Colour.BLACK)
+ self.vel_y = 0
+
+ def update(self, delta):
+ self.label.update(delta, y=self.label.position.y + self.vel_y)
+ self.vel_y -= self.speed
+
+ def is_out_of_bounds(self):
+ if self.label.position.y + self.label.height < 0:
+ return True
+ return False
+
+
+ def draw(self, surf):
+ self.label.draw(surf)
+
+class Game:
+ # Background colour
+ background_colour = (250, 248, 239)
+ high_score = 0
+ has_won = False
+
+ def __init__(self):
+ self.win = False
+ self.game_over = False
+ self.grid = Grid()
+ self.win_effect = pygame.Surface((self.grid.width + 10, self.grid.height + 10))
+ self.win_effect_time = 0
+ self.game_title = two ** 11
+ self.title = Label((0, 0), [[[f"{self.game_title=}", self]]], 50, Colour.BLACK, centered_x=False,
+ centered_y=False)
+ self.score = 0
+ self.score_label = Label((0, 0), [["Score:"], [[f"{self.score=}", self]]], 15, Colour.WHITE, filled=True,
+ fill_colour=Colour.SQUARE_OUTLINE)
+ self.high_score_label = Label((0, 0), [["Best:"], [[f"{self.high_score=}", self]]], 15, Colour.WHITE,
+ filled=True, fill_colour=Colour.SQUARE_OUTLINE)
+ self.new_game_button = Button((SCREEN_WIDTH / 2, SCREEN_HEIGHT * 8 / 9), [["New Game"]], 20, self.__init__,
+ Colour.BLACK, filled=True, fill_colour=Colour.SQUARE_OUTLINE, outlined=True,
+ outline_colour=Colour.GRAY, outline_radius=5)
+ self.how_to_play = Label((0, 0),
+ [["Join the numbers and get to the ", [f"{self.game_title=}", self], " tile!"]], 15,
+ Colour.BLACK, centered_x=False)
+ self.continue_button = Button((SCREEN_WIDTH / 3, SCREEN_HEIGHT * 3 / 5), [["Keep going"]], 20, self.keep_going,
+ Colour.BLACK, filled=True, fill_colour=Colour.SQUARE_OUTLINE, outlined=True,
+ outline_colour=Colour.GRAY, outline_radius=5)
+ self.replay_button = Button((SCREEN_WIDTH/2, SCREEN_HEIGHT * 3 / 5), [["Try again"]], 20, self.__init__,
+ Colour.BLACK, filled=True, fill_colour=Colour.SQUARE_OUTLINE, outlined=True,
+ outline_colour=Colour.GRAY, outline_radius=5)
+ self.win_label = Label((SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - self.grid.cell_height / 4), [["You win!"]], 60,
+ Colour.WHITE)
+ self.lose_label = Label((SCREEN_WIDTH/2, SCREEN_HEIGHT/2 - self.grid.cell_height/4), [["You lose!"]], 60, Colour.WHITE)
+ self.effects = []
+ self.temp = 0
+ self.update(0)
+
+ def keep_going(self):
+ self.has_won = True
+ self.win = False
+
+ def update(self, delta):
+ if self.game_over:
+ self.lose_label.update(delta)
+ self.win_effect.fill(Colour.BLACK)
+ self.win_effect.set_alpha(min(75, (math.floor(255 * self.win_effect_time / 1000))))
+ self.win_effect_time += delta
+ self.replay_button.update(delta, x=SCREEN_WIDTH / 2)
+
+ self.temp = self.grid.update(delta, not self.win)
+ self.score += self.temp
+ if self.temp > 0:
+ self.effects.append(Effect(self.score_label.position, [[f"+{self.temp}"]]))
+ if self.win:
+ self.win_label.update(delta)
+ self.continue_button.update(delta)
+ self.replay_button.update(delta, x=SCREEN_WIDTH * 2 / 3)
+ self.win_effect.fill(Colour.GOLD)
+ self.win_effect.set_alpha(min(75, (math.floor(255 * self.win_effect_time / 10000))))
+ self.win_effect_time += delta
+ self.high_score = max(self.score, self.high_score)
+ if not self.has_won and self.grid.win_condition():
+ self.win = True
+ self.has_won = True
+ if self.grid.no_legal_move():
+ self.game_over = True
+ self.title.update(delta, x=self.grid.offset.x, y=self.grid.offset.y - self.title.height * 1.5)
+ self.high_score_label.update(delta, x=self.grid.offset.x + self.grid.width - self.high_score_label.width / 2,
+ y=self.grid.offset.y - self.high_score_label.height * 1.5)
+ self.score_label.update(delta,
+ x=self.high_score_label.position.x - self.high_score_label.width / 2 - self.score_label.width - Label.horizontal_padding,
+ y=self.grid.offset.y - self.high_score_label.height * 1.5)
+ self.how_to_play.update(delta, x=self.grid.offset.x, y=self.grid.offset.y - self.how_to_play.height)
+ self.new_game_button.update(delta)
+ for effect in self.effects:
+ effect.update(delta)
+ if effect.is_out_of_bounds():
+ del self.effects[self.effects.index(effect)]
+
+ def draw(self, surf):
+ surf.fill(self.background_colour)
+ self.grid.draw(surf)
+ self.title.draw(surf)
+ self.score_label.draw(surf)
+ self.high_score_label.draw(surf)
+ self.new_game_button.draw(surf)
+ self.how_to_play.draw(surf)
+ for effect in self.effects:
+ effect.draw(surf)
+ if self.win:
+ surf.blit(self.win_effect, self.grid.offset - pygame.Vector2(5, 5))
+ self.win_label.draw(surf)
+ self.continue_button.draw(surf)
+ self.replay_button.draw(surf)
+ elif self.game_over:
+ surf.blit(self.win_effect, self.grid.offset - pygame.Vector2(5,5))
+ self.lose_label.draw(surf)
+ self.replay_button.draw(surf)
+
+
+"""
+class Tile:
+ width = SCREEN_WIDTH / 2 / 4
+ height = SCREEN_WIDTH / 2 / 4
+ gravity_force = 0.1
+
+ def __init__(self, pos, collide_list, number=2 ** random.randint(1,2)):
+ self.position = pygame.Vector2(pos)
+ self.velocity = pygame.Vector2(0, 0)
+ self.up_direction = pygame.Vector2(0, 0)
+ self.collide_list = collide_list
+ self.collide_list.append(self)
+ self.can_move = True
+ self.number = 2
+ self.center = self.get_center()
+ self.label = Label(self.center, [[[f"{self.number=}", self]]], 20, Colour.BLACK)
+
+ def get_center(self):
+ return self.position + pygame.Vector2(self.width/2, self.height/2)
+
+
+ def get_collision_rect(self):
+ return pygame.Rect(self.position, (self.width, self.height))
+
+ def get_input(self):
+ pressed = pygame.key.get_pressed()
+ if pressed[pygame.K_LEFT]:
+ self.up_direction = pygame.Vector2(-1, 0)
+ return True
+ elif pressed[pygame.K_RIGHT]:
+ self.up_direction = pygame.Vector2(1, 0)
+ return True
+ elif pressed[pygame.K_UP]:
+ self.up_direction = pygame.Vector2(0, -1)
+ return True
+ elif pressed[pygame.K_DOWN]:
+ self.up_direction = pygame.Vector2(0, 1)
+ return True
+
+ def collided(self, thing): # called when the tile collides
+ self.can_move = True
+ if isinstance(thing, Tile):
+ if thing.number == self.number:
+ thing.number *= 2
+ if self in self.collide_list:
+ del self.collide_list[self.collide_list.index(self)]
+
+ def update(self, delta, can_move):
+ if can_move:
+ self.get_input()
+
+ self.position, self.velocity = self.move_and_collide(delta, self.position, self.velocity, self.up_direction)
+ self.center = self.get_center()
+ self.label.update(delta, x=self.center.x, y=self.center.y)
+
+ def move_and_collide(self, delta, position, velocity, up_direction=pygame.Vector2(0, 0)):
+ pos = position
+ vel = velocity
+ vel += up_direction * delta * self.gravity_force
+
+ pos.x += vel.x * delta
+ for thing in self.collide_list:
+ if thing == self:
+ continue
+ if self.get_collision_rect().colliderect(thing.get_collision_rect()):
+ if vel.x > 0:
+ pos.x = thing.position.x - self.width
+ else:
+ pos.x = thing.position.x + thing.width
+ vel.x = 0
+ self.collided(thing)
+ break
+
+ pos.y += vel.y * delta
+ for thing in self.collide_list:
+ if thing == self:
+ continue
+ if self.get_collision_rect().colliderect(thing.get_collision_rect()):
+ if vel.y > 0:
+ pos.y = thing.position.y - self.height
+ else:
+ pos.y = thing.position.y + thing.height
+ vel.y = 0
+ self.collided(thing)
+ break
+
+ return pos, vel
+
+ def draw(self, surf):
+ pygame.draw.rect(surf, Colour.RED, self.get_collision_rect())
+ self.label.draw(surf)
+
+
+class Wall:
+
+ def __init__(self, pos, collide_list, is_vertical=True, face_right=True, visible=True):
+ self.position = pygame.Vector2(pos)
+ self.start_pos = pygame.Vector2(pos)
+ self.collide_list = collide_list
+ self.collide_list.append(self)
+
+ self.visible = visible
+
+ self.width = SCREEN_WIDTH / 2
+ self.height = SCREEN_HEIGHT / 2
+
+ if is_vertical:
+ self.end_pos = self.position + pygame.Vector2(0, self.height)
+ if not face_right:
+ self.position.x -= self.width
+ else:
+ self.end_pos = self.position + pygame.Vector2(self.width, 0)
+ if not face_right:
+ self.position.y -= self.height
+
+ self.end_point = pygame.Vector2(self.width, self.height)
+
+ def get_collision_rect(self):
+ return pygame.Rect(self.position, self.end_point)
+
+ def update(self, delta):
+ pass
+
+ def draw(self, surf):
+ # pygame.draw.rect(surf, Colour.BLACK, self.get_collision_rect())
+ if self.visible:
+ pygame.draw.line(surf, Colour.BLACK, self.start_pos, self.end_pos, 10)
+
+
+class Game:
+
+ def __init__(self):
+ self.collision_list = []
+ self.walls = [
+ Wall((SCREEN_WIDTH / 4, SCREEN_HEIGHT * 3 / 4), self.collision_list, False, True),
+ Wall((SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4), self.collision_list, False, False),
+ Wall((SCREEN_WIDTH * 3 / 4, SCREEN_HEIGHT / 4), self.collision_list, True, True),
+ Wall((SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4), self.collision_list, True, False),
+ Wall((SCREEN_WIDTH * 3 / 4, SCREEN_HEIGHT * 3 / 4), self.collision_list, False, True, False),
+ Wall((SCREEN_WIDTH * 3 / 4, SCREEN_HEIGHT / 4), self.collision_list, False, False, False),
+ Wall((SCREEN_WIDTH/-4, SCREEN_HEIGHT / 4), self.collision_list, False, False, False),
+ Wall((SCREEN_WIDTH / -4, SCREEN_HEIGHT * 5 /4), self.collision_list, False, False, False)
+ ]
+ self.can_move = True
+ self.tiles = [
+ Tile((SCREEN_WIDTH * 2 / 8, SCREEN_HEIGHT * 2 / 8), self.collision_list),
+ Tile((SCREEN_WIDTH * 3 / 8, SCREEN_HEIGHT * 3 / 8), self.collision_list),
+ Tile((SCREEN_WIDTH * 4 / 8, SCREEN_HEIGHT * 4 / 8), self.collision_list),
+ Tile((SCREEN_WIDTH * 5 / 8, SCREEN_HEIGHT * 5 / 8), self.collision_list),
+ ]
+ self.title = Label((SCREEN_WIDTH/2, SCREEN_HEIGHT/8), ["2048"], 50, Colour.ORANGE)
+ print(self.collision_list)
+
+
+ def update(self, delta):
+
+ if self.check_can_move():
+ self.can_move = True
+ else:
+ self.can_move = False
+
+ for tile in self.tiles:
+ tile.update(delta, self.can_move)
+ print(self.collision_list)
+
+ for wall in self.walls:
+ wall.update(delta)
+
+ self.title.update(delta)
+
+ def check_can_move(self):
+ for tile in self.tiles:
+ if not tile.can_move:
+ return False
+ return True
+ def draw(self, surf):
+ surf.fill(background_colour)
+
+ for thing in self.collision_list:
+ thing.draw(surf)
+
+ self.title.draw(surf)
+
+"""
--- /dev/null
+import pygame
+from pygametools.rectangle import RectangleLite, Rectangle
+
+
+class LabelLite:
+ horizontal_padding = 5
+ vertical_padding = 5
+ outline_thickness = 2
+
+ def __init__(self, pos, text, font_size, text_colour=(0, 0, 0), antialias=True, centered_x=True, centered_y=True,
+ filled=False, fill_colour=(150, 150, 150), outlined=False, outline_colour=(0, 0, 0), outline_radius=2,
+ font_file="C:/Users/admon/PycharmProjects/coding_club/assets/fonts/montserrat/MontserratRegular-BWBEl.ttf"
+ ):
+ self.position = pygame.Vector2(pos)
+
+ self.centered_x = centered_x
+ self.centered_y = centered_y
+ self.centering = pygame.Vector2(0, 0)
+
+ self.font_file = font_file
+ self.font = pygame.font.Font(self.font_file, font_size)
+ self.text_colour = text_colour
+ self.antialias = antialias
+ self.raw_text = text
+ self.text, self.width, self.height = self.create_text(self.raw_text)
+
+ self.rect = RectangleLite(pos, self.width, self.height, centered_x, centered_y, filled, fill_colour, outlined,
+ outline_colour, self.outline_thickness, outline_radius)
+
+ self.filled = filled
+ self.fill_colour = fill_colour
+
+ self.outlined = outlined
+ self.outline_colour = outline_colour
+
+ def create_text(self, text):
+ out = []
+ max_width = 0
+ max_height = 0
+
+ for line in text:
+ text_surface = self.font.render(line, self.antialias, self.text_colour)
+ width = text_surface.get_width()
+ height = text_surface.get_height()
+ out.append((text_surface, width, height))
+ max_width = max(max_width, width)
+ max_height += height
+ return out, max_width, max_height
+
+ def update(self, delta, new_text=None, x=None, y=None, width=None, height=None, colour=None, font_size=None):
+ self.text, self.width, self.height = self.create_text(self.raw_text)
+
+ if new_text is not None:
+ self.text, self.width, self.height = self.create_text(new_text)
+ if font_size is not None:
+ self.font = pygame.font.Font(self.font_file, font_size)
+ if new_text is not None:
+ self.text, self.width, self.height = self.create_text(new_text)
+ else:
+ self.text, self.width, self.height = self.create_text(self.raw_text)
+
+ if x is not None:
+ self.position.x = x
+ if y is not None:
+ self.position.y = y
+ if width is not None:
+ self.width = width
+ if height is not None:
+ self.height = height
+ if colour is not None:
+ self.fill_colour = colour
+ if self.centered_x:
+ self.centering.x = self.width / 2
+ if self.centered_y:
+ self.centering.y = self.height / 2
+
+ self.rect.update(delta, self.position.x, self.position.y, self.width, self.height, self.fill_colour)
+
+ def draw(self, surface):
+
+ self.rect.draw(surface)
+
+ prev_height = 0
+ for line in self.text:
+ text_surface, width, height = line
+ center = pygame.Vector2(0, 0)
+ if self.centered_x:
+ center.x = width / 2
+ if self.centered_y:
+ center.y = height / 2
+ surface.blit(text_surface, self.position - center + pygame.Vector2(0, prev_height))
+ prev_height += height
+
+
+class Label:
+ horizontal_padding = 5
+ vertical_padding = 5
+ outline_thickness = 4
+
+ def __init__(self, pos, text, font_size, text_colour=(0, 0, 0), antialias=True, centered_x=True, centered_y=True,
+ filled=False, fill_colour=(150, 150, 150), outlined=False, outline_colour=(0, 0, 0), outline_radius=2,
+ font_file="C:/Users/admon/PycharmProjects/coding_club/assets/fonts/montserrat/MontserratRegular-BWBEl.ttf",
+ path=None):
+
+ if path is None:
+ path = globals()
+ self.position = pygame.Vector2(pos)
+
+ self.centered_x = centered_x
+ self.centered_y = centered_y
+ self.centering = pygame.Vector2(0, 0)
+
+ self.font_file = font_file
+ self.font = pygame.font.Font(self.font_file, font_size)
+ self.text_colour = text_colour
+ self.antialias = antialias
+ self.raw_text = text
+
+ self.path = path
+ self.text, self.width, self.height = self.create_text(self.raw_text, self.path)
+
+ self.rect = RectangleLite(pos, self.width, self.height, centered_x, centered_y, filled, fill_colour, outlined,
+ outline_colour, self.outline_thickness, outline_radius)
+
+ self.filled = filled
+ self.fill_colour = fill_colour
+
+ self.outlined = outlined
+ self.outline_colour = outline_colour
+
+ def create_text(self, text, path):
+ """
+ [["string ", [f"{var=}", path]], ["string2"]]
+ string var
+ string2
+ """
+ out = []
+ max_width = 0
+ max_height = 0
+
+ for l in text:
+ line = ""
+ for thing in l:
+ if isinstance(thing, list):
+ if len(thing) == 1:
+ value = thing[0].split('=')[0]
+ line += str(path[value])
+ else:
+ val, path = thing
+ value = val.split('=')[0]
+ if "self." in value:
+ ind = value.index("self.")
+ value = value[ind + 5:]
+ line += eval("str(path.%s)" % value)
+
+ else:
+ line += thing
+
+ text_surface = self.font.render(line, self.antialias, self.text_colour)
+ width = text_surface.get_width()
+ height = text_surface.get_height()
+ out.append((text_surface, width, height))
+ max_width = max(max_width, width)
+ max_height += height
+ return out, max_width, max_height
+
+ def update(self, delta, new_text=None, x=None, y=None, width=None, height=None, colour=None, font_size=None):
+ self.text, self.width, self.height = self.create_text(self.raw_text, self.path)
+
+ if new_text is not None:
+ self.text, self.width, self.height = self.create_text(new_text, self.path)
+ if font_size is not None:
+ self.font = pygame.font.Font(self.font_file, font_size)
+ if new_text is not None:
+ self.text, self.width, self.height = self.create_text(new_text, self.path)
+ else:
+ self.text, self.width, self.height = self.create_text(self.raw_text, self.path)
+
+ if x is not None:
+ self.position.x = x
+ if y is not None:
+ self.position.y = y
+ if width is not None:
+ self.width = width
+ if height is not None:
+ self.height = height
+ if colour is not None:
+ self.fill_colour = colour
+ if self.centered_x:
+ self.centering.x = self.width / 2
+ if self.centered_y:
+ self.centering.y = self.height / 2
+
+ self.rect.update(delta, self.position.x, self.position.y, self.width, self.height, self.fill_colour)
+
+ def draw(self, surface):
+
+ self.rect.draw(surface)
+
+ prev_height = 0
+ for line in self.text:
+ text_surface, width, height = line
+ center = pygame.Vector2(0, 0)
+ if self.centered_x:
+ center.x = self.width/2 - width/2
+ # if self.centered_y:
+ # center.y = height / 2
+ surface.blit(text_surface, self.position + center - self.centering + pygame.Vector2(0, prev_height))
+ prev_height += height
+
+
+class ButtonLite(LabelLite):
+
+ def __init__(self, pos, text, font_size, func, text_colour=(0, 0, 0), antialias=True, centered_x=True,
+ centered_y=True,
+ filled=False, fill_colour=(150, 150, 150), outlined=False, outline_colour=(0, 0, 0), outline_radius=2,
+ font_file="C:/Users/admon/PycharmProjects/coding_club/assets/fonts/montserrat/MontserratRegular-BWBEl.ttf"):
+ super().__init__(pos, text, font_size, text_colour, antialias, centered_x, centered_y,
+ filled, fill_colour, outlined, outline_colour, outline_radius,
+ font_file)
+ self.func = func
+
+ def is_touching_mouse_pointer(self):
+ mouse_x, mouse_y = pygame.mouse.get_pos()
+ if (
+ self.position.x - self.centering.x - self.horizontal_padding <= mouse_x <= self.position.x + self.width - self.centering.x + self.horizontal_padding
+ and self.position.y - self.centering.y - self.vertical_padding <= mouse_y <= self.position.y + self.height - self.centering.y + self.vertical_padding):
+ return True
+ return False
+
+ def lighten(self):
+ self.fill_colour = (100, 100, 100)
+
+ def darken(self):
+ self.fill_colour = (150, 150, 150)
+
+ def update(self, delta, new_text=None, x=None, y=None, width=None, height=None, colour=None, font_size=None):
+ super().update(delta, new_text, x, y, width, height, colour, font_size)
+
+ if self.is_touching_mouse_pointer():
+ self.lighten()
+ if pygame.mouse.get_pressed(3)[0]:
+ self.run_function()
+ else:
+ self.darken()
+
+ def run_function(self):
+ return self.func()
+
+
+class Button(Label):
+
+ def __init__(self, pos, text, font_size, func, text_colour=(0, 0, 0), antialias=True, centered_x=True,
+ centered_y=True, filled=False, fill_colour=(150, 150, 150), outlined=False, outline_colour=(0, 0, 0), outline_radius=2,
+ font_file="C:/Users/admon/PycharmProjects/coding_club/assets/fonts/montserrat/MontserratRegular-BWBEl.ttf", path=None, cooldown=100):
+ super().__init__(pos, text, font_size, text_colour, antialias, centered_x, centered_y,
+ filled, fill_colour, outlined, outline_colour, outline_radius,
+ font_file, path)
+ self.func = func
+ self.cooldown = cooldown
+
+ def is_touching_mouse_pointer(self):
+ mouse_x, mouse_y = pygame.mouse.get_pos()
+ if (
+ self.position.x - self.centering.x - self.horizontal_padding <= mouse_x <= self.position.x + self.width - self.centering.x + self.horizontal_padding
+ and self.position.y - self.centering.y - self.vertical_padding <= mouse_y <= self.position.y + self.height - self.centering.y + self.vertical_padding):
+ return True
+ return False
+
+ def lighten(self):
+ self.fill_colour = (100, 100, 100)
+
+ def darken(self):
+ self.fill_colour = (150, 150, 150)
+
+ def update(self, delta, new_text=None, x=None, y=None, width=None, height=None, colour=None, font_size=None):
+ super().update(delta, new_text, x, y, width, height, colour, font_size)
+
+ if self.is_touching_mouse_pointer():
+ self.lighten()
+ if self.cooldown == 0 and pygame.mouse.get_pressed(3)[0]:
+ self.run_function()
+ else:
+ self.darken()
+
+ self.cooldown -= delta
+ self.cooldown = max(self.cooldown, 0)
+
+ def run_function(self):
+ return self.func()
--- /dev/null
+import pygame
+
+
+class RectangleLite:
+ horizontal_padding = 5
+ vertical_padding = 5
+
+ def __init__(self, pos, width, height, centered_x=True, centered_y=True,
+ filled=True, fill_colour=(255, 255, 255, 255),
+ outlined=True, outline_colour=(0, 0, 0, 255), outline_thickness=2, outline_radius=0):
+ x, y = pos
+ self.position = pygame.Vector2(x, y)
+ self.width = width
+ self.height = height
+
+ self.centered_x = centered_x
+ self.centered_y = centered_y
+ self.centering = pygame.Vector2(0, 0)
+
+ self.filled = filled
+ self.fill_colour = fill_colour
+
+ self.outlined = outlined
+ self.outline_colour = outline_colour
+ self.outline_thickness = outline_thickness
+ self.outline_radius = outline_radius
+
+ def update(self, delta, x=None, y=None, width=None, height=None, fill_colour=None):
+ if x is not None:
+ self.position.x = x
+ if y is not None:
+ self.position.y = y
+ if width is not None:
+ self.width = width
+ if height is not None:
+ self.height = height
+ if fill_colour is not None:
+ self.fill_colour = fill_colour
+
+ if self.centered_x:
+ self.centering.x = self.width / 2
+ if self.centered_y:
+ self.centering.y = self.height / 2
+
+ def get_collision_rect(self):
+ return pygame.Rect(
+ self.position - pygame.Vector2(self.horizontal_padding, self.vertical_padding) - self.centering,
+ (self.width + 2 * self.horizontal_padding, self.height + 2 * self.vertical_padding))
+
+ def draw(self, surface):
+ r = self.get_collision_rect()
+ if self.filled:
+ pygame.draw.rect(surface, self.fill_colour, r,border_radius=self.outline_radius)
+ if self.outlined:
+ pygame.draw.rect(surface, self.outline_colour, r, self.outline_thickness, self.outline_radius)
+
+
+class Rectangle(RectangleLite):
+
+ def __init__(self, pos, width, height, centered_x=True, centered_y=True,
+ filled=True, fill_colour=(255, 255, 255, 255),
+ outlined=True, outline_colour=(0, 0, 0, 255), outline_thickness=2, outline_radius=0,
+ horizontal_padding=12, vertical_padding=12):
+
+ super().__init__(pos, width, height, centered_x, centered_y, filled, fill_colour,
+ outlined, outline_colour, outline_thickness, outline_radius)
+
+ self.horizontal_padding = horizontal_padding
+ self.vertical_padding = vertical_padding
+
+ def update(self, delta, x=None, y=None, dx=0, dy=0, width=None, height=None, fill_colour=None,
+ centered_x=None, centered_y=None, outlined=None, outline_colour=None, outline_thickness=None,
+ outline_radius=None):
+ if x is not None:
+ self.position.x = x
+ if y is not None:
+ self.position.y = y
+ self.position += delta * pygame.Vector2(dx, dy)
+ if width is not None:
+ self.width = width
+ if height is not None:
+ self.height = height
+ if fill_colour is not None:
+ self.fill_colour = fill_colour
+ if centered_x is not None:
+ self.centered_x = centered_x
+ if centered_y is not None:
+ self.centered_y = centered_y
+ if outlined is not None:
+ self.outlined = outlined
+ if self.outlined:
+ if outline_colour is not None:
+ self.outline_colour = outline_colour
+ if outline_thickness is not None:
+ self.outline_thickness = outline_thickness
+ if outline_radius is not None:
+ self.outline_radius = outline_radius
+
+ if self.centered_x:
+ self.centering.x = self.width / 2
+ if self.centered_y:
+ self.centering.y = self.height / 2
+
+ def draw(self, surface):
+ s = pygame.Surface((surface.get_width(), surface.get_height()), pygame.SRCALPHA)
+ super().draw(s)
+ surface.blit(s, (0, 0))