tileforge 0.1.0__py3-none-any.whl

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.
tileforge/__init__.py ADDED
@@ -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"]
tileforge/lib.py ADDED
@@ -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]
tileforge/map.py ADDED
@@ -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
tileforge/renderer.py ADDED
@@ -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}")
tileforge/tileset.py ADDED
@@ -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.0
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.8
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,10 @@
1
+ tileforge/__init__.py,sha256=EtlTnzgZN6YJuqJMsEEQdqHBBB4kT2Gx826TO4u8fwU,238
2
+ tileforge/lib.py,sha256=cA_q4zyT84VvihY7HplIixdTFkXhfenS1hBlgCziJ7c,117
3
+ tileforge/map.py,sha256=Q18uS27OoKfI0iaSD4vvKU0XDiYQJX-xlnjz0tUYng0,2115
4
+ tileforge/renderer.py,sha256=TqrTdukb2iEQEI8JYXKwesU5DXf5mPKINElsPxX8cAw,1773
5
+ tileforge/tileset.py,sha256=zZTMjZe7meX4TcZAi-4ZHnTWRWePUujX7iO2G3g5edc,3279
6
+ tileforge-0.1.0.dist-info/licenses/LICENSE,sha256=LichDYrCYbWRnHQbTTMx4cgHTvt4FM_tF7P5gPD9awk,1106
7
+ tileforge-0.1.0.dist-info/METADATA,sha256=MxiDOah5UCwUAcnukg9XtA_Z44A8gkTpxJ1IoWaq4OM,3800
8
+ tileforge-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
+ tileforge-0.1.0.dist-info/top_level.txt,sha256=j7NsqiR_3MN07Y013H_-Y3ypByKRsXWRepz6O9hT0iY,10
10
+ tileforge-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -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 @@
1
+ tileforge