development #8

Merged
s452701 merged 6 commits from development into master 2021-05-24 13:20:01 +02:00
20 changed files with 173 additions and 33 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
assets/chest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 B

BIN
assets/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/ui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

View File

@ -2,14 +2,19 @@ import pygame
from settings import SCREEN_WIDTH, SCREEN_HEIGHT from settings import SCREEN_WIDTH, SCREEN_HEIGHT
from survival.camera import Camera from survival.camera import Camera
from survival.components.inventory_component import InventoryComponent
from survival.game_map import GameMap from survival.game_map import GameMap
from survival.generators.building_generator import BuildingGenerator
from survival.generators.player_generator import PlayerGenerator from survival.generators.player_generator import PlayerGenerator
from survival.generators.resource_generator import ResourceGenerator from survival.generators.resource_generator import ResourceGenerator
from survival.generators.world_generator import WorldGenerator from survival.generators.world_generator import WorldGenerator
from survival.systems.draw_system import DrawSystem
if __name__ == '__main__': if __name__ == '__main__':
pygame.init() pygame.init()
pygame.font.init()
win = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) win = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("AI Project") pygame.display.set_caption("AI Project")
@ -20,8 +25,10 @@ if __name__ == '__main__':
world = WorldGenerator().create_world(camera, game_map) world = WorldGenerator().create_world(camera, game_map)
player = PlayerGenerator().create_player(world, game_map) player = PlayerGenerator().create_player(world, game_map)
world.get_processor(DrawSystem).initialize_interface(world.component_for_entity(player, InventoryComponent))
building = BuildingGenerator().create_home(world, game_map)
ResourceGenerator(world, game_map).generate_resources() ResourceGenerator(world, game_map).generate_resources(player)
run = True run = True

View File

@ -1,10 +1,11 @@
import random import random
from typing import List
from survival.tile import Tile from survival.tile import Tile
class BiomePreset: class BiomePreset:
def __init__(self, name, min_height: float, min_moisture: float, min_heat: float, tiles: list[Tile]): def __init__(self, name, min_height: float, min_moisture: float, min_heat: float, tiles: List[Tile]):
self.name = name self.name = name
self.min_height = min_height self.min_height = min_height
self.min_moisture = min_moisture self.min_moisture = min_moisture

View File

@ -0,0 +1,15 @@
from functools import partial
class OnCollisionComponent:
def __init__(self, callbacks=None):
if callbacks is None:
callbacks = []
self.callbacks = callbacks
def callAll(self):
for func in self.callbacks:
func()
def addCallback(self, fn, **kwargs):
self.callbacks.append(partial(fn, **kwargs))

View File

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

View File

@ -0,0 +1,3 @@
class ResourceComponent:
def __init__(self, resource_type):
self.resource_type = resource_type

View File

@ -18,5 +18,8 @@ class EntityLayer:
def remove_entity(self, pos): def remove_entity(self, pos):
self.tiles[pos[1]][pos[0]] = None self.tiles[pos[1]][pos[0]] = None
def get_entity(self, pos) -> int:
return self.tiles[pos[1]][pos[0]]
def is_colliding(self, pos): def is_colliding(self, pos):
return self.tiles[pos[1]][pos[0]] is not None return self.tiles[pos[1]][pos[0]] is not None

View File

@ -22,6 +22,9 @@ class GameMap:
def remove_entity(self, pos): def remove_entity(self, pos):
self.entity_layer.remove_entity(pos) self.entity_layer.remove_entity(pos)
def get_entity(self, pos) -> int:
return self.entity_layer.get_entity(pos)
def is_colliding(self, pos): def is_colliding(self, 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) 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)

View File

