Merge remote-tracking branch 'origin/MapNew' into entities_branch
# Conflicts: # src/entities/Collidable.py
This commit is contained in:
commit
169a931329
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
||||
/.idea/
|
||||
__pycache__
|
||||
/venv/
|
||||
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"window": {
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"name": "Adventure Survival"
|
||||
}
|
||||
}
|
BIN
data/images/entities/bush.png
Normal file
BIN
data/images/entities/bush.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
BIN
data/images/entities/player.jpg
Normal file
BIN
data/images/entities/player.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
data/images/entities/treasure.png
Normal file
BIN
data/images/entities/treasure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
BIN
data/images/terrain/floor.png
Normal file
BIN
data/images/terrain/floor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 506 B |
BIN
data/images/terrain/grass.png
Normal file
BIN
data/images/terrain/grass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 350 B |
BIN
data/images/terrain/wall.png
Normal file
BIN
data/images/terrain/wall.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 297 B |
20
data/mapdata/map.txt
Normal file
20
data/mapdata/map.txt
Normal file
@ -0,0 +1,20 @@
|
||||
....................
|
||||
..wwww..w...w...ww..
|
||||
..w......w.w...w..w.
|
||||
,,wwww,,,,w,,,,wwww,
|
||||
..w......w.w...w..w.
|
||||
..wwww..w...w..w..w.
|
||||
....................
|
||||
.w...w...ww...w..ww.
|
||||
.ww.ww..w..w..w..w..
|
||||
,w,w,w,,wwww,,w,,ww,
|
||||
.w...w..w.....w..w..
|
||||
.w...w..w.....ww.ww.
|
||||
....................
|
||||
..w...w...ww...ww...
|
||||
..ww.ww..w..w.w..w..
|
||||
,,w,w,w,,wwww,wwww,,
|
||||
..w...w..w..w.w.....
|
||||
..w...w..w..w.w.....
|
||||
....................
|
||||
....................
|
3
src/Run.py
Normal file
3
src/Run.py
Normal file
@ -0,0 +1,3 @@
|
||||
from game.Game import Game
|
||||
|
||||
game = Game()
|
@ -5,6 +5,27 @@ class Collidable(Entity):
|
||||
|
||||
def __init__(self, texture, pos, id):
|
||||
super().__init__(texture, pos, id)
|
||||
self.is_collidable = True
|
||||
|
||||
def check_for_collision(self, x_pos, y_pos):
|
||||
if self.pos[0] == x_pos:
|
||||
if self.pos[1] == y_pos:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
#col = Collidable(1, 1, 1)
|
||||
|
||||
|
||||
"""
|
||||
def interact_with_hp(self, hp, Statistics):
|
||||
Statistics.set_hp(hp)
|
||||
|
||||
def interact_with_hunger(self, hunger, Statistics):
|
||||
Statistics.set_hunger(hunger)
|
||||
|
||||
def interact_with_thirst(self, thirst, Statistics):
|
||||
Statistics.set_thirst(thirst)
|
||||
|
||||
def interact_with_stamina(self, stamina, Statistics):
|
||||
Statistics.set_stamina(stamina)"""
|
@ -1,6 +1,36 @@
|
||||
class Entity:
|
||||
from pathlib import Path
|
||||
import pygame
|
||||
|
||||
|
||||
class Entity(pygame.sprite.Sprite):
|
||||
nextId = 1
|
||||
|
||||
def __init__(self, texture, size, pos):
|
||||
super().__init__()
|
||||
self.image, self.rect = self.getTexture(texture, size)
|
||||
self.rect.x = pos[0]
|
||||
self.rect.y = pos[1]
|
||||
self.id = self.getId()
|
||||
|
||||
# A method to return auto-incremented ID
|
||||
def getId(self):
|
||||
id = Entity.nextId
|
||||
Entity.nextId += 1
|
||||
return id
|
||||
|
||||
# A method that returns image and rect from a file
|
||||
def getTexture(self, textureName, tileSize):
|
||||
texturesFolder = ""
|
||||
textureFile = ""
|
||||
try:
|
||||
texturesFolder = Path("../data/images/entities")
|
||||
textureFile = texturesFolder / textureName
|
||||
except IOError:
|
||||
print("Cannot load texture from " + texturesFolder + ". Exiting...")
|
||||
exit(1)
|
||||
image = pygame.image.load(str(textureFile)).convert_alpha()
|
||||
image = pygame.transform.scale(image, (tileSize, tileSize))
|
||||
rect = image.get_rect()
|
||||
return image, rect
|
||||
|
||||
|
||||
def __init__(self, texture, pos, id):
|
||||
self.texture = texture
|
||||
self.pos = pos
|
||||
self.id = id
|
||||
|
@ -1,6 +1,35 @@
|
||||
import src.entities.Entity as Entity
|
||||
from enum import Enum
|
||||
|
||||
from src.entities.Entity import Entity
|
||||
from src.entities.Statistics import Statistics
|
||||
import pygame
|
||||
|
||||
|
||||
class Player(Entity):
|
||||
def __init__(self):
|
||||
self.statistics
|
||||
def __init__(self, spawnpoint, size):
|
||||
super().__init__("player.jpg", size, spawnpoint)
|
||||
# Where the player is facing, 0 - north, 1
|
||||
self.rotation = Rotations.NORTH
|
||||
|
||||
# Move in a desired direction
|
||||
def move(self, rotation):
|
||||
if rotation.value == Rotations.NORTH.value:
|
||||
self.rect.y -= self.rect.w
|
||||
elif rotation.value == Rotations.EAST.value:
|
||||
self.rect.x += self.rect.w
|
||||
elif rotation.value == Rotations.SOUTH.value:
|
||||
self.rect.y += self.rect.w
|
||||
elif rotation.value == Rotations.WEST.value:
|
||||
self.rect.x -= self.rect.w
|
||||
|
||||
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
|
@ -42,5 +42,3 @@ class Statistics:
|
||||
else:
|
||||
self.hp = 100
|
||||
|
||||
|
||||
|
||||
|
@ -1,3 +1,55 @@
|
||||
import pygame
|
||||
|
||||
from src.entities.Player import Rotations
|
||||
|
||||
# Player can move every given milliseconds
|
||||
TIMEOUT = 100
|
||||
class EventManager:
|
||||
def __init__(self):
|
||||
self.player
|
||||
keyTimeout = 0
|
||||
|
||||
#self.game.map
|
||||
def __init__(self, gameObject, player):
|
||||
self.game = gameObject
|
||||
self.player = player
|
||||
self.keyTimer = pygame.time.Clock()
|
||||
# Player controls
|
||||
|
||||
# TODO
|
||||
def loadKeyboardSettings(self):
|
||||
pass
|
||||
|
||||
def handleEvents(self):
|
||||
pygame.event.pump()
|
||||
|
||||
keys = pygame.key.get_pressed()
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
self.game.running = False
|
||||
self.keyTimeout += self.keyTimer.tick()
|
||||
if self.keyTimeout >= TIMEOUT:
|
||||
self.handlePlayerControls(keys)
|
||||
self.keyTimeout = 0
|
||||
|
||||
def handlePlayerControls(self, keys):
|
||||
# Key names are temporary
|
||||
# TODO: Load key bindings from JSON
|
||||
|
||||
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):
|
||||
self.player.move(Rotations.NORTH)
|
||||
if keys[pygame.K_s]:
|
||||
self.player.rotate(Rotations.SOUTH)
|
||||
if not self.game.map.collision(self.player.rect.x, self.player.rect.y + self.player.rect.w):
|
||||
self.player.move(Rotations.SOUTH)
|
||||
if keys[pygame.K_d]:
|
||||
self.player.rotate(Rotations.EAST)
|
||||
if not self.game.map.collision(self.player.rect.x + self.player.rect.w, self.player.rect.y):
|
||||
self.player.move(Rotations.EAST)
|
||||
if keys[pygame.K_a]:
|
||||
self.player.rotate(Rotations.WEST)
|
||||
if not self.game.map.collision(self.player.rect.x - self.player.rect.w, self.player.rect.y):
|
||||
self.player.move(Rotations.WEST)
|
||||
|
||||
|
||||
|
||||
|
@ -1,6 +1,55 @@
|
||||
import pygame
|
||||
import json
|
||||
from pathlib import Path
|
||||
from os import path
|
||||
|
||||
from game.EventManager import EventManager
|
||||
from game.Screen import Screen
|
||||
from game.Map import Map
|
||||
from src.entities.Player import Player
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self):
|
||||
self.resolution
|
||||
self.windowName
|
||||
self.fps
|
||||
self.timer
|
||||
self.running = True
|
||||
print("Loading configuration...", end=" ")
|
||||
|
||||
try:
|
||||
configFolder = Path("../data/config/")
|
||||
configFile = configFolder / "mainConfig.json"
|
||||
|
||||
self.config = json.loads(configFile.read_text())
|
||||
|
||||
print("OK")
|
||||
except IOError:
|
||||
print("Error reading configuration file. Exiting...")
|
||||
exit(1)
|
||||
|
||||
print("Initializing pygame...", end=" ")
|
||||
pygame.init()
|
||||
self.spritesList = pygame.sprite.Group()
|
||||
|
||||
print("OK")
|
||||
print("Initializing screen, params: " + str(self.config["window"]) + "...", end=" ")
|
||||
|
||||
# Vertical rotation is unsupported due to UI layout
|
||||
if self.config["window"]["height"] > self.config["window"]["width"]:
|
||||
print("The screen cannot be in a vertical orientation. Exiting...")
|
||||
exit(1)
|
||||
|
||||
self.screen = Screen(self, self.config["window"])
|
||||
print("OK")
|
||||
|
||||
self.mapDataFolder = path.dirname("../data/mapdata/")
|
||||
self.map = Map(path.join(self.mapDataFolder, 'map.txt'), self.screen)
|
||||
self.player = Player((0, 0), self.map.tileSize)
|
||||
self.map.addEntity(self.player)
|
||||
self.eventManager = EventManager(self, self.player)
|
||||
|
||||
self.mainLoop()
|
||||
|
||||
def mainLoop(self):
|
||||
while self.running:
|
||||
self.eventManager.handleEvents()
|
||||
self.spritesList.draw(self.screen.pygameScreen)
|
||||
pygame.display.flip()
|
||||
|
@ -1,4 +1,51 @@
|
||||
import pygame
|
||||
from game.TerrainTile import TerrainTile
|
||||
from game.Screen import Locations
|
||||
|
||||
class Map:
|
||||
def __init__(self):
|
||||
self.terrain
|
||||
self.entites = []
|
||||
def __init__(self, filename, screen):
|
||||
self.screen = screen
|
||||
self.terrain = []
|
||||
self.collidableTerrain = []
|
||||
self.collidables = pygame.sprite.Group()
|
||||
|
||||
with open(filename, 'rt') as f:
|
||||
for line in f:
|
||||
self.terrain.append(line)
|
||||
|
||||
self.tileSize = int(self.screen.mapSize/20)
|
||||
|
||||
self.tileWidth = len(self.terrain[0])
|
||||
self.tileHeight = len(self.terrain)
|
||||
self.width = self.tileWidth * self.tileSize
|
||||
self.height = self.tileHeight * self.tileSize
|
||||
|
||||
self.terrainDraw()
|
||||
|
||||
def terrainDraw(self):
|
||||
for row, tiles in enumerate(self.terrain):
|
||||
for col, tile in enumerate(tiles):
|
||||
if tile == 'w':
|
||||
object = TerrainTile(col, row, 'wall.png', self.tileSize)
|
||||
self.screen.draw(object, Locations.MAP, 0, 0)
|
||||
self.collidables.add(object)
|
||||
elif tile == ',':
|
||||
self.screen.draw(TerrainTile(col, row, 'floor.png', self.tileSize), Locations.MAP, 0, 0)
|
||||
elif tile == '.':
|
||||
self.screen.draw(TerrainTile(col, row, 'grass.png', self.tileSize), Locations.MAP, 0, 0)
|
||||
|
||||
def addEntity(self, entity):
|
||||
self.screen.draw(entity, Locations.MAP, 0, 0)
|
||||
self.collidables.add(entity)
|
||||
|
||||
def removeSpriteFromMap(self, entity):
|
||||
if self.collidables.has(entity):
|
||||
self.collidables.remove(entity)
|
||||
self.screen.removeSprite(entity)
|
||||
|
||||
# add object to map.collidables list to be collidable
|
||||
def collision(self, x, y):
|
||||
for b in self.collidables:
|
||||
if b.rect.x == x and b.rect.y == y:
|
||||
return True
|
||||
return False
|
@ -1,4 +1,57 @@
|
||||
import math
|
||||
from enum import Enum
|
||||
|
||||
import pygame
|
||||
|
||||
# minimum UI width
|
||||
MARGIN = 300
|
||||
|
||||
|
||||
class Screen:
|
||||
def __init__(self):
|
||||
self.map
|
||||
self.ui
|
||||
def __init__(self, gameObject, windowConfig):
|
||||
self.gameObject = gameObject
|
||||
self.winX = windowConfig["width"]
|
||||
self.winY = windowConfig["height"]
|
||||
pygame.display.set_caption(windowConfig["name"])
|
||||
self.pygameScreen = pygame.display.set_mode((self.winX, self.winY))
|
||||
|
||||
# map is a square inside the screen
|
||||
self.mapSize = self.calculateMapDimensions()
|
||||
|
||||
# mapCoord is a top leftmost pixel
|
||||
self.mapCoord = math.floor((self.winX - self.mapSize) / 2)
|
||||
|
||||
# draw a white rect to resemble map
|
||||
pygame.draw.rect(self.pygameScreen, (255, 255, 255), [self.mapCoord, 0, self.mapSize, self.mapSize])
|
||||
|
||||
def calculateMapDimensions(self):
|
||||
result = 0
|
||||
expectedSize = self.winY
|
||||
|
||||
# when there's not enough space for the UI on the sides
|
||||
if self.winX - expectedSize < MARGIN:
|
||||
result = expectedSize - (MARGIN - (self.winX - expectedSize))
|
||||
else:
|
||||
result = expectedSize
|
||||
|
||||
return result
|
||||
|
||||
# method to draw a sprite. Location param specifies where to draw the item (Locations enum)
|
||||
def draw(self, sprite, location, posX, posY):
|
||||
sprite.rect.x += posX
|
||||
sprite.rect.y += posY
|
||||
if location.value is Locations.RIGHT_UI.value:
|
||||
sprite.rect.x += self.mapCoord + self.mapSize
|
||||
elif location.value == Locations.MAP.value:
|
||||
sprite.rect.x += self.mapCoord
|
||||
self.gameObject.spritesList.add(sprite)
|
||||
|
||||
def removeSprite(self, sprite):
|
||||
self.gameObject.spritesList.remove(sprite)
|
||||
|
||||
|
||||
# screen locations enum
|
||||
class Locations(Enum):
|
||||
RIGHT_UI = 1
|
||||
LEFT_UI = 2
|
||||
MAP = 3
|
||||
|
@ -1,3 +1,17 @@
|
||||
class TerrainTile:
|
||||
def __init__(self):
|
||||
self.tiles = []
|
||||
import pygame
|
||||
import os
|
||||
from os import path
|
||||
|
||||
|
||||
class TerrainTile(pygame.sprite.Sprite):
|
||||
def __init__(self, x, y, texture, tileSize):
|
||||
super().__init__()
|
||||
self.imagesFolder = path.dirname("../data/images/")
|
||||
self.terrainFolder = path.join(self.imagesFolder, 'terrain')
|
||||
self.image = pygame.image.load(os.path.join(self.terrainFolder, texture)).convert()
|
||||
self.image = pygame.transform.scale(self.image, (tileSize, tileSize))
|
||||
self.rect = self.image.get_rect()
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.rect.x = x * tileSize
|
||||
self.rect.y = y * tileSize
|
||||
|
@ -1,4 +1,66 @@
|
||||
import pygame
|
||||
|
||||
# Timer class for the game
|
||||
# We count time every second, an the time elapsed in game is 60 times the actual time
|
||||
# So, if we spend 60 seconds, we count that we spent 60 minutes.
|
||||
|
||||
# Day/night cycle parameters
|
||||
DAY_START = "7:00"
|
||||
NIGHT_START = "22:00"
|
||||
|
||||
|
||||
class Timer:
|
||||
def __init__(self):
|
||||
self.time
|
||||
self.cycle
|
||||
def __init__(self, startTime="12:00"):
|
||||
self.clock = pygame.time.Clock()
|
||||
|
||||
# Time in milliseconds updated every frame, starts counting from what we specify as a parameter
|
||||
self.timePassed = self.timeToMs(startTime)
|
||||
# You have to start the clock manually by calling startClock() method
|
||||
self.isStarted = False
|
||||
|
||||
def startClock(self):
|
||||
self.isStarted = True
|
||||
|
||||
def stopClock(self):
|
||||
self.isStarted = False
|
||||
|
||||
# Returns a string with formatted time
|
||||
def getPrettyTime(self):
|
||||
# 60 times faster than real time
|
||||
minutes = int(self.timePassed / 1000) % 60
|
||||
hours = int(self.timePassed / 60000) % 24
|
||||
|
||||
# Add 0's at the beginning if necessary
|
||||
prefixHr = ""
|
||||
prefixMin = ""
|
||||
if len(str(hours)) < 2:
|
||||
prefixHr = "0"
|
||||
if len(str(minutes)) < 2:
|
||||
prefixMin = "0"
|
||||
|
||||
# Return a formatted time
|
||||
return prefixHr + str(hours) + ":" + prefixMin + str(minutes)
|
||||
|
||||
# Returns true, if it's daytime
|
||||
def isItDay(self):
|
||||
if self.timeToMs(DAY_START) < self.timePassed < self.timeToMs(NIGHT_START):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# Called every frame to update the timer
|
||||
def updateTime(self, elapsed):
|
||||
# Only happens if the time is set to be running
|
||||
if self.isStarted:
|
||||
# Modulo, since we use the 24-hour cycle
|
||||
# In our case, the time loops every 24 minutes
|
||||
self.timePassed =(self.timePassed + elapsed) % 1440000
|
||||
|
||||
# Converts time as string to integer milliseconds
|
||||
def timeToMs(self, timeString):
|
||||
timeList = timeString.split(':')
|
||||
hours = timeList[0]
|
||||
minutes = timeList[1]
|
||||
return int(hours) * 60000 + int(minutes) * 1000
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user