Merge remote-tracking branch 'origin/main'

main
Jonas Wächter 2023-03-27 11:46:24 +02:00
commit 8ca9bc03ee
19 changed files with 428 additions and 240 deletions

View File

@ -0,0 +1,12 @@
{
"level": {
"block_size": [
12,
12
],
"level_size": [
71,
40
]
}
}

View File

@ -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 @@
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#
3,3,id=HEBEL
Can't render this file because it has a wrong number of fields in line 42.

View File

@ -1,4 +1,271 @@
[
{
"id": "goal_door",
"subsheets": [
{
"id": "open",
"delays": [
1
],
"width": 12,
"height": 12
},
{
"id": "closed",
"delays": [
1
],
"width": 12,
"height": 12
}
]
},
{
"id": "gate",
"subsheets": [
{
"id": "1",
"delays": [
1
],
"width": 12,
"height": 36
}
]
},
{
"id": "small_spike",
"subsheets": [
{
"id": "1",
"delays": [
1
],
"width": 12,
"height": 12
}
]
},
{
"id": "lever",
"subsheets": [
{
"id": "on",
"delays": [
1
],
"width": 24,
"height": 24
},
{
"id": "off",
"delays": [
1
],
"width": 24,
"height": 24
}
]
},
{
"id": "temp_blocks",
"subsheets": [
{
"id": "1",
"delays": [
1
],
"width": 12,
"height": 12
},
{
"id": "2",
"delays": [
1
],
"width": 12,
"height": 12
}
]
},
{
"id": "pressureplate",
"subsheets": [
{
"id": "on",
"delays": [
1
],
"width": 24,
"height": 12
},
{
"id": "off",
"delays": [
1
],
"width": 24,
"height": 12
}
]
},
{
"id": "tutorial_block_full",
"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_full",
"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_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": [
@ -47,104 +314,5 @@
"height": 16
}
]
},
{
"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": "tutorial_block_full",
"subsheets": [
{
"id": "1",
"delays": [
1
],
"width": 16,
"height": 16
},
{
"id": "2",
"delays": [
1
],
"width": 16,
"height": 16
},
{
"id": "3",
"delays": [
1
],
"width": 16,
"height": 16
}
]
},
{
"id": "castle_block_full",
"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
}
]
}
]

View File

@ -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] != '':

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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()

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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):