@ -5,14 +5,25 @@ from survival.components.sprite_component import SpriteComponent
class BuildingGenerator: class BuildingGenerator:
def create_home(self, world, game_map): def create_home(self, world, game_map, position = [10,10]):
home = world.create_entity() home = world.create_entity()
pos = PositionComponent([32, 32], [32, 32]) home_pos = PositionComponent([position[0]*32, position[1]*32], position)
world.add_component(home, pos) world.add_component(home, home_pos)
game_map.add_entity(home, home_pos)
world.add_component(home, InventoryComponent()) world.add_component(home, InventoryComponent())
sprite = SpriteComponent('home.png')
game_map.add_entity(home, pos)
sprite = SpriteComponent('stone.png')
sprite.set_scale(2)
world.add_component(home, sprite) world.add_component(home, sprite)
world.add_component(home, CollisionComponent()) world.add_component(home, CollisionComponent())
for x_pos in [-1, 1]:
chest = world.create_entity()
chest_pos = PositionComponent(
[(position[0]+x_pos)*32, position[1]*32],
[position[0]+x_pos, position[1]]
)
world.add_component(chest, chest_pos)
game_map.add_entity(chest, chest_pos)
world.add_component(chest, InventoryComponent())
sprite = SpriteComponent('chest.png')
world.add_component(chest, sprite)
world.add_component(chest, CollisionComponent())

View File

@ -1,5 +1,7 @@
from survival.components.OnCollisionComponent import OnCollisionComponent
from survival.components.camera_target_component import CameraTargetComponent from survival.components.camera_target_component import CameraTargetComponent
from survival.components.input_component import InputComponent from survival.components.input_component import InputComponent
from survival.components.inventory_component import InventoryComponent
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
@ -14,6 +16,8 @@ class PlayerGenerator:
world.add_component(player, pos) world.add_component(player, pos)
world.add_component(player, MovementComponent()) world.add_component(player, MovementComponent())
world.add_component(player, InputComponent()) world.add_component(player, InputComponent())
world.add_component(player, OnCollisionComponent())
world.add_component(player, InventoryComponent())
camera_target = CameraTargetComponent(pos) camera_target = CameraTargetComponent(pos)
world.add_component(player, camera_target) world.add_component(player, camera_target)
game_map.add_entity(player, pos) game_map.add_entity(player, pos)

View File

@ -1,27 +1,47 @@
import random import random
from enum import Enum
from survival import GameMap
from survival.components.OnCollisionComponent import OnCollisionComponent
from survival.components.inventory_component import InventoryComponent
from survival.components.position_component import PositionComponent from survival.components.position_component import PositionComponent
from survival.components.resource_component import ResourceComponent
from survival.components.sprite_component import SpriteComponent from survival.components.sprite_component import SpriteComponent
from survival.esper import World
from survival.settings import RESOURCES_AMOUNT from survival.settings import RESOURCES_AMOUNT
class ResourceType(Enum):
FOOD = 1
WATER = 2
WOOD = 3
class ResourceGenerator: class ResourceGenerator:
def __init__(self, world, game_map): def __init__(self, world, game_map):
self.world = world self.world = world
self.map = game_map self.map = game_map
def generate_resources(self): def generate_resources(self, player: int):
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'] sprites = {
ResourceType.FOOD: 'apple.png',
ResourceType.WATER: 'water.png',
ResourceType.WOOD: '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]
pos = PositionComponent(empty_pos, empty_grid_pos) pos = PositionComponent(empty_pos, empty_grid_pos)
sprite = SpriteComponent(random.choice(sprites)) resource_type = random.choice(list(ResourceType))
sprite = SpriteComponent(sprites[resource_type])
col = OnCollisionComponent()
col.addCallback(self.remove_resource, world=self.world, game_map=self.map, entity=obj, player=player)
self.world.add_component(obj, pos) self.world.add_component(obj, pos)
self.world.add_component(obj, sprite) self.world.add_component(obj, sprite)
self.world.add_component(obj, col)
self.world.add_component(obj, ResourceComponent(resource_type))
self.map.add_entity(obj, pos) self.map.add_entity(obj, pos)
def get_empty_grid_position(self): def get_empty_grid_position(self):
@ -29,3 +49,12 @@ class ResourceGenerator:
while self.map.is_colliding(free_pos): while self.map.is_colliding(free_pos):
free_pos = [random.randrange(self.map.width), random.randrange(self.map.height)] free_pos = [random.randrange(self.map.width), random.randrange(self.map.height)]
return free_pos return free_pos
@staticmethod
def remove_resource(world: World, game_map: GameMap, entity: int, player: int):
pos = world.component_for_entity(entity, PositionComponent)
resource = world.component_for_entity(entity, ResourceComponent)
inventory = world.component_for_entity(player, InventoryComponent)
inventory.add_item(resource.resource_type, 1)
game_map.remove_entity(pos.grid_position)
world.delete_entity(entity, immediate=True)

