Gegner Bewegung ohne Runterfallen
This commit is contained in:
parent
8e465ba27b
commit
86312b2106
5 changed files with 151 additions and 76 deletions
11
editor.py
11
editor.py
|
|
@ -14,7 +14,9 @@ class Editor:
|
||||||
|
|
||||||
pygame.display.set_caption("Editor")
|
pygame.display.set_caption("Editor")
|
||||||
self.screen: pygame.Surface = pygame.display.set_mode((640, 480))
|
self.screen: pygame.Surface = pygame.display.set_mode((640, 480))
|
||||||
self.display: pygame.Surface = pygame.Surface((self.screen.get_width()//2, self.screen.get_height()//2))
|
self.display: pygame.Surface = pygame.Surface(
|
||||||
|
(self.screen.get_width() // 2, self.screen.get_height() // 2)
|
||||||
|
)
|
||||||
|
|
||||||
self.clock: pygame.time.Clock = pygame.time.Clock()
|
self.clock: pygame.time.Clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
|
@ -62,9 +64,9 @@ class Editor:
|
||||||
self.tilemap.render(surface=self.display, offset=render_scroll)
|
self.tilemap.render(surface=self.display, offset=render_scroll)
|
||||||
|
|
||||||
# Aktuelle Kachel (Kopie)
|
# Aktuelle Kachel (Kopie)
|
||||||
current_tile_img: pygame.Surface = self.assets[self.tile_list[self.tile_group]][
|
current_tile_img: pygame.Surface = self.assets[
|
||||||
self.tile_variant
|
self.tile_list[self.tile_group]
|
||||||
].copy()
|
][self.tile_variant].copy()
|
||||||
# Halbtransparent
|
# Halbtransparent
|
||||||
current_tile_img.set_alpha(100)
|
current_tile_img.set_alpha(100)
|
||||||
|
|
||||||
|
|
@ -243,4 +245,5 @@ class Editor:
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
Editor().run()
|
Editor().run()
|
||||||
|
|
|
||||||
24
game.py
24
game.py
|
|
@ -52,10 +52,12 @@ class Game:
|
||||||
"player/wall_slide": Animation(
|
"player/wall_slide": Animation(
|
||||||
load_images(path="entities/player/wall_slide")
|
load_images(path="entities/player/wall_slide")
|
||||||
),
|
),
|
||||||
|
"enemy/idle": Animation(
|
||||||
"enemy/idle": Animation(load_images(path="entities/enemy/idle"), image_duration=6),
|
load_images(path="entities/enemy/idle"), image_duration=6
|
||||||
"enemy/run": Animation(load_images(path="entities/enemy/run"), image_duration=4),
|
),
|
||||||
|
"enemy/run": Animation(
|
||||||
|
load_images(path="entities/enemy/run"), image_duration=4
|
||||||
|
),
|
||||||
"particles/leaf": Animation(
|
"particles/leaf": Animation(
|
||||||
load_images(path="particles/leaf"), image_duration=20, loop=False
|
load_images(path="particles/leaf"), image_duration=20, loop=False
|
||||||
),
|
),
|
||||||
|
|
@ -64,7 +66,7 @@ class Game:
|
||||||
),
|
),
|
||||||
"particles/particle": Animation(
|
"particles/particle": Animation(
|
||||||
load_images(path="particles/particle"), image_duration=6, loop=False
|
load_images(path="particles/particle"), image_duration=6, loop=False
|
||||||
)
|
),
|
||||||
}
|
}
|
||||||
self.player: Player = Player(game=self, pos=(50, 50), size=(8, 15))
|
self.player: Player = Player(game=self, pos=(50, 50), size=(8, 15))
|
||||||
|
|
||||||
|
|
@ -128,8 +130,6 @@ class Game:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.clouds.update()
|
self.clouds.update()
|
||||||
self.clouds.render(surface=self.display, offset=render_scroll)
|
self.clouds.render(surface=self.display, offset=render_scroll)
|
||||||
self.tilemap.render(surface=self.display, offset=render_scroll)
|
self.tilemap.render(surface=self.display, offset=render_scroll)
|
||||||
|
|
@ -147,13 +147,11 @@ class Game:
|
||||||
for particle in self.particles.copy():
|
for particle in self.particles.copy():
|
||||||
kill: bool = particle.update()
|
kill: bool = particle.update()
|
||||||
particle.render(self.display, offset=render_scroll)
|
particle.render(self.display, offset=render_scroll)
|
||||||
if particle.type == 'leaf':
|
if particle.type == "leaf":
|
||||||
particle.pos[0] += math.sin(particle.animation.frame * 0.035) * 0.3
|
particle.pos[0] += math.sin(particle.animation.frame * 0.035) * 0.3
|
||||||
if kill:
|
if kill:
|
||||||
self.particles.remove(particle)
|
self.particles.remove(particle)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
if event.type == pygame.QUIT:
|
if event.type == pygame.QUIT:
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
@ -164,7 +162,9 @@ class Game:
|
||||||
self.movement[0] = True
|
self.movement[0] = True
|
||||||
if event.key == pygame.K_d:
|
if event.key == pygame.K_d:
|
||||||
self.movement[1] = True
|
self.movement[1] = True
|
||||||
if event.key == pygame.K_SPACE or event.key == pygame.K_w: # and not self.isJumping
|
if (
|
||||||
|
event.key == pygame.K_SPACE or event.key == pygame.K_w
|
||||||
|
): # and not self.isJumping
|
||||||
self.player.jump()
|
self.player.jump()
|
||||||
|
|
||||||
if event.key == pygame.K_m:
|
if event.key == pygame.K_m:
|
||||||
|
|
@ -195,7 +195,5 @@ class Game:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
game = Game()
|
game = Game()
|
||||||
game.run()
|
game.run()
|
||||||
|
|
@ -105,6 +105,7 @@ class PhysicsEntity:
|
||||||
image = self.e_type + "/" + self.action
|
image = self.e_type + "/" + self.action
|
||||||
self.animation = self.game.assets[image].copy()
|
self.animation = self.game.assets[image].copy()
|
||||||
|
|
||||||
|
|
||||||
class Enemy(PhysicsEntity):
|
class Enemy(PhysicsEntity):
|
||||||
def __init__(self, game, pos: tuple[float, float], size: tuple[int, int]):
|
def __init__(self, game, pos: tuple[float, float], size: tuple[int, int]):
|
||||||
super().__init__(game, "enemy", pos, size)
|
super().__init__(game, "enemy", pos, size)
|
||||||
|
|
@ -113,13 +114,26 @@ class Enemy(PhysicsEntity):
|
||||||
|
|
||||||
def update(self, tilemap, movement: tuple[float, float] = (0, 0)):
|
def update(self, tilemap, movement: tuple[float, float] = (0, 0)):
|
||||||
if self.walking:
|
if self.walking:
|
||||||
|
if tilemap.solid_check(
|
||||||
|
(self.rect().centerx + (-7 if self.flip else 7), self.pos[1] + 23)
|
||||||
|
):
|
||||||
|
if self.collisions.right or self.collisions.left:
|
||||||
|
self.flip = not self.flip
|
||||||
|
else:
|
||||||
movement = (movement[0] - 0.5 if self.flip else 0.5, movement[1])
|
movement = (movement[0] - 0.5 if self.flip else 0.5, movement[1])
|
||||||
|
else:
|
||||||
|
self.flip = not self.flip
|
||||||
self.walking = max(0, self.walking - 1)
|
self.walking = max(0, self.walking - 1)
|
||||||
elif random.random() < 0.01:
|
elif random.random() < 0.01:
|
||||||
self.walking = random.randint(30, 120)
|
self.walking = random.randint(30, 120)
|
||||||
|
|
||||||
super().update(tilemap, movement)
|
super().update(tilemap, movement)
|
||||||
|
|
||||||
|
if movement[0] != 0:
|
||||||
|
self.set_action("run")
|
||||||
|
else:
|
||||||
|
self.set_action("idle")
|
||||||
|
|
||||||
|
|
||||||
class Player(PhysicsEntity):
|
class Player(PhysicsEntity):
|
||||||
def __init__(self, game, pos: tuple[float, float], size: tuple[int, int]):
|
def __init__(self, game, pos: tuple[float, float], size: tuple[int, int]):
|
||||||
|
|
@ -199,7 +213,15 @@ class Player(PhysicsEntity):
|
||||||
0,
|
0,
|
||||||
]
|
]
|
||||||
|
|
||||||
self.game.particles.append(Particle(self.game, "particle", self.rect().center, particle_velocity, random.randint(0, 7)))
|
self.game.particles.append(
|
||||||
|
Particle(
|
||||||
|
self.game,
|
||||||
|
"particle",
|
||||||
|
self.rect().center,
|
||||||
|
particle_velocity,
|
||||||
|
random.randint(0, 7),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if self.velocity[0] > 0:
|
if self.velocity[0] > 0:
|
||||||
self.velocity[0] = max(self.velocity[0] - 0.1, 0)
|
self.velocity[0] = max(self.velocity[0] - 0.1, 0)
|
||||||
|
|
@ -220,7 +242,7 @@ class Player(PhysicsEntity):
|
||||||
self.air_time = 5
|
self.air_time = 5
|
||||||
self.jumps = max(0, self.jumps - 1)
|
self.jumps = max(0, self.jumps - 1)
|
||||||
|
|
||||||
elif self.jumps > 0:
|
elif self.jumps > 0: # and not self.dashing
|
||||||
self.velocity[1] = -3
|
self.velocity[1] = -3
|
||||||
self.jumps -= 1
|
self.jumps -= 1
|
||||||
self.air_time = 5
|
self.air_time = 5
|
||||||
|
|
@ -246,7 +268,6 @@ class Player(PhysicsEntity):
|
||||||
else:
|
else:
|
||||||
self.dashing = 60
|
self.dashing = 60
|
||||||
|
|
||||||
|
|
||||||
def render(self, surface, offset=(0, 0)):
|
def render(self, surface, offset=(0, 0)):
|
||||||
if abs(self.dashing) <= 50:
|
if abs(self.dashing) <= 50:
|
||||||
super().render(surface, offset)
|
super().render(surface, offset)
|
||||||
|
|
@ -4,12 +4,19 @@ from scripts.animation import Animation
|
||||||
|
|
||||||
|
|
||||||
class Particle:
|
class Particle:
|
||||||
def __init__(self, game, p_type: str, pos: tuple[float, float], velocity: list[float] = [0, 0], frame: int = 0):
|
def __init__(
|
||||||
|
self,
|
||||||
|
game,
|
||||||
|
p_type: str,
|
||||||
|
pos: tuple[float, float],
|
||||||
|
velocity: list[float] = [0, 0],
|
||||||
|
frame: int = 0,
|
||||||
|
):
|
||||||
self.game = game
|
self.game = game
|
||||||
self.type: str = p_type
|
self.type: str = p_type
|
||||||
self.pos: list[float] = list(pos)
|
self.pos: list[float] = list(pos)
|
||||||
self.velocity: list[float] = list(velocity)
|
self.velocity: list[float] = list(velocity)
|
||||||
self.animation: Animation = self.game.assets['particles/' + p_type].copy()
|
self.animation: Animation = self.game.assets["particles/" + p_type].copy()
|
||||||
self.animation.frame = frame
|
self.animation.frame = frame
|
||||||
|
|
||||||
def update(self) -> bool:
|
def update(self) -> bool:
|
||||||
|
|
@ -24,7 +31,9 @@ class Particle:
|
||||||
|
|
||||||
return kill
|
return kill
|
||||||
|
|
||||||
def render(self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)) -> None:
|
def render(
|
||||||
|
self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)
|
||||||
|
) -> None:
|
||||||
img = self.animation.img()
|
img = self.animation.img()
|
||||||
surface.blit(
|
surface.blit(
|
||||||
img,
|
img,
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,20 @@ AUTOTILE_MAP: dict[tuple[tuple[int, int], ...], int] = {
|
||||||
tuple(sorted([(1, 0), (-1, 0), (0, 1), (0, -1)])): 8,
|
tuple(sorted([(1, 0), (-1, 0), (0, 1), (0, -1)])): 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
NEIGHBOR_OFFSETS: list[tuple[int, int]] = [(-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (0, 0), (-1, 1), (0, 1), (1, 1)]
|
NEIGHBOR_OFFSETS: list[tuple[int, int]] = [
|
||||||
PHYSICS_TILES: set[str] = {'grass', 'stone'}
|
(-1, 0),
|
||||||
AUTOTILE_TYPES: set[str] = {'grass', 'stone'}
|
(-1, -1),
|
||||||
|
(0, -1),
|
||||||
|
(1, -1),
|
||||||
|
(1, 0),
|
||||||
|
(0, 0),
|
||||||
|
(-1, 1),
|
||||||
|
(0, 1),
|
||||||
|
(1, 1),
|
||||||
|
]
|
||||||
|
PHYSICS_TILES: set[str] = {"grass", "stone"}
|
||||||
|
AUTOTILE_TYPES: set[str] = {"grass", "stone"}
|
||||||
|
|
||||||
|
|
||||||
class Tilemap:
|
class Tilemap:
|
||||||
def __init__(self, game, tile_size: int = 16) -> None:
|
def __init__(self, game, tile_size: int = 16) -> None:
|
||||||
|
|
@ -25,7 +36,9 @@ class Tilemap:
|
||||||
self.tilemap: dict[str, Tile] = {}
|
self.tilemap: dict[str, Tile] = {}
|
||||||
self.offgrid_tiles: list[Tile] = []
|
self.offgrid_tiles: list[Tile] = []
|
||||||
|
|
||||||
def extract(self, id_pairs: list[tuple[str, int]], keep: bool = False) -> list[dict]:
|
def extract(
|
||||||
|
self, id_pairs: list[tuple[str, int]], keep: bool = False
|
||||||
|
) -> list[dict]:
|
||||||
matches: list[dict] = []
|
matches: list[dict] = []
|
||||||
for tile in self.offgrid_tiles.copy():
|
for tile in self.offgrid_tiles.copy():
|
||||||
if (tile.type, tile.variant) in id_pairs:
|
if (tile.type, tile.variant) in id_pairs:
|
||||||
|
|
@ -37,7 +50,10 @@ class Tilemap:
|
||||||
tilemap_tile = self.tilemap[loc]
|
tilemap_tile = self.tilemap[loc]
|
||||||
if (tilemap_tile.type, tilemap_tile.variant) in id_pairs:
|
if (tilemap_tile.type, tilemap_tile.variant) in id_pairs:
|
||||||
end_match = tilemap_tile.to_dict()
|
end_match = tilemap_tile.to_dict()
|
||||||
end_match["pos"] = [end_match['pos'][0] * self.tile_size, end_match['pos'][1] * self.tile_size]
|
end_match["pos"] = [
|
||||||
|
end_match["pos"][0] * self.tile_size,
|
||||||
|
end_match["pos"][1] * self.tile_size,
|
||||||
|
]
|
||||||
|
|
||||||
matches.append(end_match)
|
matches.append(end_match)
|
||||||
|
|
||||||
|
|
@ -47,48 +63,67 @@ class Tilemap:
|
||||||
|
|
||||||
def tiles_around(self, pos: tuple[float, float]) -> list[Tile]:
|
def tiles_around(self, pos: tuple[float, float]) -> list[Tile]:
|
||||||
tiles: list[Tile] = []
|
tiles: list[Tile] = []
|
||||||
tile_location: tuple[int, int] = (int(pos[0] // self.tile_size), int(pos[1] // self.tile_size))
|
tile_location: tuple[int, int] = (
|
||||||
|
int(pos[0] // self.tile_size),
|
||||||
|
int(pos[1] // self.tile_size),
|
||||||
|
)
|
||||||
for offset in NEIGHBOR_OFFSETS:
|
for offset in NEIGHBOR_OFFSETS:
|
||||||
check_location: str = str(tile_location[0] + offset[0]) + ';' + str(tile_location[1] + offset[1])
|
check_location: str = (
|
||||||
|
str(tile_location[0] + offset[0])
|
||||||
|
+ ";"
|
||||||
|
+ str(tile_location[1] + offset[1])
|
||||||
|
)
|
||||||
if check_location in self.tilemap:
|
if check_location in self.tilemap:
|
||||||
tiles.append(self.tilemap[check_location])
|
tiles.append(self.tilemap[check_location])
|
||||||
return tiles
|
return tiles
|
||||||
|
|
||||||
def save(self, path: str) -> None:
|
def save(self, path: str) -> None:
|
||||||
data: dict = {
|
data: dict = {
|
||||||
'tilemap': {
|
"tilemap": {k: tile.to_dict() for k, tile in self.tilemap.items()},
|
||||||
k: tile.to_dict() for k, tile in self.tilemap.items()
|
"tile_size": self.tile_size,
|
||||||
},
|
"offgrid": [tile.to_dict() for tile in self.offgrid_tiles],
|
||||||
'tile_size': self.tile_size,
|
|
||||||
'offgrid': [
|
|
||||||
tile.to_dict() for tile in self.offgrid_tiles
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
with open(path, 'w') as f:
|
with open(path, "w") as f:
|
||||||
json.dump(data, f)
|
json.dump(data, f)
|
||||||
|
|
||||||
|
|
||||||
def load(self, path: str) -> None:
|
def load(self, path: str) -> None:
|
||||||
with open(path, 'r') as f:
|
with open(path, "r") as f:
|
||||||
map_data = json.load(f)
|
map_data = json.load(f)
|
||||||
|
|
||||||
self.tile_size: int = map_data['tile_size']
|
self.tile_size: int = map_data["tile_size"]
|
||||||
|
|
||||||
# Grid-Tiles: Dict -> Tile-Objekte
|
# Grid-Tiles: Dict -> Tile-Objekte
|
||||||
self.tilemap: dict[str, Tile] = {}
|
self.tilemap: dict[str, Tile] = {}
|
||||||
for key, tile_data in map_data['tilemap'].items():
|
for key, tile_data in map_data["tilemap"].items():
|
||||||
self.tilemap[key] = Tile.from_dict(tile_data)
|
self.tilemap[key] = Tile.from_dict(tile_data)
|
||||||
|
|
||||||
# Offgrid-Tiles: Dict -> Tile-Objekte
|
# Offgrid-Tiles: Dict -> Tile-Objekte
|
||||||
self.offgrid_tiles: list[Tile] = [
|
self.offgrid_tiles: list[Tile] = [
|
||||||
Tile.from_dict(t) for t in map_data['offgrid']
|
Tile.from_dict(t) for t in map_data["offgrid"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def solid_check(self, pos: tuple[float, float]):
|
||||||
|
tile_loc = (
|
||||||
|
str(int(pos[0] // self.tile_size))
|
||||||
|
+ ";"
|
||||||
|
+ str(int(pos[1] // self.tile_size))
|
||||||
|
)
|
||||||
|
if tile_loc in self.tilemap:
|
||||||
|
if self.tilemap[tile_loc].type in PHYSICS_TILES:
|
||||||
|
return self.tilemap[tile_loc]
|
||||||
|
|
||||||
def physics_rects_around(self, pos: tuple[float, float]) -> list[pygame.Rect]:
|
def physics_rects_around(self, pos: tuple[float, float]) -> list[pygame.Rect]:
|
||||||
rects: list[pygame.Rect] = []
|
rects: list[pygame.Rect] = []
|
||||||
for tile in self.tiles_around(pos):
|
for tile in self.tiles_around(pos):
|
||||||
if tile.type in PHYSICS_TILES:
|
if tile.type in PHYSICS_TILES:
|
||||||
rects.append(pygame.Rect(tile.pos[0] * self.tile_size, tile.pos[1] * self.tile_size, self.tile_size, self.tile_size))
|
rects.append(
|
||||||
|
pygame.Rect(
|
||||||
|
tile.pos[0] * self.tile_size,
|
||||||
|
tile.pos[1] * self.tile_size,
|
||||||
|
self.tile_size,
|
||||||
|
self.tile_size,
|
||||||
|
)
|
||||||
|
)
|
||||||
return rects
|
return rects
|
||||||
|
|
||||||
def autotile(self):
|
def autotile(self):
|
||||||
|
|
@ -96,7 +131,9 @@ class Tilemap:
|
||||||
tile: Tile = self.tilemap[loc]
|
tile: Tile = self.tilemap[loc]
|
||||||
neighbors: set[tuple[int, int]] = set()
|
neighbors: set[tuple[int, int]] = set()
|
||||||
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
|
for shift in [(1, 0), (-1, 0), (0, -1), (0, 1)]:
|
||||||
check_loc: str = str(tile.pos[0] + shift[0]) + ';' + str(tile.pos[1] + shift[1])
|
check_loc: str = (
|
||||||
|
str(tile.pos[0] + shift[0]) + ";" + str(tile.pos[1] + shift[1])
|
||||||
|
)
|
||||||
if check_loc in self.tilemap:
|
if check_loc in self.tilemap:
|
||||||
if self.tilemap[check_loc].type == tile.type:
|
if self.tilemap[check_loc].type == tile.type:
|
||||||
neighbors.add(shift)
|
neighbors.add(shift)
|
||||||
|
|
@ -104,23 +141,30 @@ class Tilemap:
|
||||||
if (tile.type in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP):
|
if (tile.type in AUTOTILE_TYPES) and (neighbors in AUTOTILE_MAP):
|
||||||
tile.variant = AUTOTILE_MAP[neighbors]
|
tile.variant = AUTOTILE_MAP[neighbors]
|
||||||
|
|
||||||
def render(self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)) -> None:
|
def render(
|
||||||
|
self, surface: pygame.Surface, offset: tuple[float, float] = (0, 0)
|
||||||
|
) -> None:
|
||||||
for tile in self.offgrid_tiles:
|
for tile in self.offgrid_tiles:
|
||||||
surface.blit(
|
surface.blit(
|
||||||
self.game.assets[tile.type][tile.variant],
|
self.game.assets[tile.type][tile.variant],
|
||||||
(tile.pos[0] - offset[0], tile.pos[1] - offset[1])
|
(tile.pos[0] - offset[0], tile.pos[1] - offset[1]),
|
||||||
)
|
)
|
||||||
|
|
||||||
for x in range(offset[0] // self.tile_size,
|
for x in range(
|
||||||
(offset[0] + surface.get_width()) // self.tile_size + 1):
|
offset[0] // self.tile_size,
|
||||||
for y in range(offset[1] // self.tile_size,
|
(offset[0] + surface.get_width()) // self.tile_size + 1,
|
||||||
(offset[1] + surface.get_height()) // self.tile_size + 1):
|
):
|
||||||
location_key: str = str(x) + ';' + str(y)
|
for y in range(
|
||||||
|
offset[1] // self.tile_size,
|
||||||
|
(offset[1] + surface.get_height()) // self.tile_size + 1,
|
||||||
|
):
|
||||||
|
location_key: str = str(x) + ";" + str(y)
|
||||||
if location_key in self.tilemap:
|
if location_key in self.tilemap:
|
||||||
tile: Tile = self.tilemap[location_key]
|
tile: Tile = self.tilemap[location_key]
|
||||||
surface.blit(
|
surface.blit(
|
||||||
self.game.assets[tile.type][tile.variant],
|
self.game.assets[tile.type][tile.variant],
|
||||||
(
|
(
|
||||||
tile.pos[0] * self.tile_size - offset[0],
|
tile.pos[0] * self.tile_size - offset[0],
|
||||||
tile.pos[1] * self.tile_size - offset[1])
|
tile.pos[1] * self.tile_size - offset[1],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue