arcade-machine-sdk 1.0.0__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,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: arcade-machine-sdk
3
+ Version: 1.0.0
4
+ Summary: SDK oficial para crear juegos compatibles con el Arcade Machine Core.
5
+ Author: Neritoou
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Neritoou/arcade-machine-sdk
8
+ Project-URL: Repository, https://github.com/Neritoou/arcade-machine-sdk
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: pygame>=2.6.0
File without changes
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "arcade-machine-sdk"
7
+ version = "1.0.0"
8
+ description = "SDK oficial para crear juegos compatibles con el Arcade Machine Core."
9
+ authors = [
10
+ { name = "Neritoou" }
11
+ ]
12
+ license = "MIT"
13
+ readme = "README.md"
14
+ requires-python = ">=3.10"
15
+ dependencies = [
16
+ "pygame>=2.6.0"
17
+ ]
18
+
19
+ [project.urls]
20
+ Homepage = "https://github.com/Neritoou/arcade-machine-sdk"
21
+ Repository = "https://github.com/Neritoou/arcade-machine-sdk"
22
+
23
+ [tool.setuptools]
24
+ package-dir = {"" = "src"}
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["src"]
28
+
29
+ [tool.setuptools.package-data]
30
+ arcade_machine_sdk = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,13 @@
1
+ from .constants import BASE_RESOLUTION, BASE_WIDTH, BASE_HEIGHT, DEFAULT_FPS
2
+ from .game import GameBase, GameMeta
3
+ from .util import json
4
+
5
+ __all__ = [
6
+ "BASE_RESOLUTION",
7
+ "DEFAULT_FPS",
8
+ "BASE_WIDTH",
9
+ "BASE_HEIGHT",
10
+ "GameBase",
11
+ "GameMeta",
12
+ "json"
13
+ ]
@@ -0,0 +1,8 @@
1
+ from .values import BASE_RESOLUTION, BASE_HEIGHT, BASE_WIDTH, DEFAULT_FPS
2
+
3
+ __all__ = [
4
+ "BASE_RESOLUTION",
5
+ "BASE_HEIGHT",
6
+ "BASE_WIDTH",
7
+ "DEFAULT_FPS"
8
+ ]
@@ -0,0 +1,7 @@
1
+ # Resolución por defecto
2
+ BASE_WIDTH = 1024
3
+ BASE_HEIGHT = 768
4
+ BASE_RESOLUTION = (BASE_WIDTH, BASE_HEIGHT)
5
+
6
+ # FPS por defecto
7
+ DEFAULT_FPS = 60
@@ -0,0 +1,7 @@
1
+ from .game_base import GameBase
2
+ from .game_meta import GameMeta
3
+
4
+ __all__ = [
5
+ "GameBase",
6
+ "GameMeta"
7
+ ]
@@ -0,0 +1,134 @@
1
+ from abc import ABC, abstractmethod
2
+ from .game_meta import GameMeta
3
+ from ..constants import BASE_RESOLUTION, DEFAULT_FPS
4
+ import pygame
5
+
6
+ class GameBase(ABC):
7
+ """
8
+ Clase base que todo juego debe implementar para integrarse al Core.
9
+
10
+ Arquitectura pensada para un loop único del Core:
11
+ - El Core mantiene la ventana y el loop global.
12
+ - Los juegos nunca crean su propio loop ni su propia ventana.
13
+ - Cada juego expone métodos que el Core llama por frame.
14
+
15
+ Flags importantes:
16
+ - running: True cuando el juego está activo.
17
+ """
18
+ def __init__(self, metadata: GameMeta) -> None:
19
+ metadata.validate()
20
+ self._running = False
21
+ self.metadata = metadata
22
+ self.__surface = None
23
+
24
+ def start(self, surface: pygame.Surface) -> None:
25
+ """
26
+ Marca el juego como activo.
27
+ - Su uso en clases concretas debe llamar a **super()**.
28
+ - Se llama cada vez que el usuario inicia el juego desde el menú del Core.
29
+ - A este método se le debe pasar una superficie en la que el juego se dibujará.
30
+
31
+ args:
32
+ **screen**: Pantalla o superficie en la que el juego se dibujará.
33
+ """
34
+ self._running = True
35
+ self.__surface = surface
36
+
37
+ def stop(self) -> None:
38
+ """
39
+ Detiene el juego, libera recursos dinámicos si es necesario.
40
+ - Su uso en clases concretas debe llamar a **super()**.
41
+ - Se llama cuando el usuario vuelve al menú o cierra el juego.
42
+ """
43
+ self._running = False
44
+
45
+ @abstractmethod
46
+ def handle_events(self, events: list[pygame.event.Event]) -> None:
47
+ """
48
+ Recibe los eventos que pasan por el Core (teclado, mouse, joystick)
49
+ y los procesa para la lógica del juego.
50
+ - Nunca debe llamar a `pygame.event.get()` por sí mismo.
51
+
52
+ args:
53
+ **events**: lista de eventos de pygame capturados en el frame actual
54
+ """
55
+ pass
56
+
57
+ @abstractmethod
58
+ def update(self, dt: float) -> None:
59
+ """
60
+ Actualización de la lógica del juego por frame.
61
+ - Se llama solo si `_running=True`.
62
+
63
+ args:
64
+ **dt**: tiempo en segundos desde el último frame (para movimientos independientes del FPS)
65
+ """
66
+ pass
67
+
68
+ @abstractmethod
69
+ def render(self) -> None:
70
+ """
71
+ Dibuja todo el estado del juego en la superficie que pasa el Core.
72
+ - Nunca crea su propia ventana.
73
+ - Se llama solo si `_running=True`.
74
+ """
75
+ pass
76
+
77
+ @property
78
+ def running(self) -> bool:
79
+ """
80
+ Devuelve el estado actual del juego (activo o no).
81
+ Útil para el Core para saber si debe llamar a update() y render().
82
+ """
83
+ return self._running
84
+
85
+ @property
86
+ def surface(self) -> pygame.Surface:
87
+ """
88
+ Devuelve la superficie actual en la que el juego se está dibujando.
89
+ """
90
+ if self.__surface == None:
91
+ raise AttributeError("No hay ninguna superficie disponible")
92
+ return self.__surface
93
+
94
+ def run_independently(self) -> None:
95
+ """
96
+ Ejecuta el juego de forma independiente sin necesidad del Core.
97
+ Útil para desarrollo y testing.
98
+ """
99
+ if not pygame.get_init():
100
+ pygame.init()
101
+
102
+ screen = pygame.display.set_mode(BASE_RESOLUTION)
103
+ pygame.display.set_caption(self.metadata.title)
104
+ clock = pygame.time.Clock()
105
+ self.start(screen)
106
+
107
+ try:
108
+ while self._running:
109
+ events = pygame.event.get()
110
+
111
+ for event in events:
112
+ if event.type == pygame.QUIT:
113
+ self.stop()
114
+ break
115
+
116
+ if self._running:
117
+ self.handle_events(events)
118
+
119
+ if self._running:
120
+ dt = clock.get_time() / 1000.0
121
+ self.update(dt)
122
+
123
+ if self._running:
124
+ self.render()
125
+ pygame.display.flip()
126
+
127
+ clock.tick(DEFAULT_FPS)
128
+
129
+ except KeyboardInterrupt:
130
+ pass
131
+
132
+ finally:
133
+ self.stop()
134
+ pygame.quit()
@@ -0,0 +1,81 @@
1
+ class GameMeta:
2
+ """Clase que sigue el patrón Builder para construir metadatos de un juego."""
3
+
4
+ def __init__(self) -> None:
5
+ self._title = ""
6
+ self._description = ""
7
+ self._release_date = ""
8
+ self._tags: list[str] = []
9
+ self._group_number = -1
10
+ self._authors: list[str] = []
11
+
12
+ def with_title(self, title: str) -> 'GameMeta':
13
+ self._title = title
14
+ return self
15
+
16
+ def with_description(self, description: str) -> 'GameMeta':
17
+ self._description = description
18
+ return self
19
+
20
+ # TODO: esto debería representarse con un UNIX timestamp o una fecha de Python, pero por ahora se puede dejar así. Además, es siquiera esto necesario?
21
+ def with_release_date(self, release_date: str) -> 'GameMeta':
22
+ self._release_date = release_date
23
+ return self
24
+
25
+ def with_tags(self, tags: list[str]) -> 'GameMeta':
26
+ self._tags.extend(tags)
27
+ return self
28
+
29
+ def add_tag(self, tag: str) -> 'GameMeta':
30
+ self._tags.append(tag)
31
+ return self
32
+
33
+ def with_group_number(self, group_number: int) -> 'GameMeta':
34
+ self._group_number = group_number
35
+ return self
36
+
37
+ def with_authors(self, authors: list[str]) -> 'GameMeta':
38
+ self._authors.extend(authors)
39
+ return self
40
+
41
+ def add_author(self, author: str) -> 'GameMeta':
42
+ self._authors.append(author)
43
+ return self
44
+
45
+ @property
46
+ def title(self) -> str:
47
+ return self._title
48
+
49
+ @property
50
+ def description(self) -> str:
51
+ return self._description
52
+
53
+ @property
54
+ def release_date(self) -> str:
55
+ return self._release_date
56
+
57
+ @property
58
+ def tags(self) -> list[str]:
59
+ return self._tags.copy()
60
+
61
+ @property
62
+ def group_number(self) -> int:
63
+ return self._group_number
64
+
65
+ @property
66
+ def authors(self) -> list[str]:
67
+ return self._authors.copy()
68
+
69
+ def validate(self) -> None:
70
+ if not self._title:
71
+ raise ValueError(f"No se especificó un título (\"title\") para el juego en sus metadatos")
72
+ if not self._description:
73
+ raise ValueError(f"No se especificó una descripción (\"description\") para el juego en sus metadatos")
74
+ if not self._release_date:
75
+ raise ValueError(f"No se especificó una fecha de lanzamiento (\"release_date\") para el juego en sus metadatos")
76
+ if not self._tags:
77
+ raise ValueError(f"No se especificó al menos una etiqueta (\"tags\") para el juego en sus metadatos")
78
+ if self._group_number == -1:
79
+ raise ValueError(f"No se especificó el número del grupo (\"group_number\") que hizo el juego en sus metadatos")
80
+ if not self._authors:
81
+ raise ValueError(f"No se especificó al menos un autor (\"authors\") que haya hecho el juego en sus metadatos")
@@ -0,0 +1,6 @@
1
+ from .json import load, save
2
+
3
+ __all__ = [
4
+ "load",
5
+ "save"
6
+ ]
@@ -0,0 +1,32 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ def load(file_path: str) -> dict[str, Any]:
6
+ """
7
+ Carga un archivo JSON y devuelve un diccionario.
8
+
9
+ args:
10
+ **file_path**: Ruta completa al archivo JSON.
11
+
12
+ returns:
13
+ Diccionario con los datos del JSON.
14
+ """
15
+ path = Path(file_path)
16
+ if not path.exists():
17
+ raise FileNotFoundError(f"No se encontró el archivo JSON en {file_path}")
18
+
19
+ with path.open("r", encoding="utf-8") as f:
20
+ return json.load(f)
21
+
22
+ def save(file_path: str, data: dict[str, Any]) -> None:
23
+ """
24
+ Guarda un diccionario como un archivo JSON.
25
+
26
+ args:
27
+ **file_path**: Ruta completa al archivo JSON.
28
+ **json**: Diccionario a escribir como JSON.
29
+ """
30
+ path = Path(file_path)
31
+ with path.open("w", encoding="utf-8") as f:
32
+ json.dump(data, f, ensure_ascii=False, indent=4)
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: arcade-machine-sdk
3
+ Version: 1.0.0
4
+ Summary: SDK oficial para crear juegos compatibles con el Arcade Machine Core.
5
+ Author: Neritoou
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Neritoou/arcade-machine-sdk
8
+ Project-URL: Repository, https://github.com/Neritoou/arcade-machine-sdk
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: pygame>=2.6.0
@@ -0,0 +1,16 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/arcade_machine_sdk/__init__.py
4
+ src/arcade_machine_sdk/py.typed
5
+ src/arcade_machine_sdk.egg-info/PKG-INFO
6
+ src/arcade_machine_sdk.egg-info/SOURCES.txt
7
+ src/arcade_machine_sdk.egg-info/dependency_links.txt
8
+ src/arcade_machine_sdk.egg-info/requires.txt
9
+ src/arcade_machine_sdk.egg-info/top_level.txt
10
+ src/arcade_machine_sdk/constants/__init__.py
11
+ src/arcade_machine_sdk/constants/values.py
12
+ src/arcade_machine_sdk/game/__init__.py
13
+ src/arcade_machine_sdk/game/game_base.py
14
+ src/arcade_machine_sdk/game/game_meta.py
15
+ src/arcade_machine_sdk/util/__init__.py
16
+ src/arcade_machine_sdk/util/json.py
@@ -0,0 +1 @@
1
+ arcade_machine_sdk