View File

@ -1,4 +1,5 @@
import random import random
from typing import List
from survival.biomes.biome_data import BiomeData from survival.biomes.biome_data import BiomeData
from survival.biomes.biome_preset import BiomePreset from survival.biomes.biome_preset import BiomePreset
@ -34,7 +35,7 @@ class TileGenerator:
return Tile(origin=tile.origin, cost=tile.cost) return Tile(origin=tile.origin, cost=tile.cost)
@staticmethod @staticmethod
def generate_random_tiles(width: int, height: int) -> list[list[Tile]]: def generate_random_tiles(width: int, height: int) -> List[List[Tile]]:
return [[TileGenerator.get_random_tile() for _ in range(width)] for _ in range(height)] return [[TileGenerator.get_random_tile() for _ in range(width)] for _ in range(height)]
@staticmethod @staticmethod

View File

@ -1,5 +1,6 @@
from enum import Enum from enum import Enum
from queue import PriorityQueue from queue import PriorityQueue
from typing import Tuple, List
from survival import GameMap from survival import GameMap
from survival.components.position_component import PositionComponent from survival.components.position_component import PositionComponent
@ -13,7 +14,7 @@ class Action(Enum):
class State: class State:
def __init__(self, position: tuple[int, int], direction: Direction): def __init__(self, position: Tuple[int, int], direction: Direction):
self.position = position self.position = position
self.direction = direction self.direction = direction
@ -32,12 +33,12 @@ class Node:
return self.cost == other.cost return self.cost == other.cost
def get_moved_position(position: tuple[int, int], direction: Direction): def get_moved_position(position: Tuple[int, int], direction: Direction):
vector = Direction.get_vector(direction) vector = Direction.get_vector(direction)
return position[0] + vector[0], position[1] + vector[1] return position[0] + vector[0], position[1] + vector[1]
def get_states(state: State, game_map: GameMap) -> list[tuple[Action, State, int]]: def get_states(state: State, game_map: GameMap) -> List[Tuple[Action, State, int]]:
states = list() states = list()
states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction)), 1)) states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction)), 1))
@ -63,7 +64,7 @@ def build_path(node: Node):
return actions return actions
def heuristic(new_node: Node, goal: tuple[int, int]): def heuristic(new_node: Node, goal: Tuple[int, int]):
return abs(new_node.state.position[0] - goal[0]) + abs(new_node.state.position[1] - goal[1]) return abs(new_node.state.position[0] - goal[0]) + abs(new_node.state.position[1] - goal[1])

View File

