tileforge 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026-present Joshua Hegedus <josh.hegedus@outlook.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: tileforge
3
+ Version: 0.1.1
4
+ Summary: A pygame rendering engine for top-down tile sets.
5
+ Author-email: Joshua Hegedus <josh.hegedus@outlook.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026-present Joshua Hegedus <josh.hegedus@outlook.com>
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
11
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
12
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
14
+ following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all copies or substantial
17
+ portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
20
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
21
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ Project-URL: Homepage, https://github.com/lsc-jh/tileforge
26
+ Project-URL: Repository, https://github.com/lsc-jh/tileforge
27
+ Keywords: rendering,engine,tile set,top-down
28
+ Classifier: Development Status :: 4 - Beta
29
+ Classifier: Programming Language :: Python :: 3.10
30
+ Classifier: Programming Language :: Python :: 3.11
31
+ Classifier: Programming Language :: Python :: 3.12
32
+ Classifier: Programming Language :: Python :: 3.13
33
+ Classifier: Programming Language :: Python :: 3.14
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Requires-Python: >=3.10
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: pygame-ce
39
+ Dynamic: license-file
40
+
41
+ # Tile Forge
42
+
43
+ This is a package for rendering top-down maps from a provided tile set image.
44
+
45
+ ## Class Diagram
46
+
47
+ ```mermaid
48
+ ---
49
+ title: Class Relations
50
+ ---
51
+
52
+ classDiagram
53
+ Renderer *-- Tileset
54
+ Renderer *-- Map
55
+ class Tileset {
56
+ + all_properties: dict[int, set[int]]
57
+ + tile_size: int
58
+ + path: str
59
+ + load()
60
+ + get_scaled_tiles(scale: int)
61
+ + has_property(tile_index: int, property_id: int): bool
62
+ + set_property(tile_index: int, property_index: int): void
63
+ + remove_property(tile_index: int, property_index: int): void
64
+ + toggle_property(tile_index: int, property_index: int): void
65
+ }
66
+
67
+ class Map {
68
+ + width: int
69
+ + height: int
70
+ + layers: list[Layer]
71
+ + set_layers(layers: list[Layer])
72
+ + set_layer_count(count: int)
73
+ + add_layer(layer: Layer)
74
+ + cell_has_property(tileset: Tileset, pos: tuple[int, int], property_id: int): bool
75
+ }
76
+
77
+ class Renderer {
78
+ - __map: Map
79
+ - __tileset: Tileset
80
+ + render_tile_size: int
81
+ + tiles: list[Surface]
82
+ + set_render_scale(scale: int)
83
+ + render(surface: Surface, offset: [int, int], callback): void
84
+ }
85
+ ```
86
+
87
+ ## Usage
88
+
89
+ ```python
90
+ import pygame
91
+ from tileforge import Tileset, Map, Renderer
92
+
93
+ pygame.init()
94
+
95
+ # You need a pygame surface to render the map, so make sure to initialize pygame first
96
+ surface = pygame.Surface((640, 480)) # Create a surface to render on
97
+
98
+ # Load the tileset and map
99
+ tileset = Tileset("path/to/tileset.png", 32)
100
+ tileset.load()
101
+
102
+ # Create a map with the loaded tileset
103
+ map = Map(10, 10) # 10x10
104
+
105
+ # Create a renderer and render the map
106
+ renderer = Renderer(tileset, map)
107
+ renderer.render(surface)
108
+ ```
@@ -0,0 +1,68 @@
1
+ # Tile Forge
2
+
3
+ This is a package for rendering top-down maps from a provided tile set image.
4
+
5
+ ## Class Diagram
6
+
7
+ ```mermaid
8
+ ---
9
+ title: Class Relations
10
+ ---
11
+
12
+ classDiagram
13
+ Renderer *-- Tileset
14
+ Renderer *-- Map
15
+ class Tileset {
16
+ + all_properties: dict[int, set[int]]
17
+ + tile_size: int
18
+ + path: str
19
+ + load()
20
+ + get_scaled_tiles(scale: int)
21
+ + has_property(tile_index: int, property_id: int): bool
22
+ + set_property(tile_index: int, property_index: int): void
23
+ + remove_property(tile_index: int, property_index: int): void
24
+ + toggle_property(tile_index: int, property_index: int): void
25
+ }
26
+
27
+ class Map {
28
+ + width: int
29
+ + height: int
30
+ + layers: list[Layer]
31
+ + set_layers(layers: list[Layer])
32
+ + set_layer_count(count: int)
33
+ + add_layer(layer: Layer)
34
+ + cell_has_property(tileset: Tileset, pos: tuple[int, int], property_id: int): bool
35
+ }
36
+
37
+ class Renderer {
38
+ - __map: Map
39
+ - __tileset: Tileset
40
+ + render_tile_size: int
41
+ + tiles: list[Surface]
42
+ + set_render_scale(scale: int)
43
+ + render(surface: Surface, offset: [int, int], callback): void
44
+ }
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ```python
50
+ import pygame
51
+ from tileforge import Tileset, Map, Renderer
52
+
53
+ pygame.init()
54
+
55
+ # You need a pygame surface to render the map, so make sure to initialize pygame first
56
+ surface = pygame.Surface((640, 480)) # Create a surface to render on
57
+
58
+ # Load the tileset and map
59
+ tileset = Tileset("path/to/tileset.png", 32)
60
+ tileset.load()
61
+
62
+ # Create a map with the loaded tileset
63
+ map = Map(10, 10) # 10x10
64
+
65
+ # Create a renderer and render the map
66
+ renderer = Renderer(tileset, map)
67
+ renderer.render(surface)
68
+ ```
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "tileforge"
3
+ version = "0.1.1"
4
+ description = "A pygame rendering engine for top-down tile sets."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = { file = "LICENSE" }
8
+ keywords = ["rendering", "engine", "tile set", "top-down"]
9
+ authors = [
10
+ { name = "Joshua Hegedus", email = "josh.hegedus@outlook.com" },
11
+ ]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Programming Language :: Python :: 3.10",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
19
+ "License :: OSI Approved :: MIT License",
20
+ ]
21
+
22
+ dependencies = [
23
+ "pygame-ce"
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/lsc-jh/tileforge"
28
+ Repository = "https://github.com/lsc-jh/tileforge"
29
+
30
+ [build-system]
31
+ requires = ["setuptools>=61.0"]
32
+ build-backend = "setuptools.build_meta"
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,9 @@
1
+ # SPDX-FileCopyrightText: 2026-present Joshua Hegedus <josh.hegedus@outlook.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ from .tileset import Tileset
6
+ from .renderer import Renderer
7
+ from .map import Map
8
+
9
+ __all__ = ["Tileset", "Renderer", "Map"]
@@ -0,0 +1,4 @@
1
+ from typing import Callable
2
+
3
+ Layer = list[list[tuple[int, int]]]
4
+ DrawCallback = Callable[[int, int, int, int], None]
@@ -0,0 +1,61 @@
1
+ from .lib import Layer
2
+ from .tileset import Tileset
3
+
4
+
5
+ class Map:
6
+ def __init__(self, width: int, height: int):
7
+ self.__layer_count = 0
8
+ self.__layers: list[Layer] = []
9
+ self.__width = width
10
+ self.__height = height
11
+
12
+ @property
13
+ def width(self) -> int:
14
+ return self.__width
15
+
16
+ @property
17
+ def height(self) -> int:
18
+ return self.__height
19
+
20
+ @property
21
+ def layers(self) -> list[Layer]:
22
+ return self.__layers
23
+
24
+ def set_layer_count(self, count: int) -> None:
25
+ if count < self.__layer_count:
26
+ self.__layers = self.__layers[:count]
27
+ else:
28
+ for _ in range(count - self.__layer_count):
29
+ self.__layers.append([[(0, 0) for _ in range(self.__width)] for _ in range(self.__height)])
30
+
31
+ self.__layer_count = count
32
+
33
+ def set_layers(self, layers: list[Layer]) -> None:
34
+ self.__layers = layers
35
+ self.__layer_count = len(layers)
36
+
37
+ def add_layer(self) -> None:
38
+ self.set_layer_count(self.__layer_count + 1)
39
+
40
+ def cell_has_property(self, tileset: Tileset, pos: tuple[int, int], property_id: int) -> bool:
41
+ """Check if any tile in the cell at (x, y) across all layers has the specified property."""
42
+ x, y = pos
43
+ for layer in self.__layers:
44
+ if y >= len(layer) or x >= len(layer[y]):
45
+ continue
46
+ index, _ = layer[y][x]
47
+ if tileset.has_property(index, property_id):
48
+ return True
49
+ return False
50
+
51
+ def __getitem__(self, item: tuple[int, int, int]) -> tuple[int, int]:
52
+ layer, x, y = item
53
+ if layer >= self.__layer_count or x >= self.__width or y >= self.__height:
54
+ raise IndexError("Layer, x, or y index out of bounds")
55
+ return self.__layers[layer][y][x]
56
+
57
+ def __setitem__(self, key: tuple[int, int, int], value: tuple[int, int]) -> None:
58
+ layer, x, y = key
59
+ if layer >= self.__layer_count or x >= self.__width or y >= self.__height:
60
+ raise IndexError("Layer, x, or y index out of bounds")
61
+ self.__layers[layer][y][x] = value
@@ -0,0 +1,47 @@
1
+ from .lib import DrawCallback
2
+ from .tileset import Tileset
3
+ from .map import Map
4
+ import pygame
5
+ from pygame import Surface
6
+
7
+
8
+ class Renderer:
9
+ def __init__(self, tileset: Tileset, map_layout: Map, render_scale: int = 1):
10
+ self.__render_scale = render_scale
11
+ self.__tileset = tileset
12
+ self.__tiles = self.__tileset.get_scaled_tiles(render_scale)
13
+ self.__map = map_layout
14
+
15
+ def set_render_scale(self, new_scale: int):
16
+ self.__render_scale = new_scale
17
+ self.__tiles = self.__tileset.get_scaled_tiles(new_scale)
18
+
19
+ @property
20
+ def tiles(self) -> list[Surface]:
21
+ return self.__tiles
22
+
23
+ @property
24
+ def render_tile_size(self) -> int:
25
+ return self.__tileset.tile_size * self.__render_scale
26
+
27
+ def render(self, surface: Surface, offset: tuple[int, int] = (0, 0), callback: DrawCallback | None = None) -> None:
28
+ offset_x, offset_y = offset
29
+ for y in range(self.__map.height):
30
+ for x in range(self.__map.width):
31
+ draw_x = x * self.render_tile_size + offset_x
32
+ draw_y = y * self.render_tile_size + offset_y
33
+
34
+ for layer in self.__map.layers:
35
+ if y >= len(layer) or x >= len(layer[y]):
36
+ continue
37
+ index, rotation = layer[y][x]
38
+ if index >= len(self.__tiles):
39
+ continue
40
+ tile = pygame.transform.rotate(self.__tiles[index], -90 * rotation)
41
+ surface.blit(tile, (draw_x, draw_y))
42
+
43
+ if callback:
44
+ try:
45
+ callback(x, y, draw_x, draw_y)
46
+ except Exception as e:
47
+ print(f"Error in callback for tile ({x}, {y}): {e}")
@@ -0,0 +1,87 @@
1
+ import pygame
2
+
3
+
4
+ class Tileset:
5
+ def __init__(self, path: str, tile_size: int):
6
+ self.__path = path
7
+ self.__tile_size = tile_size
8
+ self.tiles: list[pygame.Surface] = []
9
+ self.__tile_properties: dict[int, set[int]] = {}
10
+
11
+ @property
12
+ def tile_size(self) -> int:
13
+ return self.__tile_size
14
+
15
+ @property
16
+ def path(self) -> str:
17
+ return self.__path
18
+
19
+ @property
20
+ def all_properties(self) -> dict[int, set[int]]:
21
+ return self.__tile_properties
22
+
23
+ def __load_tileset(self):
24
+ image = pygame.image.load(self.__path).convert_alpha()
25
+ tiles = []
26
+ w, h = image.get_size()
27
+
28
+ for y in range(0, h - self.__tile_size + 1, self.__tile_size):
29
+ for x in range(0, w - self.__tile_size + 1, self.__tile_size):
30
+ tile = image.subsurface((x, y, self.__tile_size, self.__tile_size))
31
+ tiles.append(tile)
32
+
33
+ empty_tile = pygame.Surface((self.__tile_size, self.__tile_size), pygame.SRCALPHA)
34
+ tiles.insert(0, empty_tile)
35
+
36
+ return tiles
37
+
38
+ def load(self):
39
+ self.tiles = self.__load_tileset()
40
+
41
+ def get_scaled_tiles(self, scale: int) -> list[pygame.Surface]:
42
+ return [pygame.transform.scale(tile, (self.__tile_size * scale, self.__tile_size * scale)) for tile in
43
+ self.tiles]
44
+
45
+ def has_property(self, tile_index: int, property_id: int) -> bool:
46
+ return tile_index in self.__tile_properties.get(property_id, set())
47
+
48
+ def serialize_properties(self):
49
+ return {str(index): list(props) for index, props in self.__tile_properties.items()}
50
+
51
+ def deserialize_properties(self, properties: dict[str, list[int]]):
52
+ self.__tile_properties = {int(index): set(props) for index, props in properties.items()}
53
+
54
+ def set_properties(self, properties: dict[int, set[int]]):
55
+ self.__tile_properties = properties
56
+
57
+ def set_property(self, tile_index: int, property_id: int):
58
+ if property_id not in self.__tile_properties:
59
+ self.__tile_properties[property_id] = set()
60
+ self.__tile_properties[property_id].add(tile_index)
61
+
62
+ def remove_property(self, tile_index: int, property_id: int):
63
+ if property_id in self.__tile_properties:
64
+ self.__tile_properties[property_id].discard(tile_index)
65
+ if not self.__tile_properties[property_id]:
66
+ del self.__tile_properties[property_id]
67
+
68
+ def toggle_property(self, tile_index: int, property_id: int):
69
+ if property_id not in self.__tile_properties:
70
+ self.__tile_properties[property_id] = set()
71
+ if tile_index in self.__tile_properties[property_id]:
72
+ self.__tile_properties[property_id].remove(tile_index)
73
+ if not self.__tile_properties[property_id]:
74
+ del self.__tile_properties[property_id]
75
+ else:
76
+ self.__tile_properties[property_id].add(tile_index)
77
+
78
+ def get_properties(self, property_id: int) -> set[int]:
79
+ return self.__tile_properties.get(property_id, set())
80
+
81
+ def set_tile_size(self, new_size: int):
82
+ self.__tile_size = new_size
83
+ self.tiles = self.__load_tileset()
84
+
85
+ def set_path(self, new_path: str):
86
+ self.__path = new_path
87
+ self.tiles = self.__load_tileset()
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: tileforge
3
+ Version: 0.1.1
4
+ Summary: A pygame rendering engine for top-down tile sets.
5
+ Author-email: Joshua Hegedus <josh.hegedus@outlook.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026-present Joshua Hegedus <josh.hegedus@outlook.com>
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
11
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
12
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
14
+ following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all copies or substantial
17
+ portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
20
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
21
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ Project-URL: Homepage, https://github.com/lsc-jh/tileforge
26
+ Project-URL: Repository, https://github.com/lsc-jh/tileforge
27
+ Keywords: rendering,engine,tile set,top-down
28
+ Classifier: Development Status :: 4 - Beta
29
+ Classifier: Programming Language :: Python :: 3.10
30
+ Classifier: Programming Language :: Python :: 3.11
31
+ Classifier: Programming Language :: Python :: 3.12
32
+ Classifier: Programming Language :: Python :: 3.13
33
+ Classifier: Programming Language :: Python :: 3.14
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Requires-Python: >=3.10
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: pygame-ce
39
+ Dynamic: license-file
40
+
41
+ # Tile Forge
42
+
43
+ This is a package for rendering top-down maps from a provided tile set image.
44
+
45
+ ## Class Diagram
46
+
47
+ ```mermaid
48
+ ---
49
+ title: Class Relations
50
+ ---
51
+
52
+ classDiagram
53
+ Renderer *-- Tileset
54
+ Renderer *-- Map
55
+ class Tileset {
56
+ + all_properties: dict[int, set[int]]
57
+ + tile_size: int
58
+ + path: str
59
+ + load()
60
+ + get_scaled_tiles(scale: int)
61
+ + has_property(tile_index: int, property_id: int): bool
62
+ + set_property(tile_index: int, property_index: int): void
63
+ + remove_property(tile_index: int, property_index: int): void
64
+ + toggle_property(tile_index: int, property_index: int): void
65
+ }
66
+
67
+ class Map {
68
+ + width: int
69
+ + height: int
70
+ + layers: list[Layer]
71
+ + set_layers(layers: list[Layer])
72
+ + set_layer_count(count: int)
73
+ + add_layer(layer: Layer)
74
+ + cell_has_property(tileset: Tileset, pos: tuple[int, int], property_id: int): bool
75
+ }
76
+
77
+ class Renderer {
78
+ - __map: Map
79
+ - __tileset: Tileset
80
+ + render_tile_size: int
81
+ + tiles: list[Surface]
82
+ + set_render_scale(scale: int)
83
+ + render(surface: Surface, offset: [int, int], callback): void
84
+ }
85
+ ```
86
+
87
+ ## Usage
88
+
89
+ ```python
90
+ import pygame
91
+ from tileforge import Tileset, Map, Renderer
92
+
93
+ pygame.init()
94
+
95
+ # You need a pygame surface to render the map, so make sure to initialize pygame first
96
+ surface = pygame.Surface((640, 480)) # Create a surface to render on
97
+
98
+ # Load the tileset and map
99
+ tileset = Tileset("path/to/tileset.png", 32)
100
+ tileset.load()
101
+
102
+ # Create a map with the loaded tileset
103
+ map = Map(10, 10) # 10x10
104
+
105
+ # Create a renderer and render the map
106
+ renderer = Renderer(tileset, map)
107
+ renderer.render(surface)
108
+ ```
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/tileforge/__init__.py
5
+ src/tileforge/lib.py
6
+ src/tileforge/map.py
7
+ src/tileforge/renderer.py
8
+ src/tileforge/tileset.py
9
+ src/tileforge.egg-info/PKG-INFO
10
+ src/tileforge.egg-info/SOURCES.txt
11
+ src/tileforge.egg-info/dependency_links.txt
12
+ src/tileforge.egg-info/requires.txt
13
+ src/tileforge.egg-info/top_level.txt
14
+ tests/test_tileset.py
@@ -0,0 +1 @@
1
+ pygame-ce
@@ -0,0 +1 @@
1
+ tileforge
@@ -0,0 +1,118 @@
1
+ import pytest
2
+ import pygame
3
+
4
+ from tileforge import Tileset
5
+
6
+
7
+ @pytest.fixture(scope="module", autouse=True)
8
+ def pygame_init():
9
+ pygame.init()
10
+ pygame.display.set_mode((1, 1))
11
+ yield
12
+ pygame.quit()
13
+
14
+
15
+ @pytest.fixture
16
+ def mock_tileset(monkeypatch):
17
+ tile_size = 8
18
+ surface = pygame.Surface((16, 16), pygame.SRCALPHA)
19
+
20
+ def mock_load(path):
21
+ return surface
22
+
23
+ monkeypatch.setattr(pygame.image, "load", mock_load)
24
+
25
+ ts = Tileset("fake_path.png", tile_size)
26
+ ts.load()
27
+ return ts
28
+
29
+
30
+ def test_load_tiles_count(mock_tileset):
31
+ assert len(mock_tileset.tiles) == 5
32
+
33
+
34
+ def test_empty_tile_is_first(mock_tileset):
35
+ first_tile = mock_tileset.tiles[0]
36
+ assert first_tile.get_size() == (mock_tileset.tile_size, mock_tileset.tile_size)
37
+
38
+
39
+ def test_scaled_tiles(mock_tileset):
40
+ scale = 2
41
+ scaled = mock_tileset.get_scaled_tiles(scale)
42
+
43
+ assert len(scaled) == len(mock_tileset.tiles)
44
+ for tile in scaled:
45
+ assert tile.get_size() == (
46
+ mock_tileset.tile_size * scale,
47
+ mock_tileset.tile_size * scale,
48
+ )
49
+
50
+
51
+ def test_set_and_has_property(mock_tileset):
52
+ mock_tileset.set_property(100, 1)
53
+
54
+ assert mock_tileset.has_property(100, 1)
55
+ assert not mock_tileset.has_property(200, 1)
56
+
57
+
58
+ def test_remove_property(mock_tileset):
59
+ mock_tileset.set_property(1, 100)
60
+ mock_tileset.remove_property(1, 100)
61
+
62
+ assert not mock_tileset.has_property(1, 100)
63
+
64
+
65
+ def test_toggle_property(mock_tileset):
66
+ mock_tileset.toggle_property(1, 100)
67
+ assert mock_tileset.has_property(1, 100)
68
+
69
+ mock_tileset.toggle_property(1, 100)
70
+ assert not mock_tileset.has_property(1, 100)
71
+
72
+
73
+ def test_get_properties(mock_tileset):
74
+ mock_tileset.set_property(1, 100)
75
+ mock_tileset.set_property(2, 100)
76
+
77
+ props = mock_tileset.get_properties(100)
78
+ assert props == {1, 2}
79
+
80
+
81
+ def test_serialize_deserialize(mock_tileset):
82
+ mock_tileset.set_property(1, 100)
83
+ mock_tileset.set_property(2, 200)
84
+
85
+ data = mock_tileset.serialize_properties()
86
+
87
+ new_ts = Tileset("fake_path.png", 8)
88
+ new_ts.deserialize_properties(data)
89
+
90
+ assert new_ts.has_property(1, 100)
91
+ assert new_ts.has_property(2, 200)
92
+
93
+
94
+ def test_set_tile_size(monkeypatch):
95
+ surface = pygame.Surface((16, 16), pygame.SRCALPHA)
96
+
97
+ monkeypatch.setattr(pygame.image, "load", lambda _: surface)
98
+
99
+ ts = Tileset("fake.png", 8)
100
+ ts.load()
101
+
102
+ ts.set_tile_size(4)
103
+
104
+ assert ts.tile_size == 4
105
+ assert len(ts.tiles) == 17
106
+
107
+
108
+ def test_set_path(monkeypatch):
109
+ surface = pygame.Surface((16, 16), pygame.SRCALPHA)
110
+
111
+ monkeypatch.setattr(pygame.image, "load", lambda _: surface)
112
+
113
+ ts = Tileset("old.png", 8)
114
+ ts.load()
115
+
116
+ ts.set_path("new.png")
117
+
118
+ assert ts.path == "new.png"