Added more documentation

This commit is contained in:
Marcin Kostrzewski 2020-05-14 16:16:19 +02:00
parent 2b07e7394f
commit 085446ea93
10 changed files with 164 additions and 16 deletions

View File

@ -3,12 +3,24 @@ from src.entities.Entity import Entity
class Interactable(Entity): class Interactable(Entity):
def __init__(self, texture, size, pos, Statistics): def __init__(self, texture, size, pos, Statistics):
"""
Create an interactable entity, that can be interacted with.
:param texture: Path to a file with the texture
:param size: Size in px
:param pos: A tuple of coords (x,y)
:param Statistics: Outcome of the interaction
"""
super().__init__(texture, size, pos) super().__init__(texture, size, pos)
self.Statistics = Statistics self.Statistics = Statistics
def on_interaction(self, Player): def on_interaction(self, Player):
"""
Applies outcome to the Player
:param Player: Player object
"""
Player.statistics.set_hp(self.Statistics.hp) Player.statistics.set_hp(self.Statistics.hp)
Player.statistics.set_stamina(self.Statistics.stamina) Player.statistics.set_stamina(self.Statistics.stamina)
Player.statistics.set_thirst(self.Statistics.thirst) Player.statistics.set_thirst(self.Statistics.thirst)
Player.statistics.set_hunger(self.Statistics.hunger) Player.statistics.set_hunger(self.Statistics.hunger)

View File

@ -3,9 +3,22 @@ from src.entities.Interactable import Interactable
class Pickupable(Interactable): class Pickupable(Interactable):
def __init__(self, texture, size, pos, Statistics): def __init__(self, texture, size, pos, Statistics):
"""
Create a pickupable object. Pickupable object disappear when interacted with.
:param texture: Path to a file with the texture
:param size: Size in px
:param pos: Position tuple of (x,y)
:param Statistics: Outcome of the interaction
"""
super().__init__(texture, size, pos, Statistics) super().__init__(texture, size, pos, Statistics)
self.is_pickupable = True self.is_pickupable = True
def on_interaction(self, Player): def on_interaction(self, Player):
"""
Applies an outcome to the player and self-destructs.
:param Player: Player object
"""
super(Pickupable, self).on_interaction(Player) super(Pickupable, self).on_interaction(Player)
self.kill() self.kill()

View File

@ -10,7 +10,9 @@ from src.entities.Statistics import Statistics
class Player(Entity): class Player(Entity):
def __init__(self, spawnpoint, size): def __init__(self, spawnpoint, size):
""" """
:param spawnpoint: A tuple of relative coords Create a player.
:param spawnpoint: A tuple of coords (x,y)
:param size: The size in px :param size: The size in px
""" """
@ -35,7 +37,7 @@ class Player(Entity):
def applyWalkingFatigue(self): def applyWalkingFatigue(self):
""" """
Lowers player's statistics. Lowers player's statistics. Applied every few steps.
""" """
# looses hunger every 10 steps taken # looses hunger every 10 steps taken

View File

