tilemap-tools 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.
@@ -0,0 +1,2 @@
1
+ """Outils pour créer et manipuler des tilemaps"""
2
+ __version__ = "0.1.0"
tilemap_tools/cli.py ADDED
@@ -0,0 +1,76 @@
1
+ """Point d'entrée principal pour la CLI tilemap"""
2
+ import argparse
3
+ import sys
4
+ from .create_modele import main as create_main
5
+ from .view import main as view_main
6
+ from os import getcwd, listdir, remove
7
+ from os.path import exists, join, dirname
8
+
9
+ class Check_colors(argparse.Action):
10
+ def __call__(self, parser, namespace, values, option_string=None):
11
+ if len(values) < 2: # pyright: ignore[reportArgumentType]
12
+ parser.error("Vous devez fournir au moins 2 couleurs différentes.")
13
+ elif len(values) > 16: # pyright: ignore[reportArgumentType]
14
+ parser.error("Vous ne pouvez pas dessiner un modèle avec plus de 16 couleurs.")
15
+ else:
16
+ tests = []
17
+ for test in values: # pyright: ignore[reportOptionalIterable]
18
+ if test not in tests:
19
+ tests.append(test)
20
+ else:
21
+ parser.error("Les couleurs doivent êtres toutes différentes.")
22
+
23
+ setattr(namespace, self.dest, values)
24
+
25
+ class Check_fichier(argparse.Action):
26
+ def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: str, option_string: str | None = None) -> None:
27
+ for name in [".mdl", ".map"]:
28
+ if exists(join(getcwd(), values + name)):
29
+ break
30
+ else:
31
+ parser.error("Vous devez proposer un fichier valide (soit .mdl, soit .map).")
32
+
33
+ setattr(namespace, self.dest, values)
34
+
35
+ def main():
36
+ dossier_commande = getcwd()
37
+ parser = argparse.ArgumentParser(
38
+ description='Outils pour créer et manipuler des tilemaps (avec pyxel)',
39
+ usage='tilemap <command> [options]'
40
+ )
41
+
42
+ subparsers = parser.add_subparsers(dest='command', help='Commandes disponibles')
43
+
44
+ # Commande create
45
+ create_parser = subparsers.add_parser('create', help='Créer une nouvelle tilemap ou un nouveau modèle de tilemap.')
46
+ create_subparser = create_parser.add_subparsers(dest='type', help="Le type de fichier que tu veut créer.")
47
+
48
+ # Créer un modèle
49
+ create_mdl_parser = create_subparser.add_parser("modele", help='Pour créer un modèle.')
50
+ create_mdl_parser.add_argument('taille', type=int, help="La taille d'un côté du modèle.")
51
+ create_mdl_parser.add_argument('output', required=False, type=str, help='Fichier de sortie', default="mon_modèle")
52
+ create_mdl_parser.add_argument('-n', '--nb-tuiles', type=int, help="Le nombre de tuiles sur le modèle(influe sur la génération de tilemap)", choices=[3, 4], default= 3)
53
+ create_mdl_parser.add_argument('-c', "--couleurs", nargs="+", action=Check_colors, help="Les couleurs que tu voudrait utiliser (au moins 2 avec leurs codes hex: comme FFFFFF ou 06dd2e)", metavar="HEX", default=None)
54
+
55
+ view_parser = subparsers.add_parser('view', help="Permet de consulter un fichier de modèle ou de tilemap.")
56
+ view_parser.add_argument('fichier', type=str, action=Check_fichier, help="Le fichier que vous voulez consulter (.mdl ou .map)")
57
+
58
+ subparsers.add_parser("clean", help="Permet de vider le fichier temporaire qui à peut être été rempli au cours d'erreurs.")
59
+
60
+ # Parse les arguments
61
+ args = parser.parse_args()
62
+
63
+ # Exécuter la commande appropriée
64
+ if args.command == 'create_model':
65
+ create_main(args.taille, args.nb_tuiles, args.output, args.couleurs, dossier_commande)
66
+ elif args.command == 'view':
67
+ view_main(args.fichier, dossier_commande)
68
+ elif args.command == 'clean':
69
+ dossier = join(dirname(__file__), "pyxres_bin")
70
+ for file_name in listdir(dossier):
71
+ remove(join(dossier, file_name))
72
+ else:
73
+ parser.print_help()
74
+
75
+ if __name__ == '__main__':
76
+ main()
tilemap_tools/core.py ADDED
@@ -0,0 +1,70 @@
1
+ """Fonctions principales pour manipuler les tilemaps"""
2
+
3
+ class Tilemap:
4
+ def __init__(self, width, height, default_tile=0):
5
+ self.width = width
6
+ self.height = height
7
+ self.map = [[default_tile for _ in range(width)] for _ in range(height)]
8
+
9
+ def set_tile(self, x, y, tile_id):
10
+ """Définit une tile à une position donnée"""
11
+ if 0 <= x < self.width and 0 <= y < self.height:
12
+ self.map[y][x] = tile_id
13
+ else:
14
+ raise ValueError(f"Position ({x}, {y}) hors limites")
15
+
16
+ def get_tile(self, x, y):
17
+ """Récupère la tile à une position donnée"""
18
+ if 0 <= x < self.width and 0 <= y < self.height:
19
+ return self.map[y][x]
20
+ raise ValueError(f"Position ({x}, {y}) hors limites")
21
+
22
+ def save_csv(self, filename):
23
+ """Sauvegarde la tilemap en CSV"""
24
+ with open(filename, 'w') as f:
25
+ for row in self.map:
26
+ f.write(','.join(map(str, row)) + '\n')
27
+
28
+ def save_json(self, filename):
29
+ """Sauvegarde la tilemap en JSON"""
30
+ import json
31
+ data = {
32
+ 'width': self.width,
33
+ 'height': self.height,
34
+ 'tiles': self.map
35
+ }
36
+ with open(filename, 'w') as f:
37
+ json.dump(data, f, indent=2)
38
+
39
+ @staticmethod
40
+ def load_csv(filename):
41
+ """Charge une tilemap depuis un CSV"""
42
+ with open(filename, 'r') as f:
43
+ lines = f.readlines()
44
+
45
+ map_data = []
46
+ for line in lines:
47
+ row = [int(x.strip()) for x in line.strip().split(',') if x.strip()]
48
+ if row:
49
+ map_data.append(row)
50
+
51
+ if not map_data:
52
+ raise ValueError("Fichier CSV vide")
53
+
54
+ height = len(map_data)
55
+ width = len(map_data[0])
56
+
57
+ tilemap = Tilemap(width, height)
58
+ tilemap.map = map_data
59
+ return tilemap
60
+
61
+ @staticmethod
62
+ def load_json(filename):
63
+ """Charge une tilemap depuis un JSON"""
64
+ import json
65
+ with open(filename, 'r') as f:
66
+ data = json.load(f)
67
+
68
+ tilemap = Tilemap(data['width'], data['height'])
69
+ tilemap.map = data['tiles']
70
+ return tilemap
@@ -0,0 +1,68 @@
1
+ """Commande pour créer des modèles de tilemaps"""
2
+ from subprocess import run
3
+ from os.path import dirname, join, exists
4
+ from os import remove
5
+ from time import time, sleep
6
+ from pyxel import load, init, images, quit as px_quit, colors as col, Image
7
+
8
+ def main(taille:int, nb_tiles:int, file:str, colors: None | list[str], dossier:str): # pyright: ignore[reportRedeclaration]
9
+ print()
10
+ print("""Vous devez créer un modèle pour tilemap:
11
+ - Vous devez créer un carré contenant les 4 coins, les 4 cotés et le milieu du modèle
12
+ - Ce carré sera découpé en 9 (les 4 coins, les 4 cotés et le millieu) pour constituer la base pour une future tilemap.
13
+ - Après avoir créé votre modèle complet pensez à l'enregistrer puis fermer la fenètre.
14
+ - Le programme s'occupera de faire le reste pour vous et de l'enregistrer au nom que vous avez choisit (par défault modèle).""")
15
+
16
+ sleep(1)
17
+
18
+ init(0, 0)
19
+
20
+ nom_fichier_res = f'{join(dirname(__file__), "pyxres_bin", f"bin_{int(time() * 10)}")}'
21
+
22
+ if colors is None:
23
+ colors: list[str] = []
24
+ for color in col.to_list():
25
+ colors.append(hex(color)[2:])
26
+ else:
27
+ open(nom_fichier_res + ".pyxpal", "w").write("\n".join(map(str, colors)))
28
+
29
+ ok = False
30
+ while not ok:
31
+ run(["pyxel", "edit", nom_fichier_res])
32
+
33
+ try:
34
+ load(f'{nom_fichier_res}.pyxres')
35
+ img = images[0]
36
+ tilemap: list[list[int]] = []
37
+ nb_0 = 0
38
+ for x in range(taille * nb_tiles):
39
+ ligne:list[int] = []
40
+ for y in range(taille * nb_tiles):
41
+ ligne.append(img.pget(x, y))
42
+ nb_0 += 1 if img.pget(x, y) == 0 else 0
43
+ tilemap.append(ligne)
44
+ if nb_0 != (taille * nb_tiles) ** 2:
45
+ ok = True
46
+ test = Image(taille * nb_tiles, taille * nb_tiles)
47
+ test.blt(0, 0, img, 0, 0, taille * nb_tiles, taille * nb_tiles)
48
+ test.save(nom_fichier_res + ".png", 1)
49
+ with open(nom_fichier_res + ".png", "rb") as u:
50
+ img_png = u.read()
51
+ with open(join(dossier, file + ".mdl"), "wb") as f:
52
+ f.write(img_png + f",\n{taille},{nb_tiles},{"\n".join(map(str, colors))}".encode())
53
+ except Exception as e:
54
+ ok = False
55
+ print()
56
+ print("Il y à eu un problème avec le fichier, pensez à l'enregistrer avant de le fermer.")
57
+ print(f"Erreur : {e}")
58
+ print("Tentative suivante dans 1 seconde.")
59
+ sleep(1)
60
+
61
+ remove(fr"{nom_fichier_res}.pyxres")
62
+ remove(fr"{nom_fichier_res}.png")
63
+ if exists(nom_fichier_res + ".pyxpal"):
64
+ remove(fr"{nom_fichier_res}.pyxpal")
65
+
66
+ px_quit()
67
+
68
+
tilemap_tools/view.py ADDED
@@ -0,0 +1,159 @@
1
+ """
2
+ Commande pour voir le contenu du fichier de modèle ou du fichier de tilemap.
3
+ """
4
+ import tkinter as tk
5
+ from PIL import Image, ImageTk, ImageDraw
6
+ from os.path import join, exists, dirname, abspath
7
+ from os import remove
8
+ from time import time
9
+
10
+ class TilemapViewer:
11
+ def __init__(self, root: tk.Tk, image_path, nb_tiles, nom_fichier):
12
+ self.nb_tiles = nb_tiles
13
+
14
+ bg = "#6835c7"
15
+
16
+ # Charger l'image
17
+ self.original_image = Image.open(image_path)
18
+
19
+ # Créer une copie pour dessiner la grille
20
+ self.display_image = self.original_image.copy()
21
+ self.display_image = self.display_image.resize((256, 256), Image.NEAREST) # pyright: ignore[reportAttributeAccessIssue]
22
+ self.tile_size = 256 // nb_tiles
23
+ self.draw_grid()
24
+
25
+ # Convertir pour Tkinter
26
+ self.photo = ImageTk.PhotoImage(self.display_image)
27
+
28
+ frame = tk.Frame(root, bg=bg)
29
+ frame.pack(fill=tk.BOTH, expand= True)
30
+
31
+ tk.Label(frame, text= f"Voici le contenu du modèle\n{nom_fichier}.mdl", font=("Arial", 20, "bold"), bg=bg).pack()
32
+
33
+ # Canvas
34
+ self.canvas = tk.Canvas(
35
+ frame,
36
+ width=self.display_image.width,
37
+ height=self.display_image.height,
38
+ bg='lightgray'
39
+ )
40
+ self.canvas.pack(expand= True)
41
+
42
+ # Afficher l'image
43
+ self.canvas.create_image(128, 128, anchor=tk.CENTER, image=self.photo)
44
+
45
+ # Info label
46
+ self.info_label = tk.Label(frame, text="Cliquez sur une tuile\n", font=("Arial", 19), pady= 30, bg=bg)
47
+ self.info_label.pack()
48
+
49
+ # Événement de clic
50
+ self.canvas.bind("<Button-1>", self.on_tile_click)
51
+ self.canvas.bind("<B1-Motion>", self.on_tile_click)
52
+
53
+ def draw_grid(self):
54
+ """Dessine la grille sur l'image"""
55
+ draw = ImageDraw.Draw(self.display_image)
56
+
57
+ # Lignes verticales
58
+ for x in range(0, self.display_image.width, self.tile_size):
59
+ draw.line(
60
+ [(x, 0), (x, self.display_image.height)],
61
+ fill=(255, 0, 0, 128),
62
+ width=1
63
+ )
64
+
65
+ # Lignes horizontales
66
+ for y in range(0, self.display_image.height, self.tile_size):
67
+ draw.line(
68
+ [(0, y), (self.display_image.width, y)],
69
+ fill=(255, 0, 0, 128),
70
+ width=1
71
+ )
72
+
73
+ def on_tile_click(self, event):
74
+ """Gérer le clic sur une tuile"""
75
+ if event.x >= self.display_image.width or event.x <= 0 or event.y <= 0 or event.y >= self.display_image.height:
76
+ self.canvas.delete("highlight")
77
+ self.info_label.config(text="Cliquez sur une tuile\n")
78
+ return
79
+ tile_x = event.x // self.tile_size
80
+ tile_y = event.y // self.tile_size
81
+
82
+ if tile_x == 0:
83
+ x_position = "gauche"
84
+ elif tile_x == self.nb_tiles - 1:
85
+ x_position = "droite"
86
+ else:
87
+ x_position = None
88
+
89
+ if tile_y == 0:
90
+ y_position = "haut"
91
+ elif tile_y == self.nb_tiles - 1:
92
+ y_position = "bas"
93
+ else:
94
+ y_position = None
95
+
96
+ if (tile_x == 0 or tile_x == self.nb_tiles - 1) and (tile_y == 0 or tile_y == self.nb_tiles - 1):
97
+ nom = f"coin en {y_position} à {x_position}"
98
+ elif tile_x == 0 or tile_x == self.nb_tiles - 1 or tile_y == 0 or tile_y == self.nb_tiles - 1:
99
+ if x_position:
100
+ nom = f"coté {x_position if x_position == "gauche" else "droit"}"
101
+ else:
102
+ nom = f"{y_position}"
103
+ else:
104
+ nom = "millieu"
105
+
106
+ nom += f"\n({tile_x}, {tile_y})"
107
+
108
+ # Mettre à jour l'info
109
+ self.info_label.config(
110
+ text=f"Tuile sélectionnée : {nom}"
111
+ )
112
+
113
+ # Surligner la tuile
114
+ self.highlight_tile(tile_x, tile_y)
115
+
116
+ def highlight_tile(self, tile_x, tile_y):
117
+ """Surligner une tuile"""
118
+ # Effacer les anciens rectangles
119
+ self.canvas.delete("highlight")
120
+
121
+ # Dessiner un nouveau rectangle
122
+ x1 = tile_x * self.tile_size
123
+ y1 = tile_y * self.tile_size
124
+ x2 = x1 + self.tile_size
125
+ y2 = y1 + self.tile_size
126
+
127
+ self.canvas.create_rectangle(
128
+ x1, y1, x2, y2,
129
+ outline="yellow",
130
+ width=3,
131
+ tags="highlight"
132
+ )
133
+
134
+ def main(nom_fichier:str, dossier:str):
135
+ fichier_temp = join(abspath(dirname(__file__)), "pyxres_bin", f"bin_{int(time() * 10)}")
136
+
137
+ for name in [".mdl", ".map"]:
138
+ if exists(join(dossier, nom_fichier + name)):
139
+ fichier = join(dossier, nom_fichier + name)
140
+ extension = name
141
+ break
142
+
143
+ if extension == ".mdl":
144
+ with open(fichier, "rb") as f:
145
+ liste = f.read().split(b",")
146
+ img = liste[0]
147
+ nb_tuiles = int(liste[2].decode())
148
+ with open(fichier_temp + ".png", "wb") as f:
149
+ f.write(img)
150
+ root = tk.Tk()
151
+ root.title("Affichage de la tilemap.")
152
+ root.grid_rowconfigure([0, 1], weight=1)
153
+ root.geometry("500x500")
154
+
155
+ TilemapViewer(root, fichier_temp + ".png", nb_tuiles, nom_fichier)
156
+
157
+ root.mainloop()
158
+
159
+ remove(fichier_temp + ".png")
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: tilemap-tools
3
+ Version: 0.1.0
4
+ Summary: Outils CLI pour créer et manipuler des tilemaps
5
+ Home-page: https://github.com/Julosse27/tilemap-tools
6
+ Author: Julosse
7
+ Author-email: Julosse <julosse27110@gmail.com>
8
+ License: MIT
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Dynamic: author
13
+ Dynamic: home-page
14
+ Dynamic: license-file
15
+ Dynamic: requires-python
16
+
17
+ # Tilemap Tools
18
+
19
+ Outils en ligne de commande pour créer et manipuler des tilemaps en pixel art.
20
+
21
+ ## Installation
22
+ ```bash
23
+ pip install tilemap-tools
24
+ ```
25
+
26
+ ## Fonctionnalités
27
+
28
+ - ✨ Créer des modèles de tilemaps interactifs.
29
+ - 🎨 Des palettes max de 16 couleurs pour un modèle.
30
+ - 👁️ Visualisation et modification avec des grilles interactives
31
+ - 👾 Création de tilemaps (à terminer) avec un systrème complet de création de platformes
32
+ - 👨‍💻 Utilisation facile dans un programme utilisant pyxel (a faire)
33
+ - 💻 Modification à tout moment (encore à implémenter)
34
+
35
+ ## Utilisation
36
+
37
+ ### Créer un modèle
38
+ ```bash
39
+ tilemap create modele 9 -c FF0000 00FF00 0000FF
40
+ tilemap create modele 12 mon_modèle_perso -n 4
41
+ ```
42
+
43
+ ### Visualiser un modèle ou une tilemap
44
+ ```bash
45
+ tilemap view mon_modèle
46
+ ```
47
+
48
+ ### Vider les fichiers temporaires (peuvent s'accumuler au cours d'erreurs)
49
+ ```bash
50
+ tilemap clear
51
+ ```
52
+
53
+ ## Examples
54
+ ```bash
55
+ # Pour créer un modèle avec 3x3 tuiles de 9 pixels chacuns
56
+ tilemap create modele 9 niveau1
57
+
58
+ # Créer avec des couleurs personnalisées
59
+ tilemap create modele 9 niveau2 -c FF0000 00FF00 0000FF FFFF00
60
+
61
+ # Visualiser
62
+ tilemap view niveau1
63
+ ```
64
+
65
+ ## Dévellopement
66
+ ```bash
67
+ git clone https://github.com/Julosse27/tilemap-tools.git
68
+ cd tilemap-tools
69
+ pip install -e .
70
+ ```
71
+
72
+ ## License
73
+
74
+ MIT License - voir LICENSE pour plus de détails.
75
+
76
+ ## Auteur
77
+
78
+ Julosse - julosse27110@gmail.com
@@ -0,0 +1,10 @@
1
+ tilemap_tools/__init__.py,sha256=jlIS0AJLJPe4VCdWJmrEPVk2nUoYOQMRSXUHahGLW04,73
2
+ tilemap_tools/cli.py,sha256=3GVMMhTv3NRVouFkkwMTyYOj_zdY2UwiyzHN5q5e2T8,3795
3
+ tilemap_tools/core.py,sha256=MJKSst3av8NgI7zRTgMFM4RMIQ4fL7w_3Jl_kNHQmQA,2313
4
+ tilemap_tools/create_modele.py,sha256=JwbocxIvpFO8xYag1cnLjwQFGtIbnQCS_oXGnX0GQ9Q,2893
5
+ tilemap_tools/view.py,sha256=EEObK4OY0HNWXr99sw5-iXd290KojKAmoCj6z8zG69k,5509
6
+ tilemap_tools-0.1.0.dist-info/licenses/LICENSE,sha256=alZWv-FR5LCCJcddmFtpVYJuFmV_ysI0F88EyipxB78,1086
7
+ tilemap_tools-0.1.0.dist-info/METADATA,sha256=3i1Y5BTiUlOYck_vqQkztqVXKsYpYa7rit62A563BP8,1900
8
+ tilemap_tools-0.1.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
9
+ tilemap_tools-0.1.0.dist-info/top_level.txt,sha256=Tke14ObRv7S8RCzOnM-F1wO9mBwb5UkheoKs2lWWE2U,14
10
+ tilemap_tools-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Julosse27
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ tilemap_tools