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.
- arcade_machine_sdk-1.0.0/PKG-INFO +11 -0
- arcade_machine_sdk-1.0.0/README.md +0 -0
- arcade_machine_sdk-1.0.0/pyproject.toml +30 -0
- arcade_machine_sdk-1.0.0/setup.cfg +4 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/__init__.py +13 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/constants/__init__.py +8 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/constants/values.py +7 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/game/__init__.py +7 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/game/game_base.py +134 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/game/game_meta.py +81 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/py.typed +0 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/util/__init__.py +6 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk/util/json.py +32 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk.egg-info/PKG-INFO +11 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk.egg-info/SOURCES.txt +16 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk.egg-info/dependency_links.txt +1 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk.egg-info/requires.txt +1 -0
- arcade_machine_sdk-1.0.0/src/arcade_machine_sdk.egg-info/top_level.txt +1 -0
|
@@ -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,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,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")
|
|
File without changes
|
|
@@ -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
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pygame>=2.6.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
arcade_machine_sdk
|