diff --git a/data/images/entities/berry.jpg b/data/images/entities/berry.jpg deleted file mode 100644 index 375d44f..0000000 Binary files a/data/images/entities/berry.jpg and /dev/null differ diff --git a/data/images/entities/berry.png b/data/images/entities/berry.png new file mode 100644 index 0000000..253530f Binary files /dev/null and b/data/images/entities/berry.png differ diff --git a/data/images/entities/bush.png b/data/images/entities/bush.png index a59627d..f5a37c0 100644 Binary files a/data/images/entities/bush.png and b/data/images/entities/bush.png differ diff --git a/data/images/entities/cranberry.jpg b/data/images/entities/cranberry.jpg deleted file mode 100644 index 07d9fc8..0000000 Binary files a/data/images/entities/cranberry.jpg and /dev/null differ diff --git a/data/images/entities/cranberry.png b/data/images/entities/cranberry.png new file mode 100644 index 0000000..f8bb9fa Binary files /dev/null and b/data/images/entities/cranberry.png differ diff --git a/data/images/entities/mushroom.png b/data/images/entities/mushroom.png new file mode 100644 index 0000000..45a65b7 Binary files /dev/null and b/data/images/entities/mushroom.png differ diff --git a/data/images/entities/player.jpg b/data/images/entities/player.jpg deleted file mode 100644 index 1845ca8..0000000 Binary files a/data/images/entities/player.jpg and /dev/null differ diff --git a/data/images/entities/player.png b/data/images/entities/player.png new file mode 100644 index 0000000..f9da2de Binary files /dev/null and b/data/images/entities/player.png differ diff --git a/data/images/entities/rabbit.png b/data/images/entities/rabbit.png index 0a67152..d16d594 100644 Binary files a/data/images/entities/rabbit.png and b/data/images/entities/rabbit.png differ diff --git a/data/images/entities/rock.png b/data/images/entities/rock.png new file mode 100644 index 0000000..cf9822e Binary files /dev/null and b/data/images/entities/rock.png differ diff --git a/data/images/entities/treasure.png b/data/images/entities/treasure.png deleted file mode 100644 index a795552..0000000 Binary files a/data/images/entities/treasure.png and /dev/null differ diff --git a/data/images/terrain/water.jpg b/data/images/terrain/water.jpg deleted file mode 100644 index a7ecffe..0000000 Binary files a/data/images/terrain/water.jpg and /dev/null differ diff --git a/data/images/terrain/water.png b/data/images/terrain/water.png new file mode 100644 index 0000000..341c543 Binary files /dev/null and b/data/images/terrain/water.png differ diff --git a/data/mapdata/mapEntities.json b/data/mapdata/mapEntities.json index cc221d2..a590e65 100644 --- a/data/mapdata/mapEntities.json +++ b/data/mapdata/mapEntities.json @@ -13,11 +13,12 @@ "hunger" : 20 } }, + { "name" : "cranberry", "position" : { "x": 7, - "y": 1 + "y": 3 }, "isPickupable" : true, "effect" : { @@ -26,5 +27,52 @@ "thirst" : 2, "hunger" : 10 } + }, + + { + "name" : "rabbit", + "position" : { + "x": 12, + "y": 16 + }, + "isPickupable" : true, + "effect" : { + "hp": 30, + "stamina": -5, + "thirst": -5, + "hunger": 40 + } + }, + + { + "name" : "bush", + "position" : { + "x": 15, + "y": 9 + }, + "isPickupable" : false + }, + { + "name" : "rock", + "position" : { + "x": 13, + "y": 2 + }, + "isPickupable" : false + }, + + { + "name" : "mushroom", + "position" : { + "x": 14, + "y": 3 + }, + "isPickupable": true, + "effect" : { + "hp": -50, + "stamina": 0, + "thirst": 0, + "hunger": 25 + } } ] \ No newline at end of file diff --git a/src/entities/Entity.py b/src/entities/Entity.py index 857f664..fee6d5f 100644 --- a/src/entities/Entity.py +++ b/src/entities/Entity.py @@ -8,6 +8,7 @@ class Entity(pygame.sprite.Sprite): def __init__(self, texture, size, pos): super().__init__() self.image, self.rect = self.getTexture(texture, size) + self.image.set_colorkey((255, 255, 255)) self.rect.x = pos[0] self.rect.y = pos[1] self.id = self.getId() diff --git a/src/entities/Player.py b/src/entities/Player.py index c47fc35..57d8207 100644 --- a/src/entities/Player.py +++ b/src/entities/Player.py @@ -6,13 +6,21 @@ import pygame class Player(Entity): + statistics: Statistics + def __init__(self, spawnpoint, size): - super().__init__("player.jpg", size, (spawnpoint[0] * size, spawnpoint[1] * size)) + super().__init__("player.png", size, (spawnpoint[0] * size, spawnpoint[1] * size)) # Where the player is facing, 0 - north, 1 self.rotation = Rotations.NORTH + self.statistics = Statistics(100, 100, 0, 100) + # How many steps has the player taken through its lifetime + self.movePoints = 0 # Move in a desired direction def move(self, rotation): + self.movePoints += 1 + # Player gets tired aswell! + self.applyFatigue() if rotation.value == Rotations.NORTH.value: self.rect.y -= self.rect.w elif rotation.value == Rotations.EAST.value: @@ -22,14 +30,57 @@ class Player(Entity): elif rotation.value == Rotations.WEST.value: self.rect.x -= self.rect.w + def applyFatigue(self): + # looses hunger every 10 steps taken + if self.movePoints % 10 == 0: + self.statistics.set_hunger(-10) + # gets more thirsty every 5 steps + if self.movePoints % 5 == 0: + self.statistics.set_thirst(10) + # gets tired every step + self.statistics.set_stamina(-2) + + def getFacingCoord(self): + if self.rotation == Rotations.NORTH: + return self.rect.x, self.rect.y - (self.rect.h) + elif self.rotation == Rotations.SOUTH: + return self.rect.x, self.rect.y + (self.rect.h) + elif self.rotation == Rotations.EAST: + return self.rect.x + (self.rect.h), self.rect.y + elif self.rotation == Rotations.WEST: + return self.rect.x - (self.rect.h), self.rect.y + + # Returns given statistic + def getStatistic(self, stat): + if stat.value == StatisticNames.HP: + return self.statistics.hp + elif stat.value == StatisticNames.HUNGER: + return self.statistics.hunger + elif stat.value == StatisticNames.THIRST: + return self.statistics.thirst + elif stat.value == StatisticNames.STAMINA: + return self.statistics.stamina + return None + + def getStatistics(self): + return self.statistics + def rotate(self, rotation): # If the player is not facing given direction, it will not move the first time, it will only get rotated if self.rotation.value != rotation.value: self.image = pygame.transform.rotate(self.image, ((self.rotation.value - rotation.value) * 90)) self.rotation = rotation + class Rotations(Enum): NORTH = 0 EAST = 1 SOUTH = 2 - WEST = 3 \ No newline at end of file + WEST = 3 + + +class StatisticNames(Enum): + HP = 0 + STAMINA = 1 + HUNGER = 2 + THIRST = 3 \ No newline at end of file diff --git a/src/entities/Statistics.py b/src/entities/Statistics.py index c506eee..4c0a507 100644 --- a/src/entities/Statistics.py +++ b/src/entities/Statistics.py @@ -16,29 +16,29 @@ class Statistics: self.hp = 100 def set_hunger(self, hunger_diff): - if 0 <= self.hp + hunger_diff <= 100: - self.hp = self.hp + hunger_diff + if 0 <= self.hunger + hunger_diff <= 100: + self.hunger = self.hunger + hunger_diff else: - if self.hp + hunger_diff <= 0: - self.hp = 0 + if self.hunger + hunger_diff <= 0: + self.hunger = 0 else: - self.hp = 100 + self.hunger = 100 def set_thirst(self, thirst_diff): - if 0 <= self.hp + thirst_diff <= 100: - self.hp = self.hp + thirst_diff + if 0 <= self.thirst + thirst_diff <= 100: + self.thirst = self.thirst + thirst_diff else: - if self.hp + thirst_diff <= 0: - self.hp = 0 + if self.thirst + thirst_diff <= 0: + self.thirst = 0 else: - self.hp = 100 + self.thirst = 100 def set_stamina(self, stamina_diff): - if 0 <= self.hp + stamina_diff <= 100: - self.hp = self.hp + stamina_diff + if 0 <= self.stamina + stamina_diff <= 100: + self.stamina = self.stamina + stamina_diff else: - if self.hp + stamina_diff <= 0: - self.hp = 0 + if self.stamina + stamina_diff <= 0: + self.stamina = 0 else: - self.hp = 100 + self.stamina = 100 diff --git a/src/game/EventManager.py b/src/game/EventManager.py index 540b60d..d8a3bca 100644 --- a/src/game/EventManager.py +++ b/src/game/EventManager.py @@ -1,5 +1,6 @@ import pygame +from src.entities.Pickupable import Pickupable from src.entities.Player import Rotations # Player can move every given milliseconds @@ -21,19 +22,28 @@ class EventManager: def handleEvents(self): pygame.event.pump() + self.game.screen.ui.timerTextView.changeText(self.game.ingameTimer.getPrettyTime()) + keys = pygame.key.get_pressed() for event in pygame.event.get(): if event.type == pygame.QUIT: self.game.running = False + self.game.screen.ui.updateBasedOnPygameEvent(event) self.keyTimeout += self.keyTimer.tick() if self.keyTimeout >= TIMEOUT: self.handlePlayerControls(keys) self.keyTimeout = 0 + self.game.screen.ui.updateBasedOnPlayerStats(self.player.statistics) + def handlePlayerControls(self, keys): # Key names are temporary # TODO: Load key bindings from JSON - + if keys[pygame.K_SPACE]: + object = self.game.map.getEntityOnCoord(self.player.getFacingCoord()) + if type(object) is Pickupable: + object.on_pickup(self.player) + self.game.map.removeSpriteFromMap(object) if keys[pygame.K_w]: self.player.rotate(Rotations.NORTH) if not self.game.map.collision(self.player.rect.x, self.player.rect.y - self.player.rect.w): diff --git a/src/game/Game.py b/src/game/Game.py index b0573ab..ddc760a 100644 --- a/src/game/Game.py +++ b/src/game/Game.py @@ -6,7 +6,11 @@ from os import path from game.EventManager import EventManager from game.Screen import Screen from game.Map import Map + +from src.entities.Pickupable import Pickupable from src.entities.Player import Player +from src.entities.Statistics import Statistics +from src.game.Timer import Timer class Game: @@ -37,6 +41,11 @@ class Game: print("The screen cannot be in a vertical orientation. Exiting...") exit(1) + # Initialize timers + self.pgTimer = pygame.time.Clock() + self.ingameTimer = Timer() + self.ingameTimer.startClock() + self.screen = Screen(self, self.config["window"]) print("OK") @@ -50,6 +59,8 @@ class Game: def mainLoop(self): while self.running: + # Update ingame clock + self.ingameTimer.updateTime(self.pgTimer.tick()) self.eventManager.handleEvents() self.spritesList.draw(self.screen.pygameScreen) pygame.display.flip() diff --git a/src/game/Map.py b/src/game/Map.py index a8408d7..7ed08bf 100644 --- a/src/game/Map.py +++ b/src/game/Map.py @@ -1,8 +1,8 @@ import json import pygame -from game.TerrainTile import TerrainTile -from game.Screen import Locations +from src.game.TerrainTile import TerrainTile +from src.game.Screen import Locations from src.entities.Entity import Entity from src.entities.Pickupable import Pickupable @@ -42,7 +42,7 @@ class Map: for entity in entityListJson: try: if entity["isPickupable"]: - actualEntities.append(Pickupable(entity["name"] + ".jpg", + actualEntities.append(Pickupable(entity["name"] + ".png", self.tileSize, (entity["position"]["x"] * self.tileSize, entity["position"]["y"] * self.tileSize), Statistics(entity["effect"]["hp"], @@ -50,7 +50,7 @@ class Map: entity["effect"]["thirst"], entity["effect"]["stamina"]))) else: - actualEntities.append(Entity(entity["name"], + actualEntities.append(Entity(entity["name"] + ".png", self.tileSize, (entity["position"]["x"] * self.tileSize, entity["position"]["y"] * self.tileSize))) except KeyError: @@ -67,7 +67,7 @@ class Map: elif tile == '.': self.screen.draw(TerrainTile(col, row, 'grass.png', self.tileSize), Locations.MAP, 0, 0) elif tile == 'x': - object = TerrainTile(col, row, 'water.jpg', self.tileSize) + object = TerrainTile(col, row, 'water.png', self.tileSize) self.screen.draw(object, Locations.MAP, 0, 0) self.collidables.add(object) elif tile == 'w': @@ -75,6 +75,13 @@ class Map: self.screen.draw(object, Locations.MAP, 0, 0) self.collidables.add(object) + def getEntityOnCoord(self, coord): + result = None + for entity in self.collidables: + if entity.rect.x == coord[0] and entity.rect.y == coord[1]: + result = entity + return result + def addEntity(self, entity): self.screen.draw(entity, Locations.MAP, 0, 0) self.collidables.add(entity) diff --git a/src/game/Screen.py b/src/game/Screen.py index 0534af3..112c4e6 100644 --- a/src/game/Screen.py +++ b/src/game/Screen.py @@ -2,11 +2,19 @@ import math from enum import Enum import pygame +from src.ui.Ui import Ui # minimum UI width MARGIN = 300 +# screen locations enum +class Locations(Enum): + RIGHT_UI = 1 + LEFT_UI = 2 + MAP = 3 + + class Screen: def __init__(self, gameObject, windowConfig): self.gameObject = gameObject @@ -24,6 +32,8 @@ class Screen: # draw a white rect to resemble map pygame.draw.rect(self.pygameScreen, (255, 255, 255), [self.mapCoord, 0, self.mapSize, self.mapSize]) + self.__initUi__() + def calculateMapDimensions(self): result = 0 expectedSize = self.winY @@ -49,9 +59,25 @@ class Screen: def removeSprite(self, sprite): self.gameObject.spritesList.remove(sprite) + def getUiWidth(self, location: Locations): + if location is Locations.RIGHT_UI: + return self.winX - (self.mapCoord + self.mapSize) + elif location is Locations.LEFT_UI: + return self.mapCoord + elif location is Locations.MAP: + return self.mapSize -# screen locations enum -class Locations(Enum): - RIGHT_UI = 1 - LEFT_UI = 2 - MAP = 3 + def __initUi__(self): + self.ui = Ui(self.getUiWidth(Locations.RIGHT_UI), self.getUiWidth(Locations.LEFT_UI), self.winY, + self.gameObject.ingameTimer) + self.draw(self.ui.timerTextView, Locations.LEFT_UI, 0, 0) + self.draw(self.ui.isDayTextView, Locations.LEFT_UI, 0, 0) + self.draw(self.ui.console, Locations.LEFT_UI, 0, 0) + self.draw(self.ui.healthTextView, Locations.RIGHT_UI, 0, 0) + self.draw(self.ui.healthBar, Locations.RIGHT_UI, 0, 0) + self.draw(self.ui.hungerTextView, Locations.RIGHT_UI, 0, 0) + self.draw(self.ui.hungerBar, Locations.RIGHT_UI, 0, 0) + self.draw(self.ui.staminaTextView, Locations.RIGHT_UI, 0, 0) + self.draw(self.ui.staminaBar, Locations.RIGHT_UI, 0, 0) + self.draw(self.ui.thirstTextView, Locations.RIGHT_UI, 0, 0) + self.draw(self.ui.thirstBar, Locations.RIGHT_UI, 0, 0) diff --git a/src/ui/Ui.py b/src/ui/Ui.py index 289b0d4..98d26ee 100644 --- a/src/ui/Ui.py +++ b/src/ui/Ui.py @@ -1,3 +1,101 @@ +from enum import Enum + +import pygame + +from src.entities.Statistics import Statistics +from src.ui.UiBar import UiBar +from src.ui.UiConsole import UiConsole +from src.ui.UiText import UiText + + class Ui(): - def __init__(self): - self.elements = [] + def __init__(self, rightUiWidth, leftUiWidth, screenHeight, timer, font=None): + self.elements = pygame.sprite.Group() + + self.leftUiWidth = leftUiWidth + self.rightUiWidth = rightUiWidth + self.screenHeight = screenHeight + + self.barHeight = 25 + + if font is None: + font = pygame.font.Font(None, self.barHeight) + self.font = font + + self.timer = timer + self.timerTextView = UiText(pygame.Rect(0, 0, leftUiWidth, self.barHeight), font=self.font, + text=timer.getPrettyTime(), textColor=Colors.WHITE.value, + backgroundColor=Colors.GRAY.value) + self.isDayTextView = UiText( + pygame.Rect(0, self.timerTextView.rect.y + self.barHeight, leftUiWidth, self.barHeight), text="Day", + font=self.font, backgroundColor=Colors.GRAY.value, textColor=Colors.WHITE.value) + + self.healthTextView = UiText(pygame.Rect(0, 0, rightUiWidth, self.barHeight), text="Health points", + font=self.font, textColor=Colors.WHITE.value) + self.healthBar = UiBar( + pygame.Rect(0, self.healthTextView.rect.y + self.barHeight, rightUiWidth, self.barHeight)) + + self.hungerTextView = UiText( + pygame.Rect(0, self.healthBar.rect.y + self.barHeight, rightUiWidth, self.barHeight), + text="Hunger", font=self.font, textColor=Colors.WHITE.value) + self.hungerBar = UiBar( + pygame.Rect(0, self.hungerTextView.rect.y + self.barHeight, rightUiWidth, self.barHeight), + initialFilledPercent=0, + filledBarColor=Colors.YELLOW.value) + + self.staminaTextView = UiText( + pygame.Rect(0, self.hungerBar.rect.y + self.barHeight, rightUiWidth, self.barHeight), text="Stamina", + font=self.font, textColor=Colors.WHITE.value) + self.staminaBar = UiBar( + pygame.Rect(0, self.staminaTextView.rect.y + self.barHeight, rightUiWidth, self.barHeight), + filledBarColor=Colors.GREEN.value) + + self.thirstTextView = UiText( + pygame.Rect(0, self.staminaBar.rect.y + self.barHeight, rightUiWidth, self.barHeight), text="Thirst", + font=self.font, textColor=Colors.WHITE.value) + self.thirstBar = UiBar( + pygame.Rect(0, self.thirstTextView.rect.y + self.barHeight, rightUiWidth, self.barHeight), + initialFilledPercent=0, + filledBarColor=Colors.BLUE.value) + + self.console = UiConsole(pygame.Rect(0, self.timerTextView.rect.h + self.isDayTextView.rect.h, leftUiWidth, + screenHeight - self.timerTextView.rect.h - self.isDayTextView.rect.h), + font=self.font) + + def updateBasedOnPlayerStats(self, statistics: Statistics): + consoleLines = [] + if self.healthBar.value != statistics.hp: + self.healthBar.updateFill(statistics.hp) + consoleLines.append("Health: " + str(statistics.hp)) + + if self.hungerBar.value != statistics.hunger: + self.hungerBar.updateFill(statistics.hunger) + consoleLines.append("Hunger: " + str(statistics.hunger)) + + if self.staminaBar.value != statistics.stamina: + self.staminaBar.updateFill(statistics.stamina) + consoleLines.append("Stamina: " + str(statistics.stamina)) + + if self.thirstBar.value != statistics.thirst: + self.thirstBar.updateFill(statistics.thirst) + consoleLines.append("Stamina: " + str(statistics.thirst)) + + if len(consoleLines) > 0: + self.console.addLinesToConsoleAndScrollToDisplayThem(consoleLines) + + def updateBasedOnPygameEvent(self, event: pygame.event): + if event.type == pygame.MOUSEBUTTONDOWN: + console = self.console + if event.button == 4: + console.writeConsoleLines(console.topWrittenLineInd + 1) + elif event.button == 5: + console.writeConsoleLines(console.topWrittenLineInd - 1) + + +class Colors(Enum): + RED = (255, 0, 0) + GREEN = (0, 255, 0) + BLUE = (0, 0, 255) + YELLOW = (255, 255, 0) + WHITE = (255, 255, 255) + GRAY = (125, 125, 125) diff --git a/src/ui/UiBar.py b/src/ui/UiBar.py index a8dac52..8475395 100644 --- a/src/ui/UiBar.py +++ b/src/ui/UiBar.py @@ -1,5 +1,32 @@ -import src.ui.UiElement as UiElement +import pygame + +from src.ui.UiElement import UiElement class UiBar(UiElement): - pass + def __init__(self, rect: pygame.Rect, initialFilledPercent=100, filledBarColor=(255, 0, 0), emptyBarColor=(0, 0, 0), + outlineColor=(75, 75, 75), outlineThickness=10): + super().__init__(rect) + self.filledPercent = initialFilledPercent / 100 + self.emptyBarColor = emptyBarColor + self.barColor = filledBarColor + self.outlineColor = outlineColor + self.outlineThickness = outlineThickness + self.filledBarColor = filledBarColor + self.value = initialFilledPercent + + self.__genBar__() + + def __genBar__(self): + self.image = pygame.Surface((self.rect.width, self.rect.height)) + filledPartRect = pygame.rect.Rect(self.outlineThickness / 2, self.outlineThickness / 2, + (self.rect.width - self.outlineThickness) * self.filledPercent, + self.rect.height - self.outlineThickness) + self.image.fill(self.filledBarColor, filledPartRect) + pygame.draw.rect(self.image, self.outlineColor, pygame.rect.Rect(0, 0, self.rect.width, self.rect.height), + self.outlineThickness) + + def updateFill(self, filledPercent): + self.filledPercent = filledPercent / 100 + self.value = filledPercent + self.__genBar__() diff --git a/src/ui/UiButton.py b/src/ui/UiButton.py index 7c64ced..0587557 100644 --- a/src/ui/UiButton.py +++ b/src/ui/UiButton.py @@ -1,5 +1,67 @@ -import src.ui.UiElement as UiElement +from enum import Enum +import pygame + +from src.ui.UiElement import UiElement class UiButton(UiElement): - pass + + def __init__(self, rect: pygame.Rect, notClickedBtnColor=(125, 125, 125), clickedBtnColor=(255, 255, 255), + text="Click", textColor=(0, 0, 0), font=None, functionsToInvokeWhenClicked=[]): + """ + :type functionsToInvokeWhenClicked : list of tuple(function, args*), args are function arguments + """ + super().__init__(rect) + if font is None: + font = pygame.font.Font(None, 25) + self.font = font + self.textColor = textColor + self.clickedBtnColor = clickedBtnColor + self.notClickedBtnColor = notClickedBtnColor + self.text = text + self.__initBtnImages__() + + self.beingClicked = False + self.image = self._images[0] + + self.functionsToInvokeWhenClicked = [] + + self.functionsToInvokeWhenClicked.extend(functionsToInvokeWhenClicked) + + def eventHandler(self, event): + # change selected color if rectangle clicked + if event.type == pygame.MOUSEBUTTONDOWN: + if event.button == 1: + if self.rect.collidepoint(event.pos): # is mouse over button + self.image = self._images[ButtonImages.CLICKING_IMAGE.value] + self.beingClicked = True + for func, *args in self.functionsToInvokeWhenClicked: + func(*args) + elif event.type == pygame.MOUSEBUTTONUP and self.beingClicked: + if event.button == 1: + self.beingClicked = False + self.image = self._images[ButtonImages.DEFAULT_IMAGE.value] + + def __initBtnImages__(self): + self._images = [ + pygame.Surface((self.rect.width, self.rect.height)), + pygame.Surface((self.rect.width, self.rect.height)), + ] + self._images[ButtonImages.DEFAULT_IMAGE.value].fill(self.notClickedBtnColor) + self._images[ButtonImages.CLICKING_IMAGE.value].fill(self.clickedBtnColor) + self.textSurface = self.font.render(self.text, False, (0, 0, 0)) + self.textSurfaceDest = (self.rect.centerx - (self.textSurface.get_width() / 2), + self.rect.centery - (self.textSurface.get_height() / 2)) + self._images[0].blit(self.textSurface, self.textSurfaceDest) + self._images[1].blit(self.textSurface, self.textSurfaceDest) + + def addFuncToInvoke(self, tupleOfFuncAndArgs): + """ + :type tupleOfFuncAndArgs: tuple(function, *args) + """ + self.functionsToInvokeWhenClicked.append(tupleOfFuncAndArgs) + + +class ButtonImages(Enum): + DEFAULT_IMAGE = 0 + CLICKING_IMAGE = 1 diff --git a/src/ui/UiConsole.py b/src/ui/UiConsole.py new file mode 100644 index 0000000..d1afc11 --- /dev/null +++ b/src/ui/UiConsole.py @@ -0,0 +1,79 @@ +import pygame + +from src.ui.UiElement import UiElement + + +class UiConsole(UiElement): + def __init__(self, rect: pygame.Rect, bgColor=(125, 125, 125), textColor=(255, 255, 255), font=None): + super().__init__(rect) + self.textColor = textColor + + if font is None: + font = pygame.font.Font(None, 25) + self.font = font + self.bgColor = bgColor + + self.image = pygame.Surface((rect.width, rect.height)) + self.image.fill(bgColor) + + self.consoleWidth = self.image.get_width() + self.linesImagesCount = 0 + + self.consoleLines = [] + self.linesCount = 0 + self.topWrittenLineInd = 0 + + self.linesImages = [] + self.lineHeight = font.render("sampleText", False, textColor).get_height() + + self.maxLines = int(self.image.get_height() / self.lineHeight) + + self.addLinesToConsole(["Hello from console!"]) + self.writeConsoleLines() + + def writeConsoleLines(self, startingLineInd=0): + self.image.fill(self.bgColor) + if startingLineInd < 0: + startingLineInd = 0 + elif startingLineInd >= self.linesImagesCount: + startingLineInd = self.linesImagesCount - 1 + self.topWrittenLineInd = startingLineInd + writtenLines = 0 + for i in range(startingLineInd, min(self.maxLines + startingLineInd, self.linesImagesCount)): + self.image.blit(self.linesImages[i], (0, writtenLines * self.lineHeight)) + writtenLines += 1 + + def addLinesToConsole(self, linesToAdd): + for line in linesToAdd: + self.consoleLines.append(line) + self.linesCount += 1 + + row = pygame.Surface((self.consoleWidth, self.lineHeight)) + row.fill(self.bgColor) + + howMuchRowIsFilled = 0 + words = line.split(' ') + for word in words: + wordImage = self.font.render(' ' + word, False, self.textColor) + if howMuchRowIsFilled + wordImage.get_width() <= self.consoleWidth: + row.blit(wordImage, (howMuchRowIsFilled, 0)) + howMuchRowIsFilled += wordImage.get_width() + else: + self.linesImages.append(row) + self.linesImagesCount += 1 + row = pygame.Surface((self.consoleWidth, self.lineHeight)) + row.fill(self.bgColor) + howMuchRowIsFilled = 0 + + row.blit(wordImage, (howMuchRowIsFilled, 0)) + howMuchRowIsFilled += wordImage.get_width() + + self.linesImages.append(row) + self.linesImagesCount += 1 + + def addLinesToConsoleAndScrollToDisplayThem(self, linesToAdd): + self.addLinesToConsole(linesToAdd) + ind = 0 + if self.linesImagesCount > self.maxLines: + ind = self.linesImagesCount - self.maxLines + self.writeConsoleLines(ind) diff --git a/src/ui/UiElement.py b/src/ui/UiElement.py index a398959..6ec18b0 100644 --- a/src/ui/UiElement.py +++ b/src/ui/UiElement.py @@ -1,2 +1,7 @@ -class UiElement: - pass +import pygame + + +class UiElement(pygame.sprite.Sprite): + def __init__(self, rect): + super().__init__() + self.rect = rect diff --git a/src/ui/UiImage.py b/src/ui/UiImage.py index d8cd485..6dbb5fc 100644 --- a/src/ui/UiImage.py +++ b/src/ui/UiImage.py @@ -1,5 +1,10 @@ -import src.ui.UiElement as UiElement +from src.ui.UiElement import UiElement +import pygame class UiImage(UiElement): - pass + def __init__(self, rect: pygame.Rect, image: pygame.Surface): + super().__init__(rect) + self.image = pygame.transform.scale(image, (rect.width, rect.height)) + + diff --git a/src/ui/UiText.py b/src/ui/UiText.py index 2200d35..676faf7 100644 --- a/src/ui/UiText.py +++ b/src/ui/UiText.py @@ -1,5 +1,34 @@ -import src.ui.UiElement as UiElement +from typing import Tuple + +import pygame + +from src.ui.UiElement import UiElement class UiText(UiElement): - pass + def __init__(self, rect: pygame.Rect, text: str, font: pygame.font.Font = None, + textColor=(0, 0, 0), antialias: bool = False, + backgroundColor=None): + super().__init__(rect) + + self.backgroundColor = backgroundColor + self.antialias = antialias + self.textColor = textColor + self.text = text + if font is None: + font = pygame.font.Font(None, 12) + self.font = font + + self.image = pygame.Surface((rect.w, rect.h)) + if backgroundColor is not None: + self.image.fill(backgroundColor) + wordImage = self.font.render(text, antialias, textColor) + self.image.blit(wordImage, (0, 0)) + + def changeText(self, newText): + self.text = newText + + if self.backgroundColor is not None: + self.image.fill(self.backgroundColor) + wordImage = self.font.render(self.text, self.antialias, self.textColor) + self.image.blit(wordImage, (0, 0))