@ -1,12 +1,27 @@
class Statistics: class Statistics:
def __init__(self, hp, hunger, thirst, stamina): def __init__(self, hp, hunger, thirst, stamina):
"""
Create a statistic object. Used to determine some object state (usually player's).
:param hp: Health points
:param hunger: Hunger (it rises)
:param thirst: Thirst (also rises)
:param stamina: Stamina points that decrease.
"""
self.hp = hp self.hp = hp
self.hunger = hunger self.hunger = hunger
self.thirst = thirst self.thirst = thirst
self.stamina = stamina self.stamina = stamina
# methods that don't let the values pass below 0 and over 100 during change """
methods that don't let the values pass below 0 and over 100 during change
"""
def set_hp(self, hp_diff): def set_hp(self, hp_diff):
"""
Setter method for setting HP.
:param hp_diff: Difference as integer
"""
if 0 <= self.hp + hp_diff <= 100: if 0 <= self.hp + hp_diff <= 100:
self.hp = self.hp + hp_diff self.hp = self.hp + hp_diff
else: else:
@ -16,6 +31,11 @@ class Statistics:
self.hp = 100 self.hp = 100
def set_hunger(self, hunger_diff): def set_hunger(self, hunger_diff):
"""
Setter method for setting hunger points.
:param hunger_diff: Difference as integer
"""
if 0 <= self.hunger + hunger_diff <= 100: if 0 <= self.hunger + hunger_diff <= 100:
self.hunger = self.hunger + hunger_diff self.hunger = self.hunger + hunger_diff
else: else:
@ -25,6 +45,11 @@ class Statistics:
self.hunger = 100 self.hunger = 100
def set_thirst(self, thirst_diff): def set_thirst(self, thirst_diff):
"""
Setter method for setting thirst.
:param thirst_diff: Difference as integer
"""
if 0 <= self.thirst + thirst_diff <= 100: if 0 <= self.thirst + thirst_diff <= 100:
self.thirst = self.thirst + thirst_diff self.thirst = self.thirst + thirst_diff
else: else:
@ -34,6 +59,11 @@ class Statistics:
self.thirst = 100 self.thirst = 100
def set_stamina(self, stamina_diff): def set_stamina(self, stamina_diff):
"""
Setter method for setting stamina points.
:param stamina_diff: Difference as integer
"""
if 0 <= self.stamina + stamina_diff <= 100: if 0 <= self.stamina + stamina_diff <= 100:
self.stamina = self.stamina + stamina_diff self.stamina = self.stamina + stamina_diff
else: else:
@ -41,4 +71,3 @@ class Statistics:
self.stamina = 0 self.stamina = 0
else: else:
self.stamina = 100 self.stamina = 100

View File

@ -8,6 +8,12 @@ from src.entities.Enums import Movement
class EventManager: class EventManager:
def __init__(self, gameObject, player): def __init__(self, gameObject, player):
"""
Create a singleton EventManager to handle events like keyboard input.
:param gameObject: Game object
:param player: The player
"""
# TODO: Is this really necessary? # TODO: Is this really necessary?
self.game = gameObject self.game = gameObject
@ -21,6 +27,9 @@ class EventManager:
pass pass
def handleEvents(self): def handleEvents(self):
"""
Called every frame. Captures keyboard input, PyGame events, updates the time and UI.
"""
pygame.event.pump() pygame.event.pump()
if self.turnOff: if self.turnOff:
@ -51,6 +60,12 @@ class EventManager:
self.game.screen.ui.updateBarsBasedOnPlayerStats(self.player.statistics) self.game.screen.ui.updateBarsBasedOnPlayerStats(self.player.statistics)
def handleClickingOnCollidablesAndTerrains(self, pos): def handleClickingOnCollidablesAndTerrains(self, pos):
"""
Handles clicking on objects. Calls A* functions to move the player to the object that the mouse was
pointing to.
:param pos: Absolute object coords as a tuple of (x,y)
"""
# get a list of all collidables that are under the mouse cursor # get a list of all collidables that are under the mouse cursor
clicked_collidables = [s for s in self.game.map.collidables if s.rect.collidepoint(pos)] clicked_collidables = [s for s in self.game.map.collidables if s.rect.collidepoint(pos)]
@ -67,6 +82,11 @@ class EventManager:
print("NO TERRAIN FOUND UNDER CLICK") print("NO TERRAIN FOUND UNDER CLICK")
def handlePlayerControls(self, keys): def handlePlayerControls(self, keys):
"""
Handles player movement with the keyboard.
:param keys: A list of pressed keys
"""
# Key names are temporary # Key names are temporary
# TODO: Load key bindings from JSON # TODO: Load key bindings from JSON

View File

@ -14,6 +14,12 @@ from src.game.Timer import Timer
# Main Game class # Main Game class
class Game: class Game:
def __init__(self, filesPath): def __init__(self, filesPath):
"""
Game script initialization. Loads all files, creates screen, map, tiles, entities and a player.
Starts the main game loop at the end.
:param filesPath: Absolute path to the root of the gamefiles
"""
self.running = True self.running = True
print("Loading configuration...", end=" ") print("Loading configuration...", end=" ")
@ -75,6 +81,9 @@ class Game:
self.mainLoop() self.mainLoop()
def mainLoop(self): def mainLoop(self):
"""
Continuously running loop. Calls events, updates and draws everything.
"""
while self.running: while self.running:
# Tick the timers # Tick the timers
self.ingameTimer.updateTime(self.pgTimer.tick()) self.ingameTimer.updateTime(self.pgTimer.tick())

View File

@ -11,7 +11,6 @@ from src.entities.Pickupable import Pickupable
from src.entities.Statistics import Statistics from src.entities.Statistics import Statistics
# TODO: Map should determine entities' position
class Map: class Map:
def __init__(self, filename, screen): def __init__(self, filename, screen):
""" """
@ -161,7 +160,12 @@ class Map:
# TODO: REMOVE DONT ADD # TODO: REMOVE DONT ADD
def addEntity(self, entity, DONTADD=False): def addEntity(self, entity, DONTADD=False):
# TODO: This method should set entities coords """
Adds an entity to the map.
:param entity: Entity
:param DONTADD: ????
"""
self.screen.addSprite(entity, Locations.MAP) self.screen.addSprite(entity, Locations.MAP)
# dodajemy bo wszystkie entity są kolizyjne # dodajemy bo wszystkie entity są kolizyjne
self.collidables.add(entity) self.collidables.add(entity)

View File

@ -17,6 +17,13 @@ class Locations(Enum):
class Screen: class Screen:
def __init__(self, gameObject, windowConfig): def __init__(self, gameObject, windowConfig):
"""
Create a signleton screen object.
:param gameObject: Game object
:param windowConfig: A dictionary with window settings.
:except KeyError
"""
self.gameObject = gameObject self.gameObject = gameObject
self.winX = windowConfig["width"] self.winX = windowConfig["width"]
self.winY = windowConfig["height"] self.winY = windowConfig["height"]
@ -36,6 +43,12 @@ class Screen:
self.__initUi__() self.__initUi__()
def calculateMapDimensions(self): def calculateMapDimensions(self):
"""
Determines the map's dimmension based on the screen size.
The map will be shrunk down if there's not enough space for the UI.
:return: New map size as Int
"""
result = 0 result = 0
expectedSize = self.winY expectedSize = self.winY
@ -49,7 +62,8 @@ class Screen:
def addSprite(self, sprite, location): def addSprite(self, sprite, location):
""" """
Adds a sprite to a screen at a given location Adds a sprite to a screen at a given location.
:type location: Locations :type location: Locations
:param sprite: A sprite to add. :param sprite: A sprite to add.
:param location: Where should the sprite be displayed :param location: Where should the sprite be displayed
@ -66,6 +80,12 @@ class Screen:
self.gameObject.spritesList.add(sprite) self.gameObject.spritesList.add(sprite)
def getUiWidth(self, location: Locations): def getUiWidth(self, location: Locations):
"""
Gets the width of the UI.
:param location: Specify what UI size to return
:return: Width as Int
"""
if location is Locations.RIGHT_UI: if location is Locations.RIGHT_UI:
return self.winX - (self.mapCoord + self.mapSize) return self.winX - (self.mapCoord + self.mapSize)
elif location is Locations.LEFT_UI: elif location is Locations.LEFT_UI:
@ -75,6 +95,10 @@ class Screen:
# TODO: Move to game / events # TODO: Move to game / events
def __initUi__(self): def __initUi__(self):
"""
Creates entire UI
"""
self.ui = Ui(self.getUiWidth(Locations.RIGHT_UI), self.getUiWidth(Locations.LEFT_UI), self.winY, self.ui = Ui(self.getUiWidth(Locations.RIGHT_UI), self.getUiWidth(Locations.LEFT_UI), self.winY,
self.gameObject.ingameTimer) self.gameObject.ingameTimer)
self.addSprite(self.ui.timerTextView, Locations.LEFT_UI) self.addSprite(self.ui.timerTextView, Locations.LEFT_UI)

View File

@ -5,6 +5,15 @@ import pygame
# TODO: Relative coords # TODO: Relative coords
class TerrainTile(pygame.sprite.Sprite): class TerrainTile(pygame.sprite.Sprite):
def __init__(self, x, y, texture, tileSize, cost): def __init__(self, x, y, texture, tileSize, cost):
"""
Create a tile object.
:param x: Absolute X coord
:param y: Absolute Y coord
:param texture: Texture name found in data/images/terrain
:param tileSize: Size in px
:param cost: Cost of getting into this tile
"""
super().__init__() super().__init__()
terrainTexturesPath = Path("./data/images/terrain").resolve() terrainTexturesPath = Path("./data/images/terrain").resolve()
self.image = pygame.image.load(str(terrainTexturesPath / texture)).convert() self.image = pygame.image.load(str(terrainTexturesPath / texture)).convert()
@ -15,4 +24,3 @@ class TerrainTile(pygame.sprite.Sprite):
self.rect.x = x * tileSize self.rect.x = x * tileSize
self.rect.y = y * tileSize self.rect.y = y * tileSize
self.cost = cost self.cost = cost

View File

@ -11,6 +11,12 @@ NIGHT_START = "22:00"
class Timer: class Timer:
def __init__(self, startTime="12:00"): def __init__(self, startTime="12:00"):
"""
Create an in-game timer. It acts like a regular clock, but each minute is one second IRL.
The clock is not started by default. Call Timer.startClock() to make it running.
:param startTime: Starting time as a string in the format of HH:MM, where HH is hours value, MM is minutes value
"""
self.clock = pygame.time.Clock() self.clock = pygame.time.Clock()
# Time in milliseconds updated every frame, starts counting from what we specify as a parameter # Time in milliseconds updated every frame, starts counting from what we specify as a parameter
@ -19,13 +25,23 @@ class Timer:
self.isStarted = False self.isStarted = False
def startClock(self): def startClock(self):
"""
Start the clock.
"""
self.isStarted = True self.isStarted = True
def stopClock(self): def stopClock(self):
"""
Stop the clock.
"""
self.isStarted = False self.isStarted = False
# Returns a string with formatted time
def getPrettyTime(self): def getPrettyTime(self):
"""
Get a pretty looking time.
:return: String in the format of HH:MM
"""
# 60 times faster than real time # 60 times faster than real time
minutes = int(self.timePassed / 1000) % 60 minutes = int(self.timePassed / 1000) % 60
hours = int(self.timePassed / 60000) % 24 hours = int(self.timePassed / 60000) % 24
@ -41,26 +57,37 @@ class Timer:
# Return a formatted time # Return a formatted time
return prefixHr + str(hours) + ":" + prefixMin + str(minutes) return prefixHr + str(hours) + ":" + prefixMin + str(minutes)
# Returns true, if it's daytime
def isItDay(self): def isItDay(self):
"""
Get current cycle.
:return: True, if it's daytime, otherwise False.
"""
if self.timeToMs(DAY_START) < self.timePassed < self.timeToMs(NIGHT_START): if self.timeToMs(DAY_START) < self.timePassed < self.timeToMs(NIGHT_START):
return True return True
else: else:
return False return False
# Called every frame to update the timer
def updateTime(self, elapsed): def updateTime(self, elapsed):
"""
Should be called every frame to update the timer.
:param elapsed: Time of the frame
"""
# Only happens if the time is set to be running # Only happens if the time is set to be running
if self.isStarted: if self.isStarted:
# Modulo, since we use the 24-hour cycle # Modulo, since we use the 24-hour cycle
# In our case, the time loops every 24 minutes # In our case, the time loops every 24 minutes
self.timePassed = (self.timePassed + elapsed) % 1440000 self.timePassed = (self.timePassed + elapsed) % 1440000
# Converts time as string to integer milliseconds
def timeToMs(self, timeString): def timeToMs(self, timeString):
"""
Converts time from string format to milliseconds.
:param timeString: Time string in format HH:MM
:return: Milliseconds integer
"""
timeList = timeString.split(':') timeList = timeString.split(':')
hours = timeList[0] hours = timeList[0]
minutes = timeList[1] minutes = timeList[1]
return int(hours) * 60000 + int(minutes) * 1000 return int(hours) * 60000 + int(minutes) * 1000