From e53c1084df5bf66f1fc3be4fd6b12cea5591e90e Mon Sep 17 00:00:00 2001 From: Yan Wittmann Date: Mon, 27 Mar 2023 11:46:03 +0200 Subject: [PATCH] Started writing new level creation --- project/data/config/config.json | 12 ++ project/data/levels/1-1.csv | 5 +- project/data/sprites/sprites.json | 152 +++++++++++++++++- project/level/Level.py | 9 +- project/level/elements/BlockElement.py | 7 - .../level/elements/ButtonInputLevelElement.py | 20 +++ project/level/elements/DoorElement.py | 14 -- project/level/elements/GateElement.py | 8 - project/level/elements/GoalDoorElement.py | 7 - project/level/elements/InputLevelElement.py | 14 ++ project/level/elements/InteractableElement.py | 12 -- project/level/elements/LevelElement.py | 20 +++ project/level/elements/LoadedLevel.py | 69 +++----- .../level/elements/SimpleBlockLevelElement.py | 24 +++ project/level/elements/StaticLevelElement.py | 17 +- project/physics/ConstantsParser.py | 11 ++ project/sprite/DynamicSprite.py | 4 +- project/sprite/Sprite.py | 45 ++---- project/sprite/StaticSprite.py | 4 +- 19 files changed, 311 insertions(+), 143 deletions(-) create mode 100644 project/data/config/config.json delete mode 100644 project/level/elements/BlockElement.py create mode 100644 project/level/elements/ButtonInputLevelElement.py delete mode 100644 project/level/elements/DoorElement.py delete mode 100644 project/level/elements/GateElement.py delete mode 100644 project/level/elements/GoalDoorElement.py create mode 100644 project/level/elements/InputLevelElement.py delete mode 100644 project/level/elements/InteractableElement.py create mode 100644 project/level/elements/LevelElement.py create mode 100644 project/level/elements/SimpleBlockLevelElement.py create mode 100644 project/physics/ConstantsParser.py diff --git a/project/data/config/config.json b/project/data/config/config.json new file mode 100644 index 0000000..b0e5126 --- /dev/null +++ b/project/data/config/config.json @@ -0,0 +1,12 @@ +{ + "level": { + "block_size": [ + 12, + 12 + ], + "level_size": [ + 71, + 40 + ] + } +} \ No newline at end of file diff --git a/project/data/levels/1-1.csv b/project/data/levels/1-1.csv index d736825..1de94ae 100644 --- a/project/data/levels/1-1.csv +++ b/project/data/levels/1-1.csv @@ -26,7 +26,7 @@ #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,# #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,# #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,# -#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,x,x,x,x,x,x,x,x,x,,,,,,,,,,,,,,,,,D,,,# +#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,x,x,x,x,x,x,x,x,x,,,,,,,,,,,,,,,,,,,,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# @@ -38,4 +38,5 @@ #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# -#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# \ No newline at end of file +#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# +3,3,id=HEBEL \ No newline at end of file diff --git a/project/data/sprites/sprites.json b/project/data/sprites/sprites.json index cb5ffcc..f3f1e16 100644 --- a/project/data/sprites/sprites.json +++ b/project/data/sprites/sprites.json @@ -93,7 +93,7 @@ "id": "tutorial_block_full", "subsheets": [ { - "id": "1", + "id": "on", "delays": [ 1 ], @@ -101,7 +101,7 @@ "height": 16 }, { - "id": "2", + "id": "off", "delays": [ 1 ], @@ -146,5 +146,153 @@ "height": 12 } ] + }, + { + "id": "castle_block_wall_left", + "subsheets": [ + { + "id": "1", + "delays": [ + 1 + ], + "width": 12, + "height": 12 + }, + { + "id": "2", + "delays": [ + 1 + ], + "width": 12, + "height": 12 + }, + { + "id": "3", + "delays": [ + 1 + ], + "width": 12, + "height": 12 + } + ] + }, + { + "id": "castle_block_wall_right", + "subsheets": [ + { + "id": "1", + "delays": [ + 1 + ], + "width": 12, + "height": 12 + }, + { + "id": "2", + "delays": [ + 1 + ], + "width": 12, + "height": 12 + }, + { + "id": "3", + "delays": [ + 1 + ], + "width": 12, + "height": 12 + } + ] + }, + { + "id": "ghost_character", + "subsheets": [ + { + "id": "idle", + "delays": [ + 40, + 10 + ], + "width": 24, + "height": 36 + }, + { + "id": "walk_l", + "delays": [ + 1, + 1 + ], + "width": 24, + "height": 36 + }, + { + "id": "walk_r", + "delays": [ + 1, + 1 + ], + "width": 24, + "height": 36 + }, + { + "id": "jump", + "delays": [ + 1, + 1 + ], + "width": 24, + "height": 36 + } + ] + }, + { + "id": "test_1", + "subsheets": [ + { + "id": "walk_l", + "delays": [ + 1, + 2, + 3, + 1 + ], + "width": 16, + "height": 16 + }, + { + "id": "idle", + "delays": [ + 1, + 1, + 3, + 1, + 1 + ], + "width": 16, + "height": 16 + }, + { + "id": "walk_r", + "delays": [ + 1, + 1, + 1, + 1 + ], + "width": 16, + "height": 16 + }, + { + "id": "other_test", + "delays": [ + 1, + 1, + 1 + ], + "width": 16, + "height": 16 + } + ] } ] \ No newline at end of file diff --git a/project/level/Level.py b/project/level/Level.py index 73eeaa7..a0a531d 100644 --- a/project/level/Level.py +++ b/project/level/Level.py @@ -1,4 +1,5 @@ -LEVEL_SIZE = (71, 40) +from physics import ConstantsParser + class Level: @@ -15,13 +16,13 @@ class Level: tiles = [] for line_number, line in enumerate(csv_grid): - if line_number <= LEVEL_SIZE[1]: + if line_number <= ConstantsParser.CONFIG.level_size[1]: tiles.append([]) for item_number, item in enumerate(line): - if item_number <= LEVEL_SIZE[0]: + if item_number <= ConstantsParser.CONFIG.level_size[0]: tiles[line_number].append({'name': item}) else: - print('Level is too wide:', item_number, '>', LEVEL_SIZE[0]) + print('Level is too wide:', item_number, '>', ConstantsParser.CONFIG.level_size[0]) break elif line[0] != '': diff --git a/project/level/elements/BlockElement.py b/project/level/elements/BlockElement.py deleted file mode 100644 index 6058886..0000000 --- a/project/level/elements/BlockElement.py +++ /dev/null @@ -1,7 +0,0 @@ -from level.elements.StaticLevelElement import StaticLevelElement -from sprite.Spritesheet import Spritesheet - - -class BlockElement(StaticLevelElement): - def __init__(self, spritesheet: Spritesheet): - super().__init__(spritesheet) diff --git a/project/level/elements/ButtonInputLevelElement.py b/project/level/elements/ButtonInputLevelElement.py new file mode 100644 index 0000000..8fce444 --- /dev/null +++ b/project/level/elements/ButtonInputLevelElement.py @@ -0,0 +1,20 @@ +from level.Level import Level +from level.elements.InputLevelElement import InputLevelElement +from physics.SpriteManager import SpriteManager +from physics.TickData import TickData +from sprite.SpritesheetManager import SpritesheetManager + + +class ButtonInputLevelElement(InputLevelElement): + + def load(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, level: Level): + name = self.tile['name'] + position = self.tile['position'] + + self.spritesheet = spritesheet_manager.get_sheet('pressureplate') + self.set_animation_state('on' if self.emitter_state else 'off') + self.position_scale.position = position + + def tick(self, tick_data: TickData): + collisions = self.get_collides_with() + print(collisions) diff --git a/project/level/elements/DoorElement.py b/project/level/elements/DoorElement.py deleted file mode 100644 index 8ed9286..0000000 --- a/project/level/elements/DoorElement.py +++ /dev/null @@ -1,14 +0,0 @@ -from level.elements.StaticLevelElement import StaticLevelElement -from sprite.Spritesheet import Spritesheet - - -class DoorElement(StaticLevelElement): - def __init__(self, spritesheet: Spritesheet, id : str, requires : list[str]): - super().__init__(spritesheet) - self.id = id - self.requires = requires - - def openDoor(self): - pass - def closeDoor(self): - pass \ No newline at end of file diff --git a/project/level/elements/GateElement.py b/project/level/elements/GateElement.py deleted file mode 100644 index d3c21bd..0000000 --- a/project/level/elements/GateElement.py +++ /dev/null @@ -1,8 +0,0 @@ -from level.elements.DoorElement import DoorElement -from sprite.Spritesheet import Spritesheet - - -class GateElement(DoorElement): - - def __int__(self, spritesheet: Spritesheet, id : str, requires : list[str]): - super().__int__(spritesheet, id, requires) \ No newline at end of file diff --git a/project/level/elements/GoalDoorElement.py b/project/level/elements/GoalDoorElement.py deleted file mode 100644 index f76775b..0000000 --- a/project/level/elements/GoalDoorElement.py +++ /dev/null @@ -1,7 +0,0 @@ -from level.elements.DoorElement import DoorElement -from sprite.Spritesheet import Spritesheet - - -class GoalDoorElement(DoorElement): - def __int__(self, spritesheet: Spritesheet, id : str, requires : list[str]): - super().__int__(spritesheet, id, requires) \ No newline at end of file diff --git a/project/level/elements/InputLevelElement.py b/project/level/elements/InputLevelElement.py new file mode 100644 index 0000000..11e009d --- /dev/null +++ b/project/level/elements/InputLevelElement.py @@ -0,0 +1,14 @@ + +from abc import ABC + +from level.elements.StaticLevelElement import StaticLevelElement + + +class InputLevelElement(StaticLevelElement, ABC): + def __init__(self, tile: dict, loaded_level): + super().__init__(tile, loaded_level) + + if 'emitter_state' in tile: + self.emitter_state = tile['emitter_state'] == 'true' + else: + self.emitter_state = False diff --git a/project/level/elements/InteractableElement.py b/project/level/elements/InteractableElement.py deleted file mode 100644 index 36153c0..0000000 --- a/project/level/elements/InteractableElement.py +++ /dev/null @@ -1,12 +0,0 @@ -from level.elements.StaticLevelElement import StaticLevelElement -from sprite.Spritesheet import Spritesheet - - -class InteractableElement(StaticLevelElement): - def __init__(self, spritesheet: Spritesheet, block_id: str): - super().__init__(spritesheet) - self.block_id = block_id - self.element = [] - - def connect_element(self, element: StaticLevelElement): - self.element.append(element) \ No newline at end of file diff --git a/project/level/elements/LevelElement.py b/project/level/elements/LevelElement.py new file mode 100644 index 0000000..52f87eb --- /dev/null +++ b/project/level/elements/LevelElement.py @@ -0,0 +1,20 @@ +from abc import abstractmethod + +from level.Level import Level +from physics.SpriteManager import SpriteManager +from sprite.SpritesheetManager import SpritesheetManager + + +class LevelElement: + def __init__(self, tile: dict, loaded_level): + self.tile = tile + self.loaded_level = loaded_level + self.sprites = [] + + @abstractmethod + def load(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, level: Level): + pass + + def destroy(self, sprite_manager: SpriteManager): + for sprite in self.sprites: + sprite_manager.remove_ui_element(sprite) diff --git a/project/level/elements/LoadedLevel.py b/project/level/elements/LoadedLevel.py index de011f0..cba0ebb 100644 --- a/project/level/elements/LoadedLevel.py +++ b/project/level/elements/LoadedLevel.py @@ -1,16 +1,17 @@ import random from level.Level import Level -from level.LevelElementSymbols import LevelElementSymbols -from level.elements.BlockElement import BlockElement -from level.elements.GateElement import GateElement -from level.elements.GoalDoorElement import GoalDoorElement -from level.elements.InteractableElement import InteractableElement -from level.elements.StaticLevelElement import StaticLevelElement +from level.elements.ButtonInputLevelElement import ButtonInputLevelElement +from level.elements.SimpleBlockLevelElement import SimpleBlockLevelElement +from physics import ConstantsParser from physics.SpriteManager import SpriteManager, DrawLayers from sprite.SpritesheetManager import SpritesheetManager -BLOCK_SIZE = (12, 12) +TILES = { + '#': SimpleBlockLevelElement, + 'L': ButtonInputLevelElement, # TODO + 'P': ButtonInputLevelElement +} class LoadedLevel: @@ -20,53 +21,21 @@ class LoadedLevel: self.blocks = [] def load_level(self, level: Level): - requires_dict: dict[str, list[StaticLevelElement]] = {} for row_number, row in enumerate(level.tiles): for tile_number, tile in enumerate(row): - position = (tile_number * BLOCK_SIZE[0], row_number * BLOCK_SIZE[1]) + position = (tile_number * ConstantsParser.CONFIG.block_size[0], + row_number * ConstantsParser.CONFIG.block_size[1]) + tile['position'] = position - tile_element = LevelElementSymbols.dict[tile['name']] + name = tile['name'] + if name in TILES: + element = TILES[name](tile, self) + self.blocks.append(element) + self.sprite_manager.add_ui_element(DrawLayers.OBJECTS, element) + element.load(self.sprite_manager, self.spritesheet_manager, level) - sprite = None - - if tile_element in LevelElementSymbols.BLOCKS_LIST: - spritesheet = self.spritesheet_manager.get_sheet(level.theme + tile_element['sprite_id']) - sprite = BlockElement(spritesheet) - sprite.set_animation_state(str(random.randint(1, 3))) - sprite.position_scale.position = position - - elif tile_element == LevelElementSymbols.GOAL_DOOR: - spritesheet = self.spritesheet_manager.get_sheet(tile_element['sprite_id']) - sprite = GoalDoorElement(spritesheet, tile['id'], tile['requires']) - sprite.set_animation_state('closed') - sprite.position_scale.position = position - for required in tile['requires']: - requires_dict[required] = requires_dict.get(required, []).append(sprite) - - elif tile_element == LevelElementSymbols.GATE: - spritesheet = self.spritesheet_manager.get_sheet(tile_element['sprite_id']) - sprite = GateElement(spritesheet, tile['id'], tile['requires']) - sprite.set_animation_state('closed') - sprite.position_scale.position = position - for required in tile['requires']: - requires_dict[required] = requires_dict.get(required, []).append(sprite) - - elif tile_element in LevelElementSymbols.INTERACTABLE_LIST: - spritesheet = self.spritesheet_manager.get_sheet(tile_element['sprite_id']) - sprite = InteractableElement(spritesheet, tile['id']) - sprite.set_animation_state('on') - sprite.position_scale.position = position - - # tile['id'], tile['requirements'] - - if sprite is not None: - self.blocks.append(sprite) - self.sprite_manager.add_ui_element(DrawLayers.OBJECTS, sprite) - - for block in self.blocks: - if isinstance(block, InteractableElement): - for requirement in requires_dict: - block.connect_element(requirement) + elif not name == '': + print(f"Unknown tile: {name}") def destroy_level(self): for block in self.blocks: diff --git a/project/level/elements/SimpleBlockLevelElement.py b/project/level/elements/SimpleBlockLevelElement.py new file mode 100644 index 0000000..22dd14f --- /dev/null +++ b/project/level/elements/SimpleBlockLevelElement.py @@ -0,0 +1,24 @@ +import random + +from level.Level import Level +from level.elements.StaticLevelElement import StaticLevelElement +from physics.SpriteManager import SpriteManager +from sprite.SpritesheetManager import SpritesheetManager + +BLOCK_TYPES = { + '#': 'block_full' +} + + +class SimpleBlockLevelElement(StaticLevelElement): + def __init__(self, tile: dict, loaded_level): + super().__init__(tile, loaded_level) + + def load(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, level: Level): + name = self.tile['name'] + position = self.tile['position'] + block_type = BLOCK_TYPES[name] + + self.spritesheet = spritesheet_manager.get_sheet(level.theme + '_' + block_type) + self.set_animation_state(str(random.randint(1, 3))) + self.position_scale.position = position diff --git a/project/level/elements/StaticLevelElement.py b/project/level/elements/StaticLevelElement.py index 8b412c6..f0e87ef 100644 --- a/project/level/elements/StaticLevelElement.py +++ b/project/level/elements/StaticLevelElement.py @@ -1,7 +1,16 @@ -from sprite.Spritesheet import Spritesheet +import uuid +from abc import ABC + +from level.elements.LevelElement import LevelElement from sprite.StaticSprite import StaticSprite -class StaticLevelElement(StaticSprite): - def __init__(self, spritesheet: Spritesheet): - super().__init__(spritesheet) +class StaticLevelElement(StaticSprite, LevelElement, ABC): + def __init__(self, tile: dict, loaded_level): + StaticSprite.__init__(self) + LevelElement.__init__(self, tile, loaded_level) + + if 'id' in tile: + self.id = tile['id'] + else: + self.id = uuid.uuid4() diff --git a/project/physics/ConstantsParser.py b/project/physics/ConstantsParser.py new file mode 100644 index 0000000..9441b72 --- /dev/null +++ b/project/physics/ConstantsParser.py @@ -0,0 +1,11 @@ +import json + + +class ConstantsParser: + def __init__(self, file: str): + parsed = json.load(open(file)) + self.level_size = (parsed['level']['level_size'][0], parsed['level']['level_size'][1]) + self.block_size = (parsed['level']['block_size'][0], parsed['level']['block_size'][1]) + + +CONFIG = ConstantsParser("data/config/config.json") diff --git a/project/sprite/DynamicSprite.py b/project/sprite/DynamicSprite.py index 357ed27..2271396 100644 --- a/project/sprite/DynamicSprite.py +++ b/project/sprite/DynamicSprite.py @@ -1,3 +1,5 @@ +from typing import Optional + from physics.CollisionDirection import CollisionDirection from sprite.Spritesheet import Spritesheet from sprite.StaticSprite import StaticSprite @@ -5,7 +7,7 @@ from physics.TickData import TickData class DynamicSprite(StaticSprite): - def __init__(self, spritesheet: Spritesheet): + def __init__(self, spritesheet: Optional[Spritesheet] = None): super().__init__(spritesheet) self.motion = (0, 0) diff --git a/project/sprite/Sprite.py b/project/sprite/Sprite.py index f422ace..8f592c8 100644 --- a/project/sprite/Sprite.py +++ b/project/sprite/Sprite.py @@ -12,11 +12,14 @@ from ui_elements.UiElement import UiElement class Sprite(UiElement): - def __init__(self, spritesheet: Spritesheet): + def __init__(self, spritesheet: Optional[Spritesheet] = None): super().__init__() self.spritesheet = spritesheet - self.animation_state = list(self.spritesheet.animations.keys())[0] + if self.spritesheet is not None: + self.animation_state = list(self.spritesheet.animations.keys())[0] + else: + self.animation_state = None self.animation_frame = 0 self.animation_delay = 0 @@ -43,6 +46,10 @@ class Sprite(UiElement): return None def tick(self, tick_data: TickData): + if self.spritesheet is None: + self.image = None + return + animation = self.spritesheet.animations[self.animation_state] if self.animated: @@ -55,10 +62,15 @@ class Sprite(UiElement): self.image = animation['images'][self.animation_frame % len(animation['images'])] def set_animation_state(self, state: str): + if self.spritesheet is None: + return + if state in self.spritesheet.animations: self.animation_state = state self.animation_delay = 0 self.tick(self.empty_tick_data()) + else: + print('Unknown animation state: ' + state) def set_animation_frame(self, frame: int): self.animation_frame = frame @@ -77,32 +89,3 @@ class Sprite(UiElement): self.image.get_width() * self.position_scale.scale[0], self.image.get_height() * self.position_scale.scale[1] ) - - def dump(self, file): - # re-attach all the images to a single surface and save it to a file - width = 0 - height = 0 - - for animation in self.spritesheet.animations.values(): - max_height = 0 - total_width = 0 - for image in animation['images']: - total_width += image.get_width() - max_height = max(max_height, image.get_height()) - width = max(width, total_width) - height += max_height - - sheet = pygame.Surface((width, height)) - - x = 0 - y = 0 - for animation in self.spritesheet.animations.values(): - max_height = 0 - for image in animation['images']: - sheet.blit(image, (x, y)) - x += image.get_width() - max_height = max(max_height, image.get_height()) - y += max_height - x = 0 - - pygame.image.save(sheet, file) diff --git a/project/sprite/StaticSprite.py b/project/sprite/StaticSprite.py index b5e48cc..cf1827b 100644 --- a/project/sprite/StaticSprite.py +++ b/project/sprite/StaticSprite.py @@ -1,9 +1,11 @@ +from typing import Optional + from sprite.Sprite import Sprite from sprite.Spritesheet import Spritesheet class StaticSprite(Sprite): - def __init__(self, spritesheet: Spritesheet): + def __init__(self, spritesheet: Optional[Spritesheet] = None): super().__init__(spritesheet) def collides_with(self, collider: 'StaticSprite', tolerance: float = 0.0):