Merge pull request 'dev-jakklu' (#5) from dev-jakklu into master

Reviewed-on: #5
This commit is contained in:
Jakub Klupieć 2021-05-08 22:22:46 +02:00
commit e8ba46148c
37 changed files with 441 additions and 53 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 807 B

View File

@ -0,0 +1,11 @@
import os
import pygame as pg
for i in os.listdir('.'):
try:
img = pg.image.load(i)
pg.image.save(img, i)
except:
print("Failed to fix image " + i)
print('Success. All images were fixed!')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,21 +1,10 @@
import pygame import pygame
from settings import SCREEN_WIDTH, SCREEN_HEIGHT from settings import SCREEN_WIDTH, SCREEN_HEIGHT
from survival import esper
from survival.camera import Camera from survival.camera import Camera
from survival.components.camera_target_component import CameraTargetComponent
from survival.components.input_component import InputComponent
from survival.components.movement_component import MovementComponent
from survival.components.position_component import PositionComponent
from survival.components.sprite_component import SpriteComponent
from survival.game_map import GameMap from survival.game_map import GameMap
from survival.player_generator import PlayerGenerator from survival.player_generator import PlayerGenerator
from survival.resource_generator import ResourceGenerator from survival.resource_generator import ResourceGenerator
from survival.systems.camera_system import CameraSystem
from survival.systems.collision_system import CollisionSystem
from survival.systems.draw_system import DrawSystem
from survival.systems.input_system import InputSystem
from survival.systems.movement_system import MovementSystem
from survival.world_generator import WorldGenerator from survival.world_generator import WorldGenerator
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -0,0 +1,18 @@
from survival.components.collision_component import CollisionComponent
from survival.components.inventory_component import InventoryComponent
from survival.components.position_component import PositionComponent
from survival.components.sprite_component import SpriteComponent
class BuildingGenerator:
def create_home(self, world, game_map):
home = world.create_entity()
pos = PositionComponent([32, 32], [32, 32])
world.add_component(home, pos)
world.add_component(home, InventoryComponent())
game_map.add_entity(home, pos)
sprite = SpriteComponent('stone.png')
sprite.set_scale(2)
world.add_component(home, sprite)
world.add_component(home, CollisionComponent())

View File

@ -0,0 +1,12 @@
from survival.enums import Direction
class DirectionChangeComponent:
def __init__(self, direction):
self.direction = direction
def rotate_left(self):
self.direction = Direction.rotate_left(self.direction)
def rotate_right(self):
self.direction = Direction.rotate_right(self.direction)

View File

@ -0,0 +1,24 @@
class InventoryComponent:
def __init__(self, maxitems):
self.maxitems = maxitems
self.items = {}
def addItem(self, item, count):
if item not in self.items:
self.items[item] = count
else:
self.items[item] = self.items[item] + count
if self.items[item] > self.maxitems:
self.items[item] = self.maxitems
def removeItem(self, item, count):
if self.items:
self.items[item] = self.items[item] - count
if self.items[item] < 0:
self.items[item] = 0
def hasItem(self, item):
if self.items[item] != 0:
return True
else:
return False

View File

@ -1,5 +1,4 @@
class MovingComponent: class MovingComponent:
def __init__(self, direction, target): def __init__(self):
self.direction = direction self.target = None
self.movement_target = target self.direction_vector = None
self.checked_collision = False

View File

@ -0,0 +1,5 @@
class PathfindingComponent:
def __init__(self, target_pos):
self.target_grid_pos = (int(target_pos[0] / 32), int(target_pos[1] / 32))
self.current_target = None
self.path = None

View File

@ -1,4 +1,15 @@
from survival.enums import Direction
class PositionComponent: class PositionComponent:
def __init__(self, pos, grid_pos): def __init__(self, pos, grid_pos, direction=Direction.DOWN):
self.position = pos self.position = pos
self.grid_position = grid_pos self.grid_position = grid_pos
self.direction = direction
self.direction_change_timer = 0
def rotate_left(self):
return Direction.rotate_left(self.direction)
def rotate_right(self):
return Direction.rotate_right(self.direction)

View File

@ -0,0 +1,20 @@
class TimeComponent:
def __init__(self, minute, hour, day, timer):
self.minute = minute
self.hour = hour
self.day = day
self.timer = timer
def add_time(self, minutes):
self.minute += minutes
if self.minute >= 60:
temp = self.minute - 60
self.hour += 1
if self.hour >= 24:
temp2 = self.hour - 24
self.day += 1
self.hour = temp2
self.minute = temp
def __str__(self):
return f'Day {self.day}, {self.hour}:{self.minute}'

34
survival/enums.py Normal file
View File

@ -0,0 +1,34 @@
from enum import IntEnum
class Direction(IntEnum):
DOWN = 0
LEFT = 1
UP = 2
RIGHT = 3
@staticmethod
def rotate_left(direction):
return Direction((direction - 1) % 4)
@staticmethod
def rotate_right(direction):
return Direction((direction + 1) % 4)
@staticmethod
def get_vector(direction):
if direction == Direction.UP:
return 0, -1
elif direction == Direction.DOWN:
return 0, 1
elif direction == Direction.LEFT:
return -1, 0
elif direction == Direction.RIGHT:
return 1, 0
@staticmethod
def from_vector(vector):
if vector[0] == 0:
return Direction.DOWN if vector[1] == 1 else Direction.UP
else:
return Direction.LEFT if vector[0] == -1 else Direction.RIGHT

View File

@ -1,6 +1,4 @@
from survival.components.position_component import PositionComponent
from survival.entity_layer import EntityLayer from survival.entity_layer import EntityLayer
from survival.player import Player
from survival.tile_layer import TileLayer from survival.tile_layer import TileLayer
@ -25,4 +23,4 @@ class GameMap:
self.entity_layer.remove_entity(pos) self.entity_layer.remove_entity(pos)
def is_colliding(self, pos): def is_colliding(self, pos):
return self.entity_layer.is_colliding(pos) return pos[0] < 0 or pos[0] >= self.width or pos[1] < 0 or pos[1] >= self.height or self.entity_layer.is_colliding(pos)

88
survival/graph_search.py Normal file
View File

@ -0,0 +1,88 @@
from enum import Enum
from survival import GameMap
from survival.components.position_component import PositionComponent
from survival.enums import Direction
class Action(Enum):
ROTATE_LEFT = 0
ROTATE_RIGHT = 1
MOVE = 2
class State:
def __init__(self, position, direction):
self.position = position
self.direction = direction
class Node:
def __init__(self, state: State, parent=None, action=None):
self.state = state
self.parent = parent
self.action = action
def get_moved_position(position, direction):
vector = Direction.get_vector(direction)
return position[0] + vector[0], position[1] + vector[1]
def get_states(state: State, game_map: GameMap):
states = list()
states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction))))
states.append((Action.ROTATE_RIGHT, State(state.position, state.direction.rotate_right(state.direction))))
target_state = get_moved_position(state.position, state.direction)
if not game_map.is_colliding(target_state):
states.append((Action.MOVE, State(target_state, state.direction)))
return states
def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
fringe = list()
explored = list()
explored_states = set()
fringe_states = set()
start = State(start.grid_position, start.direction)
fringe.append(Node(start))
fringe_states.add((tuple(start.position), start.direction))
while True:
# No solutions found
if not any(fringe):
return []
node = fringe.pop(0)
fringe_states.remove((tuple(node.state.position), node.state.direction))
# Check goal
if node.state.position == goal:
actions = [node.action]
parent = node.parent
while parent is not None:
if parent.action is not None:
actions.append(parent.action)
parent = parent.parent
actions.reverse()
return actions
explored.append(node)
explored_states.add((tuple(node.state.position), node.state.direction))
# Get all possible states
for state in get_states(node.state, game_map):
sub_state = (tuple(state[1].position), state[1].direction)
if sub_state not in fringe_states and sub_state not in explored_states:
new_node = Node(state=state[1],
parent=node,
action=state[0])
fringe.append(new_node)
fringe_states.add((tuple(new_node.state.position), new_node.state.direction))

