diff --git a/project/data/levels/0-tutorial.csv b/project/data/levels/0-tutorial.csv index 3879df9..2c30b16 100644 --- a/project/data/levels/0-tutorial.csv +++ b/project/data/levels/0-tutorial.csv @@ -20,7 +20,7 @@ ,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,, ,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,+,+,+,+,+,+,#,#,#,#,#,#,#,#,#,,,,,,, ,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,#,#,#,,,,,,, -,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,, +,,,,,M,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,, ,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,, ,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,+,+,+,+,+,+,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,, ,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,,,,,,,,,,,,,,,,#,#,#,#,#,#,#,#,#,,,,,,, @@ -31,14 +31,16 @@ ,,,,,,,,,,,,,,,,,,,,,,,#,#,,,,,,,,,,,+,+,+,+,+,+,,,,,#,#,#,#,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,G,,,,,,,,,,,D,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,L,,,,,,,,,,,,,,,,,,,,,,,,, -,,,,,,,,,,,,,,,,P,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,P,,,,,,P,,,,,,,,,M,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,#,#,#,#,#,#,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,#,#,#,#,#,+,+,+,+,+,+,+,+,+,+ #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# #,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,# +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, AQ,18,requires_xor=lever-1;button-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, AU,32,requires_and=lever-1;button-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, AJ,33,id=lever-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Q,34,id=button-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file +Q,34,id=button-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +F,23,size=7,,weight=0.5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file diff --git a/project/data/sprites/movable_box.png b/project/data/sprites/movable_box.png new file mode 100644 index 0000000..21731bf Binary files /dev/null and b/project/data/sprites/movable_box.png differ diff --git a/project/data/sprites/sprites.json b/project/data/sprites/sprites.json index 8172757..c85982f 100644 --- a/project/data/sprites/sprites.json +++ b/project/data/sprites/sprites.json @@ -340,6 +340,19 @@ } ] }, + { + "id": "movable_box", + "subsheets": [ + { + "id": "1", + "delays": [ + 1 + ], + "width": 12, + "height": 12 + } + ] + }, { "id": "test_1", "subsheets": [ diff --git a/project/level/Level.py b/project/level/Level.py index 04aa36b..d505038 100644 --- a/project/level/Level.py +++ b/project/level/Level.py @@ -34,7 +34,7 @@ class Level: continue split_item = line[i].split('=') - if split_item[0] in ['id', 'emitter_state', 'debug', 'active_state']: + if split_item[0] in ['id', 'emitter_state', 'debug', 'active_state', 'size', 'weight']: tile[split_item[0]] = split_item[1] elif split_item[0] in ['requires', 'requires_and', 'requires_or', 'requires_xor']: tile[split_item[0]] = split_item[1].split(';') diff --git a/project/level/elements/LevelElement.py b/project/level/elements/LevelElement.py index a5b3d76..6b88284 100644 --- a/project/level/elements/LevelElement.py +++ b/project/level/elements/LevelElement.py @@ -10,7 +10,6 @@ class LevelElement: def __init__(self, tile: dict, loaded_level): self.tile = tile self.loaded_level = loaded_level - self.sprites = [] if 'id' in tile: self.id = tile['id'] @@ -20,7 +19,3 @@ class LevelElement: @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 3b9f6d6..10109af 100644 --- a/project/level/elements/LoadedLevel.py +++ b/project/level/elements/LoadedLevel.py @@ -1,12 +1,13 @@ from typing import Optional from level.Level import Level -from level.elements.ButtonInputLevelElement import ButtonInputLevelElement -from level.elements.GateReceiverLevelElement import GateReceiverLevelElement +from level.elements.dynamic.PushableBoxLevelElement import PushableBoxLevelElement +from level.elements.static.ButtonInputLevelElement import ButtonInputLevelElement +from level.elements.static.GateReceiverLevelElement import GateReceiverLevelElement from level.elements.LevelElement import LevelElement -from level.elements.LeverInputLevelElement import LeverInputLevelElement -from level.elements.SimpleBlockLevelElement import SimpleBlockLevelElement -from level.elements.UnknownTileLevelElement import UnknownTileLevelElement +from level.elements.static.LeverInputLevelElement import LeverInputLevelElement +from level.elements.static.SimpleBlockLevelElement import SimpleBlockLevelElement +from level.elements.static.UnknownTileLevelElement import UnknownTileLevelElement from physics import ConstantsParser from physics.SpriteManager import SpriteManager, DrawLayers from sprite.SpritesheetManager import SpritesheetManager @@ -24,6 +25,8 @@ TILES = { 'L': LeverInputLevelElement, 'G': GateReceiverLevelElement, + + 'M': PushableBoxLevelElement, } @@ -48,8 +51,6 @@ class LoadedLevel: if name in TILES: element = TILES[name](tile, self) - if isinstance(element, LeverInputLevelElement): - print(f"Found lever at {position}") elif not name == '': print(f"Unknown tile at {position}: {name}") element = UnknownTileLevelElement(tile, self) diff --git a/project/level/elements/dynamic/DynamicLevelElement.py b/project/level/elements/dynamic/DynamicLevelElement.py new file mode 100644 index 0000000..d6f2db0 --- /dev/null +++ b/project/level/elements/dynamic/DynamicLevelElement.py @@ -0,0 +1,10 @@ +from abc import ABC + +from level.elements.LevelElement import LevelElement +from sprite.DynamicSprite import DynamicSprite + + +class DynamicLevelElement(DynamicSprite, LevelElement, ABC): + def __init__(self, tile: dict, loaded_level): + DynamicSprite.__init__(self) + LevelElement.__init__(self, tile, loaded_level) diff --git a/project/level/elements/dynamic/PushableBoxLevelElement.py b/project/level/elements/dynamic/PushableBoxLevelElement.py new file mode 100644 index 0000000..4d3ae00 --- /dev/null +++ b/project/level/elements/dynamic/PushableBoxLevelElement.py @@ -0,0 +1,18 @@ +from level.Level import Level +from level.elements.dynamic.PushableLevelElement import PushableLevelElement +from physics.SpriteManager import SpriteManager +from sprite.SpritesheetManager import SpritesheetManager + + +class PushableBoxLevelElement(PushableLevelElement): + def __init__(self, tile: dict, loaded_level): + super().__init__(tile, loaded_level) + + if 'size' in self.tile: + self.position_scale.scale = (float(self.tile['size']), + float(self.tile['size'])) + + def load(self, sprite_manager: SpriteManager, spritesheet_manager: SpritesheetManager, level: Level): + self.spritesheet = spritesheet_manager.get_sheet('movable_box') + self.set_animation_state('1') + self.position_scale.position = self.tile['position'] diff --git a/project/level/elements/dynamic/PushableLevelElement.py b/project/level/elements/dynamic/PushableLevelElement.py new file mode 100644 index 0000000..ac21915 --- /dev/null +++ b/project/level/elements/dynamic/PushableLevelElement.py @@ -0,0 +1,28 @@ +from abc import ABC + +from level.elements.dynamic.DynamicLevelElement import DynamicLevelElement +from physics.CollisionDirection import CollisionDirection +from physics.TickData import TickData + + +class PushableLevelElement(DynamicLevelElement, ABC): + def __init__(self, tile: dict, loaded_level): + super().__init__(tile, loaded_level) + + if 'weight' in self.tile: + self.weight = float(self.tile['weight']) + else: + self.weight = 1.0 + + def tick(self, tick_data: TickData): + super().tick(tick_data) + + collides_element = self.get_collides_with_direction(CollisionDirection.LEFT) + + if collides_element: + self.motion = (self.motion[0] + self.weight, self.motion[1]) + + collides_element = self.get_collides_with_direction(CollisionDirection.RIGHT) + + if collides_element: + self.motion = (self.motion[0] - self.weight, self.motion[1]) diff --git a/project/level/elements/ButtonInputLevelElement.py b/project/level/elements/static/ButtonInputLevelElement.py similarity index 95% rename from project/level/elements/ButtonInputLevelElement.py rename to project/level/elements/static/ButtonInputLevelElement.py index 073dbfb..4142b02 100644 --- a/project/level/elements/ButtonInputLevelElement.py +++ b/project/level/elements/static/ButtonInputLevelElement.py @@ -1,5 +1,5 @@ from level.Level import Level -from level.elements.InputLevelElement import InputLevelElement +from level.elements.static.InputLevelElement import InputLevelElement from physics.CollisionDirection import CollisionDirection from physics.SpriteManager import SpriteManager from physics.TickData import TickData diff --git a/project/level/elements/GateReceiverLevelElement.py b/project/level/elements/static/GateReceiverLevelElement.py similarity index 91% rename from project/level/elements/GateReceiverLevelElement.py rename to project/level/elements/static/GateReceiverLevelElement.py index 22b89cb..1ab39fd 100644 --- a/project/level/elements/GateReceiverLevelElement.py +++ b/project/level/elements/static/GateReceiverLevelElement.py @@ -1,5 +1,5 @@ from level.Level import Level -from level.elements.ReceiverLevelElement import ReceiverLevelElement +from level.elements.static.ReceiverLevelElement import ReceiverLevelElement from physics.SpriteManager import SpriteManager from sprite.SpritesheetManager import SpritesheetManager @@ -13,7 +13,6 @@ class GateReceiverLevelElement(ReceiverLevelElement): self.set_animation_state('open' if self.active_state else 'close') self.position_scale.position = self.tile['position'] - def set_active(self, active: bool): super().set_active(active) self.set_animation_state('open' if self.active_state else 'close') diff --git a/project/level/elements/InputLevelElement.py b/project/level/elements/static/InputLevelElement.py similarity index 81% rename from project/level/elements/InputLevelElement.py rename to project/level/elements/static/InputLevelElement.py index 11e009d..8db3e52 100644 --- a/project/level/elements/InputLevelElement.py +++ b/project/level/elements/static/InputLevelElement.py @@ -1,7 +1,7 @@ from abc import ABC -from level.elements.StaticLevelElement import StaticLevelElement +from level.elements.static.StaticLevelElement import StaticLevelElement class InputLevelElement(StaticLevelElement, ABC): diff --git a/project/level/elements/LeverInputLevelElement.py b/project/level/elements/static/LeverInputLevelElement.py similarity index 95% rename from project/level/elements/LeverInputLevelElement.py rename to project/level/elements/static/LeverInputLevelElement.py index a21dd93..91454aa 100644 --- a/project/level/elements/LeverInputLevelElement.py +++ b/project/level/elements/static/LeverInputLevelElement.py @@ -1,5 +1,5 @@ from level.Level import Level -from level.elements.InputLevelElement import InputLevelElement +from level.elements.static.InputLevelElement import InputLevelElement from physics.CollisionDirection import CollisionDirection from physics.SpriteManager import SpriteManager from physics.TickData import TickData diff --git a/project/level/elements/ReceiverLevelElement.py b/project/level/elements/static/ReceiverLevelElement.py similarity index 84% rename from project/level/elements/ReceiverLevelElement.py rename to project/level/elements/static/ReceiverLevelElement.py index 39304f7..d3efeeb 100644 --- a/project/level/elements/ReceiverLevelElement.py +++ b/project/level/elements/static/ReceiverLevelElement.py @@ -1,6 +1,6 @@ from abc import ABC -from level.elements.StaticLevelElement import StaticLevelElement +from level.elements.static.StaticLevelElement import StaticLevelElement from physics.TickData import TickData @@ -18,13 +18,17 @@ class ReceiverLevelElement(StaticLevelElement, ABC): self.requires['xor'] = tile['requires_xor'] self.active_state = False + if 'active_state' in tile: - self.set_active(tile['active_state'] == 'True') + self.active_state_invert = tile['active_state'] == 'True' else: - self.set_active(False) + self.active_state_invert = False def set_active(self, active: bool): - self.active_state = active + if self.active_state_invert: + self.active_state = not active + else: + self.active_state = active def tick(self, tick_data: TickData): super().tick(tick_data) @@ -48,7 +52,6 @@ class ReceiverLevelElement(StaticLevelElement, ABC): break if 'xor' in self.requires: - print(self.requires['xor']) for requirement in self.requires['xor']: element = self.loaded_level.get_element_by_id(requirement) if element and element.emitter_state: diff --git a/project/level/elements/SimpleBlockLevelElement.py b/project/level/elements/static/SimpleBlockLevelElement.py similarity index 93% rename from project/level/elements/SimpleBlockLevelElement.py rename to project/level/elements/static/SimpleBlockLevelElement.py index b151222..0dfa8b7 100644 --- a/project/level/elements/SimpleBlockLevelElement.py +++ b/project/level/elements/static/SimpleBlockLevelElement.py @@ -1,7 +1,7 @@ import random from level.Level import Level -from level.elements.StaticLevelElement import StaticLevelElement +from level.elements.static.StaticLevelElement import StaticLevelElement from physics.SpriteManager import SpriteManager from sprite.SpritesheetManager import SpritesheetManager diff --git a/project/level/elements/StaticLevelElement.py b/project/level/elements/static/StaticLevelElement.py similarity index 96% rename from project/level/elements/StaticLevelElement.py rename to project/level/elements/static/StaticLevelElement.py index 88cd088..9f42d5b 100644 --- a/project/level/elements/StaticLevelElement.py +++ b/project/level/elements/static/StaticLevelElement.py @@ -1,4 +1,3 @@ -import uuid from abc import ABC from level.elements.LevelElement import LevelElement diff --git a/project/level/elements/UnknownTileLevelElement.py b/project/level/elements/static/UnknownTileLevelElement.py similarity index 86% rename from project/level/elements/UnknownTileLevelElement.py rename to project/level/elements/static/UnknownTileLevelElement.py index 1ea4fbe..882ba27 100644 --- a/project/level/elements/UnknownTileLevelElement.py +++ b/project/level/elements/static/UnknownTileLevelElement.py @@ -1,5 +1,5 @@ from level.Level import Level -from level.elements.StaticLevelElement import StaticLevelElement +from level.elements.static.StaticLevelElement import StaticLevelElement from physics.SpriteManager import SpriteManager from sprite.SpritesheetManager import SpritesheetManager diff --git a/project/main.py b/project/main.py index a1bedd9..3e41d03 100644 --- a/project/main.py +++ b/project/main.py @@ -12,6 +12,7 @@ from sprite.PositionScale import PositionScale from sprite.SpritesheetManager import SpritesheetManager from sprite.Sprite import Sprite from sprite.StaticSprite import StaticSprite +from ui_elements import CoordinateTransform from ui_elements.ClickEvent import ClickEvent from ui_elements.KeyManager import KeyManager from ui_elements.LevelMenu import LevelMenu @@ -66,6 +67,12 @@ if what_to_run == 'menu': key_manager.update_key_events(pygame_events) click_events: list[ClickEvent] = ClickEvent.create_events(pygame_events, screen_transform) + for event in click_events: + for layer in sprite_manager.layers: + for sprite in sprite_manager.layers[layer]: + if sprite.get_bounding_box().contains_point(event.screen_position): + sprite.click(event) + for event in pygame_events: if event.type == pygame.QUIT: pygame.quit() @@ -97,7 +104,7 @@ elif what_to_run == 'level': parsed_levels_manager.load_from_config('data/levels/levels.json') generated_level = LoadedLevel(sprite_manager, spritesheet_manager) - generated_level.load_level(parsed_levels_manager.levels[1]) + generated_level.load_level(parsed_levels_manager.levels[0]) ghost_character = PlayerSprite(spritesheet_manager.get_sheet("ghost_character")) ghost_character.position_scale = PositionScale((90, 50), (1, 1)) sprite_manager.add_ui_element(DrawLayers.OBJECTS, ghost_character) diff --git a/project/physics/PhysicsElementsHandler.py b/project/physics/PhysicsElementsHandler.py index e513a88..057911f 100644 --- a/project/physics/PhysicsElementsHandler.py +++ b/project/physics/PhysicsElementsHandler.py @@ -4,6 +4,7 @@ from typing import Optional from physics.CollisionDirection import CollisionDirection from physics.TickData import TickData from sprite.DynamicSprite import DynamicSprite +from sprite.PositionScale import PositionScale from sprite.Sprite import Sprite from sprite.StaticSprite import StaticSprite from ui_elements.UiElement import UiElement @@ -26,6 +27,8 @@ class PhysicsElementsHandler: sprites = [] for layer in layers: for sprite in layers[layer]: + # if str(type(sprite)) == "": + # print(f"Found pushable box") sprite.tick(tick_data) if isinstance(sprite, Sprite): sprites.append(sprite) @@ -51,15 +54,15 @@ class PhysicsElementsHandler: sorted_dynamic_sprites = sorted(dynamic_sprites, key=lambda spr: spr.position_scale.position[1]) for sprite in sorted_dynamic_sprites: - collides_with = self.attempt_move(tick_data.dt, sprite, colliders, MOTION_STEPS) + collides_with = self.attempt_move(tick_data, sprite, colliders, MOTION_STEPS) if collides_with is not None: for callback in self.collision_callbacks: callback(sprite, collides_with) - def attempt_move(self, dt: float, sprite: DynamicSprite, colliders: list[StaticSprite], + def attempt_move(self, tick_data: TickData, sprite: DynamicSprite, colliders: list[StaticSprite], motion_steps: int) -> Optional[StaticSprite]: total_motion = sprite.motion - motion_step = ((total_motion[0] * dt) / motion_steps, (total_motion[1] * dt) / motion_steps) + motion_step = ((total_motion[0] * tick_data.dt) / motion_steps, (total_motion[1] * tick_data.dt) / motion_steps) collides_with_last = None collided = [False, False] @@ -73,7 +76,7 @@ class PhysicsElementsHandler: # print('Elements: ', list(filter(lambda spr: str(type(spr)) == "", colliders))) - collides_with = self.check_collides(sprite, colliders) + collides_with = self.check_collides(sprite, colliders, tick_data.screen_transform) for collider in collides_with: if collider is not None: if sprite.is_collider and collider.is_collider: @@ -117,7 +120,7 @@ class PhysicsElementsHandler: sprite.position_scale.position[1] + motion_step[1] ) - collides_with = self.check_collides(sprite, colliders) + collides_with = self.check_collides(sprite, colliders, tick_data.screen_transform) for collider in collides_with: if collider is not None: if sprite.is_collider and collider.is_collider: @@ -157,13 +160,14 @@ class PhysicsElementsHandler: return collides_with_last - def check_collides(self, sprite: StaticSprite, colliders: list[StaticSprite]) -> list[StaticSprite]: + def check_collides(self, sprite: StaticSprite, colliders: list[StaticSprite], screen_transform: PositionScale) -> list[StaticSprite]: collides_with = [] for collider in colliders: if sprite is not collider: distance = self.calculate_basic_distance(sprite, collider) - if distance > 50: + if distance > max(self.get_sprite_size_for_distance(sprite, screen_transform), + self.get_sprite_size_for_distance(collider, screen_transform)): continue if sprite.collides_with(collider, TOLERANCE): collides_with.append(collider) @@ -175,3 +179,10 @@ class PhysicsElementsHandler: def calculate_basic_distance(self, sprite1: StaticSprite, sprite2: StaticSprite) -> float: return math.sqrt((sprite1.position_scale.position[0] - sprite2.position_scale.position[0]) ** 2 + (sprite1.position_scale.position[1] - sprite2.position_scale.position[1]) ** 2) + + def get_sprite_size_for_distance(self, ui_element: UiElement, screen_transform: PositionScale) -> int: + image = ui_element.render_sprite_image() + if image is None: + return 0 + return max(image.get_width() * ui_element.position_scale.scale[0] * screen_transform.scale[0], + image.get_height() * ui_element.position_scale.scale[1] * screen_transform.scale[1])