aether-engine 1.0.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.
Files changed (64) hide show
  1. aether/__init__.py +54 -0
  2. aether/audio/__init__.py +0 -0
  3. aether/audio/audio_manager.py +211 -0
  4. aether/audio/music.py +6 -0
  5. aether/audio/sound.py +6 -0
  6. aether/core/__init__.py +211 -0
  7. aether/core/achievement_system.py +304 -0
  8. aether/core/application.py +19 -0
  9. aether/core/config.py +75 -0
  10. aether/core/console.py +366 -0
  11. aether/core/engine.py +50 -0
  12. aether/core/event_bus.py +24 -0
  13. aether/core/input.py +58 -0
  14. aether/core/plugin_system.py +93 -0
  15. aether/core/save_system.py +409 -0
  16. aether/core/time_manager.py +25 -0
  17. aether/core/window.py +57 -0
  18. aether/graphics/__init__.py +0 -0
  19. aether/graphics/camera.py +69 -0
  20. aether/graphics/framebuffer.py +119 -0
  21. aether/graphics/light.py +32 -0
  22. aether/graphics/lod_system.py +170 -0
  23. aether/graphics/material.py +25 -0
  24. aether/graphics/mesh.py +83 -0
  25. aether/graphics/mesh_factory.py +67 -0
  26. aether/graphics/particle_system.py +268 -0
  27. aether/graphics/post_process.py +279 -0
  28. aether/graphics/renderer.py +135 -0
  29. aether/graphics/shader.py +61 -0
  30. aether/graphics/shader_library.py +157 -0
  31. aether/graphics/sky_atmosphere.py +309 -0
  32. aether/graphics/terrain.py +408 -0
  33. aether/graphics/texture.py +34 -0
  34. aether/graphics/water.py +432 -0
  35. aether/physics/__init__.py +0 -0
  36. aether/physics/bullet_backend.py +395 -0
  37. aether/physics/collider.py +22 -0
  38. aether/physics/physics_world.py +436 -0
  39. aether/physics/raycast.py +25 -0
  40. aether/physics/rigidbody.py +7 -0
  41. aether/resources/__init__.py +0 -0
  42. aether/resources/asset_database.py +76 -0
  43. aether/resources/resource_manager.py +147 -0
  44. aether/resources/serialization.py +114 -0
  45. aether/scene/__init__.py +0 -0
  46. aether/scene/component.py +368 -0
  47. aether/scene/entity.py +46 -0
  48. aether/scene/scene.py +33 -0
  49. aether/scene/scene_graph.py +252 -0
  50. aether/scene/scene_manager.py +191 -0
  51. aether/ui/__init__.py +0 -0
  52. aether/ui/button.py +297 -0
  53. aether/ui/canvas.py +11 -0
  54. aether/ui/text.py +286 -0
  55. aether/utils/__init__.py +0 -0
  56. aether/utils/logger.py +16 -0
  57. aether/utils/math_utils.py +31 -0
  58. aether/utils/object_pool.py +137 -0
  59. aether/utils/profiler.py +187 -0
  60. aether_engine-1.0.0.dist-info/METADATA +71 -0
  61. aether_engine-1.0.0.dist-info/RECORD +64 -0
  62. aether_engine-1.0.0.dist-info/WHEEL +5 -0
  63. aether_engine-1.0.0.dist-info/licenses/LICENSE +21 -0
  64. aether_engine-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,304 @@
