Skip to content

Commit 878a25f

Browse files
committed
v3.0.1
1 parent 248e35f commit 878a25f

56 files changed

Lines changed: 910 additions & 438 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@
55
Формат основан на [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
и этот проект придерживается [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [3.0.1]
9+
10+
### Added
11+
- **Редактор: Save As** — в тулбаре появилась отдельная кнопка `Save As`, а также горячая клавиша `Ctrl+Shift+S` для сохранения сцены в новый JSON-файл.
12+
13+
### Changed
14+
- **Редактор: пути к изображениям** — при сохранении сцены `sprite_path` теперь автоматически нормализуется относительно JSON-файла, чтобы сцены оставались переносимыми между машинами и каталогами.
15+
- **Runtime round-trip**`RuntimeScene.save(...)` теперь тоже пересчитывает пути к изображениям относительно нового JSON, а runtime использует ту же общую логику поиска ассетов, что и редактор.
16+
- **Версия библиотеки** — релиз обновлён до `3.0.1`.
17+
18+
---
19+
820
## [3.0.0]
921

1022
### Added

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "spritepro"
7-
version = "3.0.0"
7+
version = "3.0.1"
88
authors = [
99
{ name="NeoXider", email="neoxider@gmail.com" },
1010
]

spritePro/__init__.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
HOOKS_LIFECYCLE,
136136
HOOKS_SPRITE,
137137
HOOKS_SCENE,
138-
HOOKS_INPUT
138+
HOOKS_INPUT,
139139
)
140140
from .builder import SpriteBuilder, ParticleBuilder, particles
141141
from .asset_watcher import AssetWatcher, HotReloadManager, get_hot_reload_manager
@@ -314,6 +314,22 @@ def sprite(path: str = "") -> SpriteBuilder:
314314
"save_load",
315315
"validation",
316316
"plugins",
317+
"validate_color",
318+
"validate_vector2",
319+
"validate_float",
320+
"validate_string",
321+
"validate_enum",
322+
"validate_list",
323+
"validate_dict",
324+
"PluginManager",
325+
"PluginInfo",
326+
"get_plugin_manager",
327+
"register_plugin",
328+
"hook",
329+
"HOOKS_LIFECYCLE",
330+
"HOOKS_SPRITE",
331+
"HOOKS_SCENE",
332+
"HOOKS_INPUT",
317333
# Scenes
318334
"Scene",
319335
"SceneManager",
@@ -341,9 +357,13 @@ def sprite(path: str = "") -> SpriteBuilder:
341357
"ParticleBuilder",
342358
"sprite",
343359
"particles",
360+
"AssetWatcher",
361+
"HotReloadManager",
362+
"get_hot_reload_manager",
344363
# Global facade
345364
"get_context",
346365
"get_game",
366+
"get_physics",
347367
"get_physics_world",
348368
"physics",
349369
"register_sprite",

spritePro/asset_watcher.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@
22

33
from __future__ import annotations
44

5-
import os
6-
import time
75
from typing import Callable, Optional, Dict, List, Any
86
from pathlib import Path
9-
from threading import Thread
10-
import pygame
117

128
import spritePro
139

spritePro/builder.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@
66

77
from __future__ import annotations
88

9-
from typing import Optional, Sequence, Tuple, Union, Any, Callable
10-
from pathlib import Path
9+
from typing import Optional, Sequence, Tuple, Union, Any
1110

1211
import pygame
1312
from pygame.math import Vector2
1413

15-
import spritePro
1614
from .sprite import Sprite
1715
from .constants import Anchor
1816
from .particles import ParticleEmitter, ParticleConfig
@@ -397,7 +395,9 @@ def build(self) -> Sprite:
397395
w, h = img.get_size()
398396
mask_surf = pygame.Surface((w, h), pygame.SRCALPHA)
399397
pygame.draw.rect(
400-
mask_surf, (255, 255, 255, 255), (0, 0, w, h),
398+
mask_surf,
399+
(255, 255, 255, 255),
400+
(0, 0, w, h),
401401
border_radius=min(self._border_radius, w // 2, h // 2),
402402
)
403403
out = img.copy()

spritePro/cli_tools/android.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ def infer_android_version(project_root: Path) -> str:
7979

8080
def infer_android_orientation(project_root: Path) -> str:
8181
config_text = read_text_if_exists(project_root / "config.py")
82-
size_match = re.search(r"""^WINDOW_SIZE\s*=\s*\((\d+)\s*,\s*(\d+)\)\s*$""", config_text, re.MULTILINE)
82+
size_match = re.search(
83+
r"""^WINDOW_SIZE\s*=\s*\((\d+)\s*,\s*(\d+)\)\s*$""", config_text, re.MULTILINE
84+
)
8385
if not size_match:
8486
return "all"
8587
width = int(size_match.group(1))
@@ -173,7 +175,9 @@ def run_android_build(
173175
logging.info("Using existing buildozer.spec: %s", spec_path)
174176

175177
if mode == "spec":
176-
logging.info("Spec generated only. Next step: run inside Linux/WSL -> buildozer android debug")
178+
logging.info(
179+
"Spec generated only. Next step: run inside Linux/WSL -> buildozer android debug"
180+
)
177181
return 0
178182

179183
if not sys.platform.startswith("linux"):
@@ -184,7 +188,7 @@ def run_android_build(
184188

185189
buildozer_exe = shutil.which("buildozer")
186190
if buildozer_exe is None:
187-
raise RuntimeError('Buildozer не найден в PATH. Установите его: pip install buildozer')
191+
raise RuntimeError("Buildozer не найден в PATH. Установите его: pip install buildozer")
188192

189193
if mode == "debug":
190194
command = [buildozer_exe, "android", "debug"]

spritePro/components/text.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ def _render_text_multiline(self, text: str, line_spacing: int = 2) -> pygame.Sur
200200
lines = [""]
201201
line_surfs = [self.font.render(line or " ", True, self.color) for line in lines]
202202
max_w = max(s.get_width() for s in line_surfs)
203-
total_h = sum(s.get_height() for s in line_surfs) + max(0, len(line_surfs) - 1) * line_spacing
203+
total_h = (
204+
sum(s.get_height() for s in line_surfs) + max(0, len(line_surfs) - 1) * line_spacing
205+
)
204206
surf = pygame.Surface((max_w, total_h), pygame.SRCALPHA)
205207
y = 0
206208
for srf in line_surfs:

spritePro/demoGames/builder_demo.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Demo: Builder Pattern - Fluent API для создания спрайтов."""
2+
23
import sys
34

45
import pygame
@@ -31,13 +32,7 @@ def __init__(self):
3132
.build()
3233
)
3334

34-
self.coin = (
35-
s.sprite("")
36-
.position(600, 300)
37-
.size(32, 32)
38-
.color(255, 215, 0)
39-
.build()
40-
)
35+
self.coin = s.sprite("").position(600, 300).size(32, 32).color(255, 215, 0).build()
4136
self.coin.set_circle_shape(radius=16, color=(255, 215, 0))
4237

4338
self.emitter = (

spritePro/demoGames/events_rps_demo.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,15 @@ def __init__(self, net: s.NetClient, role: str) -> None:
4747
super().__init__()
4848
s.multiplayer.init_context(net, role)
4949
self.ctx = s.multiplayer_ctx
50-
self.title = s.TextSprite("Rock / Paper / Scissors", 30, (255, 255, 255), (400, 40), scene=self)
51-
self.hint = s.TextSprite("1=Rock 2=Paper 3=Scissors", 20, (200, 200, 200), (400, 80), scene=self)
52-
self.status = s.TextSprite("Make your choice...", 20, (220, 220, 220), (400, 120), scene=self)
50+
self.title = s.TextSprite(
51+
"Rock / Paper / Scissors", 30, (255, 255, 255), (400, 40), scene=self
52+
)
53+
self.hint = s.TextSprite(
54+
"1=Rock 2=Paper 3=Scissors", 20, (200, 200, 200), (400, 80), scene=self
55+
)
56+
self.status = s.TextSprite(
57+
"Make your choice...", 20, (220, 220, 220), (400, 120), scene=self
58+
)
5359
self.my_info = s.TextSprite("You (ID: ?)", 20, (120, 200, 255), (200, 220), scene=self)
5460
self.other_info = s.TextSprite("Other (ID: ?)", 20, (255, 200, 120), (600, 220), scene=self)
5561
self.my_choice_text = s.TextSprite("?", 36, (120, 200, 255), (200, 270), scene=self)
@@ -107,7 +113,10 @@ def update(self, dt: float) -> None:
107113
other_id_label = "?" if self.other_id is None else str(self.other_id)
108114
self.other_info.set_text(f"Other (ID: {other_id_label})")
109115

110-
if self.last_result_at is not None and s.time_since_start - self.last_result_at >= self.result_delay:
116+
if (
117+
self.last_result_at is not None
118+
and s.time_since_start - self.last_result_at >= self.result_delay
119+
):
111120
self.last_result_at = None
112121
self.my_choice = None
113122
self.other_choice = None

spritePro/demoGames/fluent_tween_demo.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ def __init__(self):
2222
self.box_kill = s.Sprite("", (50, 50), (700, 200), scene=self)
2323
self.box_kill.set_color((150, 255, 150))
2424

25-
self.title = s.TextSprite("Fluent Tween API (Do*)", 28, (255, 255, 255), (s.WH_C.x, 24), scene=self)
25+
self.title = s.TextSprite(
26+
"Fluent Tween API (Do*)", 28, (255, 255, 255), (s.WH_C.x, 24), scene=self
27+
)
2628
hints = [
2729
"1: DoMove | 2: DoMoveBy | 3: DoScale | 4: DoRotateBy(180) | 5: DoColor | 6: DoFadeOut/In",
2830
"7: DoMove(...).SetEase(Ease.OutCubic).SetDelay(0.3).OnComplete(callback)",
@@ -52,11 +54,15 @@ def reset(self) -> None:
5254
if self.active_kill_handle:
5355
self.active_kill_handle.Kill(complete=False)
5456
self.active_kill_handle = None
55-
self.box.set_position(self.base_pos).set_scale(self.base_scale).rotate_to(self.base_angle).set_alpha(
56-
self.base_alpha
57-
).set_color(self.base_color)
58-
self.box_loop.set_position(self.base_pos_loop).set_scale(1.0).set_color((255, 180, 100)).set_alpha(255)
59-
self.box_kill.set_position(self.base_pos_kill).set_scale(1.0).set_color((150, 255, 150)).set_alpha(255)
57+
self.box.set_position(self.base_pos).set_scale(self.base_scale).rotate_to(
58+
self.base_angle
59+
).set_alpha(self.base_alpha).set_color(self.base_color)
60+
self.box_loop.set_position(self.base_pos_loop).set_scale(1.0).set_color(
61+
(255, 180, 100)
62+
).set_alpha(255)
63+
self.box_kill.set_position(self.base_pos_kill).set_scale(1.0).set_color(
64+
(150, 255, 150)
65+
).set_alpha(255)
6066

6167
def update(self, dt: float) -> None:
6268
if s.input.was_pressed(pygame.K_1):
@@ -96,7 +102,9 @@ def update(self, dt: float) -> None:
96102
self.active_loop_handle.Kill(complete=False)
97103
self.box_loop.set_position(self.base_pos_loop)
98104
self.box_loop.set_scale(1.0)
99-
self.active_loop_handle = self.box_loop.DoMove((450, 450), 1.2).SetLoops(-1).SetYoyo(True)
105+
self.active_loop_handle = (
106+
self.box_loop.DoMove((450, 450), 1.2).SetLoops(-1).SetYoyo(True)
107+
)
100108

101109
if s.input.was_pressed(pygame.K_k):
102110
if self.active_kill_handle:

0 commit comments

Comments
 (0)