58
survival/pathfinding.py Normal file
View File

@ -0,0 +1,58 @@
from collections import deque as Queue
def valid_neighbor(n, game_map, visited):
if n[0] < 0 or n[1] < 0 or n[0] >= game_map.width or n[1] >= game_map.height:
return False
if visited[n[0]][n[1]]:
return False
if game_map.is_colliding(n):
return False
return True
def breadth_first_search(game_map, start, target):
visited = [[False for _ in range(game_map.height)] for _ in range(game_map.width)]
q = Queue()
came_from = dict()
start = tuple(start)
target = tuple(target)
came_from[start] = None
q.append(start)
visited[start[0]][start[1]] = True
while len(q) > 0:
cell = q.popleft()
if cell == target:
break
neighbors = [
(cell[0] - 1, cell[1]),
(cell[0], cell[1] + 1),
(cell[0] + 1, cell[1]),
(cell[0], cell[1] - 1),
]
for neighbor in neighbors:
if valid_neighbor(neighbor, game_map, visited):
q.append(neighbor)
visited[neighbor[0]][neighbor[1]] = True
came_from[neighbor] = cell
path = list()
current = target
while current != start:
path.append(current)
if current not in came_from:
path.clear()
return path
current = came_from[current]
path.reverse()
return path

View File