1
+ """Система достижений"""
2
+ from typing import Dict, List, Callable, Any, Optional
3
+ from enum import Enum
4
+ import json
5
+ import time
6
+ from pathlib import Path
7
+
8
+
9
+ class AchievementState(Enum):
10
+ LOCKED = 0
11
+ UNLOCKED = 1
12
+ HIDDEN = 2
13
+
14
+
15
+ class Achievement:
16
+ """Достижение"""
17
+
18
+ def __init__(self,
19
+ achievement_id: str,
20
+ name: str,
21
+ description: str = "",
22
+ icon: str = "",
23
+ hidden: bool = False,
24
+ points: int = 10):
25
+
26
+ self.id = achievement_id
27
+ self.name = name
28
+ self.description = description
29
+ self.icon = icon
30
+ self.hidden = hidden
31
+ self.points = points
32
+
33
+ self.state = AchievementState.HIDDEN if hidden else AchievementState.LOCKED
34
+ self.unlock_time: Optional[float] = None
35
+ self.progress = 0.0 # 0.0 - 1.0
36
+ self.progress_max = 1.0
37
+
38
+ # Условие разблокировки
39
+ self.unlock_condition: Optional[Callable] = None
40
+
41
+ # Статистика
42
+ self.unlock_count = 0
43
+
44
+ def check_unlock(self, context: Dict[str, Any] = None) -> bool:
45
+ """Проверяет условие разблокировки"""
46
+ if self.state == AchievementState.UNLOCKED:
47
+ return True
48
+
49
+ if self.unlock_condition:
50
+ try:
51
+ if self.unlock_condition(context or {}):
52
+ self.unlock()
53
+ return True
54
+ except Exception:
55
+ pass
56
+
57
+ return False
58
+
59
+ def unlock(self):
60
+ """Разблокирует достижение"""
61
+ if self.state != AchievementState.UNLOCKED:
62
+ self.state = AchievementState.UNLOCKED
63
+ self.unlock_time = time.time()
64
+ self.progress = self.progress_max
65
+ self.unlock_count += 1
66
+
67
+ def update_progress(self, amount: float):
68
+ """Обновляет прогресс"""
69
+ if self.state == AchievementState.UNLOCKED:
70
+ return
71
+
72
+ self.progress = min(self.progress_max, self.progress + amount)
73
+
74
+ if self.progress >= self.progress_max:
75
+ self.unlock()
76
+
77
+ def reset(self):
78
+ """Сбрасывает достижение"""
79
+ self.state = AchievementState.HIDDEN if self.hidden else AchievementState.LOCKED
80
+ self.unlock_time = None
81
+ self.progress = 0.0
82
+
83
+ def to_dict(self) -> dict:
84
+ return {
85
+ 'id': self.id,
86
+ 'state': self.state.value,
87
+ 'unlock_time': self.unlock_time,
88
+ 'progress': self.progress,
89
+ 'unlock_count': self.unlock_count,
90
+ }
91
+
92
+ @classmethod
93
+ def from_dict(cls, data: dict, achievement_def: 'Achievement') -> 'Achievement':
94
+ ach = Achievement(
95
+ achievement_def.id,
96
+ achievement_def.name,
97
+ achievement_def.description,
98
+ achievement_def.icon,
99
+ achievement_def.hidden,
100
+ achievement_def.points
101
+ )
102
+ ach.state = AchievementState(data.get('state', 0))
103
+ ach.unlock_time = data.get('unlock_time')
104
+ ach.progress = data.get('progress', 0.0)
105
+ ach.unlock_count = data.get('unlock_count', 0)
106
+ ach.unlock_condition = achievement_def.unlock_condition
107
+ return ach
108
+
109
+
110
+ class AchievementCategory:
111
+ """Категория достижений"""
112
+
113
+ def __init__(self, name: str, icon: str = ""):
114
+ self.name = name
115
+ self.icon = icon
116
+ self.achievements: Dict[str, Achievement] = {}
117
+
118
+
119
+ class AchievementSystem:
120
+ """Система достижений"""
121
+
122
+ _instance = None
123
+
124
+ def __new__(cls):
125
+ if cls._instance is None:
126
+ cls._instance = super().__new__(cls)
127
+ cls._instance._initialize()
128
+ return cls._instance
129
+
130
+ def _initialize(self):
131
+ """Инициализация системы"""
132
+ self._categories: Dict[str, AchievementCategory] = {}
133
+ self._achievements: Dict[str, Achievement] = {}
134
+ self._unlocked_queue: List[Achievement] = []
135
+
136
+ # Статистика игрока
137
+ self.stats: Dict[str, Any] = {
138
+ 'play_time': 0.0,
139
+ 'deaths': 0,
140
+ 'kills': 0,
141
+ 'distance_traveled': 0.0,
142
+ 'items_collected': 0,
143
+ 'quests_completed': 0,
144
+ 'total_score': 0,
145
+ }
146
+
147
+ # Коллбэки
148
+ self.on_achievement_unlocked: List[Callable] = []
149
+
150
+ # Сохранение
151
+ self._save_path = Path("./achievements.json")
152
+
153
+ print("[AchievementSystem] Initialized")
154
+
155
+ def create_category(self, name: str, icon: str = "") -> AchievementCategory:
156
+ """Создаёт категорию"""
157
+ category = AchievementCategory(name, icon)
158
+ self._categories[name] = category
159
+ return category
160
+
161
+ def register_achievement(self,
162
+ achievement_id: str,
163
+ name: str,
164
+ description: str = "",
165
+ category: str = "General",
166
+ icon: str = "",
167
+ hidden: bool = False,
168
+ points: int = 10,
169
+ unlock_condition: Optional[Callable] = None) -> Achievement:
170
+ """Регистрирует достижение"""
171
+ if category not in self._categories:
172
+ self.create_category(category)
173
+
174
+ achievement = Achievement(
175
+ achievement_id, name, description,
176
+ icon, hidden, points
177
+ )
178
+ achievement.unlock_condition = unlock_condition
179
+
180
+ self._categories[category].achievements[achievement_id] = achievement
181
+ self._achievements[achievement_id] = achievement
182
+
183
+ return achievement
184
+
185
+ def get_achievement(self, achievement_id: str) -> Optional[Achievement]:
186
+ """Получает достижение по ID"""
187
+ return self._achievements.get(achievement_id)
188
+
189
+ def is_unlocked(self, achievement_id: str) -> bool:
190
+ """Проверяет, разблокировано ли достижение"""
191
+ ach = self.get_achievement(achievement_id)
192
+ return ach.state == AchievementState.UNLOCKED if ach else False
193
+
194
+ def update_stat(self, stat_name: str, value: Any):
195
+ """Обновляет статистику"""
196
+ if stat_name in self.stats:
197
+ if isinstance(self.stats[stat_name], (int, float)):
198
+ self.stats[stat_name] += value
199
+ else:
200
+ self.stats[stat_name] = value
201
+
202
+ # Проверяем достижения
203
+ self._check_achievements()
204
+
205
+ def increment_stat(self, stat_name: str, amount: float = 1.0):
206
+ """Увеличивает статистику"""
207
+ if stat_name in self.stats:
208
+ if isinstance(self.stats[stat_name], (int, float)):
209
+ self.stats[stat_name] += amount
210
+
211
+ self._check_achievements()
212
+
213
+ def _check_achievements(self):
214
+ """Проверяет все достижения"""
215
+ context = {'stats': self.stats, 'achievements': self._achievements}
216
+
217
+ for achievement in self._achievements.values():
218
+ if achievement.state != AchievementState.UNLOCKED:
219
+ if achievement.check_unlock(context):
220
+ self._unlocked_queue.append(achievement)
221
+
222
+ # Вызываем коллбэки
223
+ for callback in self.on_achievement_unlocked:
224
+ callback(achievement)
225
+
226
+ print(f"[Achievement] Unlocked: {achievement.name} (+{achievement.points} points)")
227
+
228
+ self.stats['total_score'] += achievement.points
229
+
230
+ def get_unlocked_queue(self) -> List[Achievement]:
231
+ """Возвращает очередь разблокированных достижений"""
232
+ queue = self._unlocked_queue.copy()
233
+ self._unlocked_queue.clear()
234
+ return queue
235
+
236
+ def get_progress(self, achievement_id: str) -> float:
237
+ """Возвращает прогресс достижения"""
238
+ ach = self.get_achievement(achievement_id)
239
+ return ach.progress / ach.progress_max if ach else 0.0
240
+
241
+ def save(self, path: str = None):
242
+ """Сохраняет достижения"""
243
+ save_path = Path(path) if path else self._save_path
244
+
245
+ data = {
246
+ 'achievements': {},
247
+ 'stats': self.stats,
248
+ }
249
+
250
+ for ach_id, ach in self._achievements.items():
251
+ data['achievements'][ach_id] = ach.to_dict()
252
+
253
+ try:
254
+ with open(save_path, 'w') as f:
255
+ json.dump(data, f, indent=2)
256
+ print(f"[AchievementSystem] Saved to {save_path}")
257
+ except Exception as e:
258
+ print(f"[AchievementSystem] Failed to save: {e}")
259
+
260
+ def load(self, path: str = None):
261
+ """Загружает достижения"""
262
+ load_path = Path(path) if path else self._save_path
263
+
264
+ if not load_path.exists():
265
+ return
266
+
267
+ try:
268
+ with open(load_path, 'r') as f:
269
+ data = json.load(f)
270
+
271
+ self.stats = data.get('stats', self.stats)
272
+
273
+ ach_data = data.get('achievements', {})
274
+ for ach_id, ach_state in ach_data.items():
275
+ if ach_id in self._achievements:
276
+ self._achievements[ach_id] = Achievement.from_dict(
277
+ ach_state, self._achievements[ach_id]
278
+ )
279
+
280
+ print(f"[AchievementSystem] Loaded from {load_path}")
281
+ except Exception as e:
282
+ print(f"[AchievementSystem] Failed to load: {e}")
283
+
284
+ def reset_all(self):
285
+ """Сбрасывает все достижения"""
286
+ for achievement in self._achievements.values():
287
+ achievement.reset()
288
+ self.stats = {k: 0 if isinstance(v, (int, float)) else v
289
+ for k, v in self.stats.items()}
290
+ print("[AchievementSystem] All achievements reset")
291
+
292
+ def get_categories(self) -> Dict[str, AchievementCategory]:
293
+ return self._categories.copy()
294
+
295
+ def get_all_achievements(self) -> List[Achievement]:
296
+ return list(self._achievements.values())
297
+
298
+ def get_unlocked_count(self) -> int:
299
+ return sum(1 for ach in self._achievements.values()
300
+ if ach.state == AchievementState.UNLOCKED)
301
+
302
+ def get_total_points(self) -> int:
303
+ return sum(ach.points for ach in self._achievements.values()
304
+ if ach.state == AchievementState.UNLOCKED)
@@ -0,0 +1,19 @@
1
+ """Базовый класс приложения — для быстрого создания игр"""
2
+ from aether.core.engine import Engine
3
+ from aether.scene.scene import Scene
4
+
5
+
6
+ class Application:
7
+ """Наследуйся от этого класса для создания игры"""
8
+
9
+ def __init__(self, width=1920, height=1080, title="Aether Game"):
10
+ self.engine = Engine(width, height, title)
11
+ self._setup()
12
+
13
+ def _setup(self):
14
+ """Переопредели этот метод для настройки сцены"""
15
+ scene = Scene("Default Scene")
16
+ self.engine.load_scene(scene)
17
+
18
+ def run(self):
19
+ self.engine.run()
aether/core/config.py ADDED
@@ -0,0 +1,75 @@
1
+ """Конфигурация движка — централизованные настройки"""
2
+ import json
3
+ import os
4
+ from dataclasses import dataclass, field, asdict
5
+ from typing import Dict, Any
6
+
7
+
8
+ @dataclass
9
+ class GraphicsConfig:
10
+ """Графические настройки"""
11
+ width: int = 1920
12
+ height: int = 1080
13
+ fullscreen: bool = False
14
+ vsync: bool = True
15
+ msaa_samples: int = 4
16
+ shadow_resolution: int = 2048
17
+ shadow_cascades: int = 4
18
+ bloom_enabled: bool = True
19
+ ssao_enabled: bool = True
20
+ hdr_enabled: bool = True
21
+
22
+
23
+ @dataclass
24
+ class PhysicsConfig:
25
+ """Настройки физики"""
26
+ gravity: tuple = (0.0, -9.81, 0.0)
27
+ fixed_timestep: float = 1.0 / 60.0
28
+ max_substeps: int = 8
29
+ solver_iterations: int = 10
30
+
31
+
32
+ @dataclass
33
+ class AudioConfig:
34
+ """Настройки звука"""
35
+ master_volume: float = 1.0
36
+ sfx_volume: float = 1.0
37
+ music_volume: float = 0.8
38
+ sample_rate: int = 44100
39
+ channels: int = 32
40
+
41
+
42
+ @dataclass
43
+ class EngineConfig:
44
+ """Главная конфигурация движка"""
45
+ app_name: str = "Aether Game"
46
+ graphics: GraphicsConfig = field(default_factory=GraphicsConfig)
47
+ physics: PhysicsConfig = field(default_factory=PhysicsConfig)
48
+ audio: AudioConfig = field(default_factory=AudioConfig)
49
+ asset_paths: list = field(default_factory=lambda: ["./assets", "./"])
50
+
51
+ def save(self, path: str):
52
+ """Сохраняет конфиг в JSON"""
53
+ with open(path, 'w') as f:
54
+ json.dump(asdict(self), f, indent=2)
55
+
56
+ @classmethod
57
+ def load(cls, path: str) -> 'EngineConfig':
58
+ """Загружает конфиг из JSON"""
59
+ with open(path, 'r') as f:
60
+ data = json.load(f)
61
+
62
+ config = cls()
63
+ config.app_name = data.get('app_name', config.app_name)
64
+
65
+ gfx = data.get('graphics', {})
66
+ config.graphics = GraphicsConfig(**gfx)
67
+
68
+ phys = data.get('physics', {})
69
+ config.physics = PhysicsConfig(**phys)
70
+
71
+ audio = data.get('audio', {})
72
+ config.audio = AudioConfig(**audio)
73
+
74
+ config.asset_paths = data.get('asset_paths', config.asset_paths)
75
+ return config