@ -4,12 +4,12 @@ import pygame
class Image: class Image:
def __init__(self, filename): def __init__(self, filename, pos=(0, 0), scale=1):
self.texture = pygame.image.load(os.path.join('..', 'assets', filename)).convert_alpha() self.texture = pygame.image.load(os.path.join('..', 'assets', filename)).convert_alpha()
self.image = self.texture self.image = self.texture
self.origin = (0, 0) self.origin = (0, 0)
self.pos = (0, 0) self.pos = pos
self.scale = 1 self.set_scale(scale)
def set_scale(self, scale): def set_scale(self, scale):
self.image = pygame.transform.scale(self.texture, self.image = pygame.transform.scale(self.texture,
@ -20,3 +20,8 @@ class Image:
window.blit(self.image, camera.apply(self.pos), window.blit(self.image, camera.apply(self.pos),
pygame.Rect(self.origin[0] * self.scale, self.origin[1] * self.scale, 32 * self.scale, pygame.Rect(self.origin[0] * self.scale, self.origin[1] * self.scale, 32 * self.scale,
32 * self.scale)) 32 * self.scale))
def draw_static(self, window):
window.blit(self.image, self.pos,
pygame.Rect(self.origin[0] * self.scale, self.origin[1] * self.scale, 32 * self.scale,
32 * self.scale))

View File

@ -1,6 +1,7 @@
import operator import operator
from survival import esper from survival import esper
from survival.components.OnCollisionComponent import OnCollisionComponent
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 from survival.enums import Direction
@ -11,7 +12,8 @@ class CollisionSystem(esper.Processor):
self.map = game_map self.map = game_map
def process(self, dt): def process(self, dt):
for ent, (pos, moving) in self.world.get_components(PositionComponent, MovingComponent): for ent, (pos, moving, onCol) in self.world.get_components(PositionComponent, MovingComponent,
OnCollisionComponent):
if moving.target is not None: if moving.target is not None:
continue continue
@ -22,6 +24,15 @@ class CollisionSystem(esper.Processor):
moving.direction_vector = vector moving.direction_vector = vector
if self.check_collision(moving.target): if self.check_collision(moving.target):
self.world.remove_component(ent, MovingComponent) self.world.remove_component(ent, MovingComponent)
onCol.callAll()
colliding_object: int = self.map.get_entity(moving.target)
if colliding_object is None or not self.world.entity_exists(colliding_object):
continue
if self.world.has_component(colliding_object, OnCollisionComponent):
self.world.component_for_entity(colliding_object, OnCollisionComponent).callAll()
else: else:
self.map.move_entity(pos.grid_position, moving.target) self.map.move_entity(pos.grid_position, moving.target)
pos.grid_position = moving.target pos.grid_position = moving.target

View File

@ -1,14 +1,21 @@
from survival import esper from survival import esper
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.user_interface import UserInterface
class DrawSystem(esper.Processor): class DrawSystem(esper.Processor):
def __init__(self, camera): def __init__(self, camera):
self.camera = camera self.camera = camera
self.ui = None
def initialize_interface(self, inventory):
self.ui = UserInterface(self.camera.window, inventory)
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) sprite.image.origin = (32 * pos.direction.value, 0)
self.camera.draw(sprite.image) self.camera.draw(sprite.image)
self.ui.update()
self.ui.draw()

View File

@ -0,0 +1,42 @@
import pygame.font
from survival import settings
from survival.components.inventory_component import InventoryComponent
from survival.generators.resource_generator import ResourceType
from survival.image import Image
class UserInterface:
def __init__(self, window, inventory: InventoryComponent):
self.width = settings.SCREEN_WIDTH
self.height = settings.SCREEN_HEIGHT
self.window = window
self.pos = (self.width - 240, 50)
self.scale = 2
self.inventory = inventory
self.images = {
ResourceType.FOOD: Image('apple.png', self.pos, self.scale),
ResourceType.WATER: Image('water.png', self.pos, self.scale),
ResourceType.WOOD: Image('wood.png', self.pos, self.scale)
}
i = 0
for key, value in self.images.items():
self.images[key].pos = (self.pos[0] + i * 32 * self.scale + 8 * i, self.pos[1])
i += 1
self.slot_image = Image('ui.png', self.pos, scale=2)
self.font = pygame.font.SysFont('Comic Sans MS', 20)
def update(self):
pass
def draw(self):
for key, image in self.images.items():
items_count = self.inventory.items[key] if self.inventory.has_item(key) else 0
self.slot_image.pos = image.pos
self.slot_image.draw_static(self.window)
image.draw_static(self.window)
textsurface = self.font.render(str(items_count), False, (255, 255, 255))
self.window.blit(textsurface, (image.pos[0] + 48, image.pos[1] + 36))