@ -2,8 +2,6 @@ from random import randint
import pygame import pygame
from survival.image import Image
class Player: class Player:
def __init__(self): def __init__(self):

View File

@ -3,6 +3,7 @@ from survival.components.input_component import InputComponent
from survival.components.movement_component import MovementComponent from survival.components.movement_component import MovementComponent
from survival.components.position_component import PositionComponent from survival.components.position_component import PositionComponent
from survival.components.sprite_component import SpriteComponent from survival.components.sprite_component import SpriteComponent
from survival.components.time_component import TimeComponent
class PlayerGenerator: class PlayerGenerator:
@ -19,5 +20,6 @@ class PlayerGenerator:
sprite = SpriteComponent('stevenson.png') sprite = SpriteComponent('stevenson.png')
sprite.set_scale(1) sprite.set_scale(1)
world.add_component(player, sprite) world.add_component(player, sprite)
world.add_component(player, TimeComponent(0, 0, 0, 0))
return player return player

View File

@ -13,7 +13,7 @@ class ResourceGenerator:
def generate_resources(self): def generate_resources(self):
for x in range(RESOURCES_AMOUNT): for x in range(RESOURCES_AMOUNT):
obj = self.world.create_entity() obj = self.world.create_entity()
sprites = ['apple.png', 'water.png', 'wood.png', 'stone.png'] sprites = ['apple.png', 'water.png', 'wood.png']
empty_grid_pos = self.get_empty_grid_position() empty_grid_pos = self.get_empty_grid_position()
empty_pos = [empty_grid_pos[0] * 32, empty_grid_pos[1] * 32] empty_pos = [empty_grid_pos[0] * 32, empty_grid_pos[1] * 32]

View File

@ -1,3 +1,4 @@
SCREEN_WIDTH = 1920 SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080 SCREEN_HEIGHT = 1080
RESOURCES_AMOUNT = 300 RESOURCES_AMOUNT = 300
DIRECTION_CHANGE_DELAY = 200

View File

@ -1,6 +1,9 @@
import operator
from survival import esper from survival import esper
from survival.components.moving_component import MovingComponent from survival.components.moving_component import MovingComponent
from survival.components.position_component import PositionComponent from survival.components.position_component import PositionComponent
from survival.enums import Direction
class CollisionSystem(esper.Processor): class CollisionSystem(esper.Processor):
@ -9,17 +12,19 @@ class CollisionSystem(esper.Processor):
def process(self, dt): def process(self, dt):
for ent, (pos, moving) in self.world.get_components(PositionComponent, MovingComponent): for ent, (pos, moving) in self.world.get_components(PositionComponent, MovingComponent):
if moving.checked_collision: if moving.target is not None:
continue continue
moving.checked_collision = True moving.checked_collision = True
if self.check_collision(moving.movement_target): vector = Direction.get_vector(pos.direction)
moving.target = tuple(map(operator.add, vector, pos.grid_position))
moving.direction_vector = vector
if self.check_collision(moving.target):
self.world.remove_component(ent, MovingComponent) self.world.remove_component(ent, MovingComponent)
else: else:
self.map.move_entity(pos.grid_position, moving.movement_target) self.map.move_entity(pos.grid_position, moving.target)
pos.grid_position = moving.movement_target pos.grid_position = moving.target
def check_collision(self, pos): def check_collision(self, pos):
return self.map.is_colliding(pos) return self.map.is_colliding(pos)

View File

@ -0,0 +1,27 @@
from survival import esper
from survival.components.direction_component import DirectionChangeComponent
from survival.components.position_component import PositionComponent
from survival.settings import DIRECTION_CHANGE_DELAY
class DirectionSystem(esper.Processor):
def process(self, dt):
for ent, (pos, direction) in self.world.get_components(PositionComponent, DirectionChangeComponent):
if pos.direction_change_timer > 0:
pos.direction_change_timer -= dt
continue
dir_left = pos.rotate_left()
dir_right = pos.rotate_right()
pos.direction_change_timer = DIRECTION_CHANGE_DELAY
if dir_left == direction.direction:
pos.direction = dir_left
elif dir_right == direction.direction:
pos.direction = dir_right
else:
pos.direction = dir_left
continue
self.world.remove_component(ent, DirectionChangeComponent)

View File

