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 +9 -0
- tileforge/lib.py +4 -0
- tileforge/map.py +61 -0
- tileforge/renderer.py +47 -0
- tileforge/tileset.py +87 -0
- tileforge-0.1.0.dist-info/METADATA +108 -0
- tileforge-0.1.0.dist-info/RECORD +10 -0
- tileforge-0.1.0.dist-info/WHEEL +5 -0
- tileforge-0.1.0.dist-info/licenses/LICENSE +18 -0
- tileforge-0.1.0.dist-info/top_level.txt +1 -0
tileforge/__init__.py
ADDED
tileforge/lib.py
ADDED
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,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
|