@ -10,4 +10,5 @@ class DrawSystem(esper.Processor):
def process(self, dt): def process(self, dt):
for ent, (sprite, pos) in self.world.get_components(SpriteComponent, PositionComponent): for ent, (sprite, pos) in self.world.get_components(SpriteComponent, PositionComponent):
sprite.image.pos = pos.position sprite.image.pos = pos.position
sprite.image.origin = (32 * pos.direction.value, 0)
self.camera.draw(sprite.image) self.camera.draw(sprite.image)

View File

@ -1,26 +1,34 @@
import pygame import pygame
from survival import esper from survival import esper
from survival.components.direction_component import DirectionChangeComponent
from survival.components.input_component import InputComponent from survival.components.input_component import InputComponent
from survival.components.moving_component import MovingComponent from survival.components.moving_component import MovingComponent
from survival.components.pathfinding_component import PathfindingComponent
from survival.components.position_component import PositionComponent from survival.components.position_component import PositionComponent
class InputSystem(esper.Processor): class InputSystem(esper.Processor):
def __init__(self): def __init__(self, camera):
self.map = None self.camera = camera
def process(self, dt): def process(self, dt):
for ent, (inp, pos) in self.world.get_components(InputComponent, PositionComponent): for ent, (inp, pos) in self.world.get_components(InputComponent, PositionComponent):
keys = pygame.key.get_pressed() keys = pygame.key.get_pressed()
mouse = pygame.mouse.get_pressed(3)
if mouse[0] == 1:
pos = pygame.mouse.get_pos()
pos = (pos[0] - self.camera.camera.left, pos[1] - self.camera.camera.top)
if not self.world.has_component(ent, PathfindingComponent):
self.world.add_component(ent, PathfindingComponent(pos))
if self.world.has_component(ent, MovingComponent): if self.world.has_component(ent, MovingComponent):
continue continue
if keys[pygame.K_LEFT]: if keys[pygame.K_LEFT]:
self.world.add_component(ent, MovingComponent([-1, 0], [pos.grid_position[0] - 1, pos.grid_position[1]])) if not self.world.has_component(ent, DirectionChangeComponent):
self.world.add_component(ent, DirectionChangeComponent(pos.rotate_left()))
elif keys[pygame.K_RIGHT]: elif keys[pygame.K_RIGHT]:
self.world.add_component(ent, MovingComponent([1, 0], [pos.grid_position[0] + 1, pos.grid_position[1]])) if not self.world.has_component(ent, DirectionChangeComponent):
elif keys[pygame.K_DOWN]: self.world.add_component(ent, DirectionChangeComponent(pos.rotate_right()))
self.world.add_component(ent, MovingComponent([0, 1], [pos.grid_position[0], pos.grid_position[1] + 1]))
elif keys[pygame.K_UP]: elif keys[pygame.K_UP]:
self.world.add_component(ent, MovingComponent([0, -1], [pos.grid_position[0], pos.grid_position[1] - 1])) self.world.add_component(ent, MovingComponent())

View File

@ -13,23 +13,22 @@ class MovementSystem(esper.Processor):
for ent, (mov, pos, moving, sprite) in self.world.get_components(MovementComponent, PositionComponent, for ent, (mov, pos, moving, sprite) in self.world.get_components(MovementComponent, PositionComponent,
MovingComponent, MovingComponent,
SpriteComponent): SpriteComponent):
if moving.direction[0] != 0:
pos.position[0] += moving.direction[0] * mov.speed * dt / 100
if abs(moving.movement_target[0] * 32 - pos.position[0]) < 0.1 * mov.speed:
pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32]
self.world.remove_component(ent, MovingComponent)
else:
pos.position[1] += moving.direction[1] * mov.speed * dt / 100
if abs(pos.position[1] - moving.movement_target[1] * 32) < 0.1 * mov.speed:
pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32]
self.world.remove_component(ent, MovingComponent)
if moving.direction[0] == 1: pos.position[0] += moving.direction_vector[0] * mov.speed * dt / 100
sprite.image.origin = (96, 0) pos.position[1] += moving.direction_vector[1] * mov.speed * dt / 100
elif moving.direction[0] == -1:
sprite.image.origin = (64, 0)
elif moving.direction[1] == 1:
sprite.image.origin = (0, 0)
else:
sprite.image.origin = (32, 0)
if abs(moving.target[0] * 32 - pos.position[0]) < 0.1 * mov.speed and abs(
pos.position[1] - moving.target[1] * 32) < 0.1 * mov.speed:
pos.position = [moving.target[0] * 32, moving.target[1] * 32]
self.world.remove_component(ent, MovingComponent)
# if moving.direction[0] != 0:
# pos.position[0] += moving.direction[0] * mov.speed * dt / 100
# if abs(moving.movement_target[0] * 32 - pos.position[0]) < 0.1 * mov.speed:
# pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32]
# self.world.remove_component(ent, MovingComponent)
# else:
# pos.position[1] += moving.direction[1] * mov.speed * dt / 100
# if abs(pos.position[1] - moving.movement_target[1] * 32) < 0.1 * mov.speed:
# pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32]
# self.world.remove_component(ent, MovingComponent)

View File

@ -0,0 +1,62 @@
from survival import esper
from survival.components.direction_component import DirectionChangeComponent
from survival.components.movement_component import MovementComponent
from survival.components.moving_component import MovingComponent
from survival.components.position_component import PositionComponent
from survival.enums import Direction
from survival.graph_search import graph_search, Action
from survival.pathfinding import breadth_first_search
from survival.systems.input_system import PathfindingComponent
class PathfindingMovementSystem(esper.Processor):
def __init__(self, game_map):
self.game_map = game_map
pass
def process(self, dt):
for ent, (pos, pathfinding, movement) in self.world.get_components(PositionComponent, PathfindingComponent,
MovementComponent):
if pathfinding.path is None:
pathfinding.path = graph_search(self.game_map, pos, pathfinding.target_grid_pos)
if len(pathfinding.path) < 1:
self.world.remove_component(ent, PathfindingComponent)
continue
if self.world.has_component(ent, MovingComponent) or self.world.has_component(ent, DirectionChangeComponent):
continue
action = pathfinding.path.pop(0)
if action == Action.ROTATE_LEFT:
self.world.add_component(ent, DirectionChangeComponent(Direction.rotate_left(pos.direction)))
elif action == Action.ROTATE_RIGHT:
self.world.add_component(ent, DirectionChangeComponent(Direction.rotate_right(pos.direction)))
else:
self.world.add_component(ent, MovingComponent())
# if pathfinding.path is None:
# pathfinding.path = breadth_first_search(self.game_map, pos.grid_position, pathfinding.target_grid_pos)
#
# if len(pathfinding.path) < 1 and pathfinding.current_target is None:
# self.world.remove_component(ent, PathfindingComponent)
# continue
#
# if self.world.has_component(ent, MovingComponent):
# continue
#
# if pathfinding.current_target is None:
# target = pathfinding.path.pop(0)
# else:
# target = pathfinding.current_target
#
# vector = (target[0] - pos.grid_position[0], target[1] - pos.grid_position[1])
# direction = Direction.from_vector(vector)
# if direction != pos.direction:
# pathfinding.current_target = target
# self.world.add_component(ent, DirectionChangeComponent(direction))
# continue
#
# pathfinding.current_target = None
# self.world.add_component(ent, MovingComponent())

View File

@ -0,0 +1,12 @@
from survival import esper
from survival.components.time_component import TimeComponent
class TimeSystem(esper.Processor):
def process(self, dt):
for ent, time in self.world.get_component(TimeComponent):
time.timer += dt
if time.timer > 1000:
time.add_time(1)
time.timer = 0
print(time)

View File

@ -1,19 +1,25 @@
from survival import esper from survival import esper
from survival.systems.camera_system import CameraSystem from survival.systems.camera_system import CameraSystem
from survival.systems.collision_system import CollisionSystem from survival.systems.collision_system import CollisionSystem
from survival.systems.direction_system import DirectionSystem
from survival.systems.draw_system import DrawSystem from survival.systems.draw_system import DrawSystem
from survival.systems.input_system import InputSystem from survival.systems.input_system import InputSystem
from survival.systems.movement_system import MovementSystem from survival.systems.movement_system import MovementSystem
from survival.systems.pathfinding_movement_system import PathfindingMovementSystem
from survival.systems.time_system import TimeSystem
class WorldGenerator: class WorldGenerator:
def create_world(self, camera, game_map): def create_world(self, camera, game_map):
world = esper.World() world = esper.World()
world.add_processor(InputSystem()) world.add_processor(InputSystem(camera))
world.add_processor(CameraSystem(camera)) world.add_processor(CameraSystem(camera))
world.add_processor(MovementSystem(), priority=1) world.add_processor(MovementSystem(), priority=1)
world.add_processor(CollisionSystem(game_map), priority=2) world.add_processor(CollisionSystem(game_map), priority=2)
world.add_processor(DrawSystem(camera)) world.add_processor(DrawSystem(camera))
world.add_processor(TimeSystem())
world.add_processor(PathfindingMovementSystem(game_map), priority=3)
world.add_processor(DirectionSystem())
return world return world