mima-engine 0.4.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 (153) hide show
  1. mima/__init__.py +4 -0
  2. mima/backend/__init__.py +1 -0
  3. mima/backend/pygame_assets.py +401 -0
  4. mima/backend/pygame_audio.py +78 -0
  5. mima/backend/pygame_backend.py +603 -0
  6. mima/backend/pygame_camera.py +63 -0
  7. mima/backend/pygame_events.py +695 -0
  8. mima/backend/touch_control_scheme_a.py +126 -0
  9. mima/backend/touch_control_scheme_b.py +132 -0
  10. mima/core/__init__.py +0 -0
  11. mima/core/collision.py +325 -0
  12. mima/core/database.py +58 -0
  13. mima/core/engine.py +367 -0
  14. mima/core/mode_engine.py +81 -0
  15. mima/core/scene_engine.py +81 -0
  16. mima/integrated/__init__.py +0 -0
  17. mima/integrated/entity.py +183 -0
  18. mima/integrated/layered_map.py +351 -0
  19. mima/integrated/sprite.py +156 -0
  20. mima/layered/__init__.py +0 -0
  21. mima/layered/assets.py +56 -0
  22. mima/layered/scene.py +415 -0
  23. mima/layered/shape.py +99 -0
  24. mima/layered/shaped_sprite.py +78 -0
  25. mima/layered/virtual_input.py +302 -0
  26. mima/maps/__init__.py +0 -0
  27. mima/maps/template.py +71 -0
  28. mima/maps/tile.py +20 -0
  29. mima/maps/tile_animation.py +7 -0
  30. mima/maps/tile_info.py +10 -0
  31. mima/maps/tile_layer.py +52 -0
  32. mima/maps/tiled/__init__.py +0 -0
  33. mima/maps/tiled/tiled_layer.py +48 -0
  34. mima/maps/tiled/tiled_map.py +95 -0
  35. mima/maps/tiled/tiled_object.py +79 -0
  36. mima/maps/tiled/tiled_objectgroup.py +25 -0
  37. mima/maps/tiled/tiled_template.py +49 -0
  38. mima/maps/tiled/tiled_tile.py +90 -0
  39. mima/maps/tiled/tiled_tileset.py +51 -0
  40. mima/maps/tilemap.py +216 -0
  41. mima/maps/tileset.py +39 -0
  42. mima/maps/tileset_info.py +9 -0
  43. mima/maps/transition_map.py +146 -0
  44. mima/objects/__init__.py +0 -0
  45. mima/objects/animated_sprite.py +217 -0
  46. mima/objects/attribute_effect.py +26 -0
  47. mima/objects/attributes.py +126 -0
  48. mima/objects/creature.py +384 -0
  49. mima/objects/dynamic.py +206 -0
  50. mima/objects/effects/__init__.py +0 -0
  51. mima/objects/effects/colorize_screen.py +60 -0
  52. mima/objects/effects/debug_box.py +133 -0
  53. mima/objects/effects/light.py +103 -0
  54. mima/objects/effects/show_sprite.py +50 -0
  55. mima/objects/effects/walking_on_grass.py +70 -0
  56. mima/objects/effects/walking_on_water.py +57 -0
  57. mima/objects/loader.py +111 -0
  58. mima/objects/projectile.py +111 -0
  59. mima/objects/sprite.py +116 -0
  60. mima/objects/world/__init__.py +0 -0
  61. mima/objects/world/color_gate.py +67 -0
  62. mima/objects/world/color_switch.py +101 -0
  63. mima/objects/world/container.py +175 -0
  64. mima/objects/world/floor_switch.py +109 -0
  65. mima/objects/world/gate.py +178 -0
  66. mima/objects/world/light_source.py +121 -0
  67. mima/objects/world/logic_gate.py +157 -0
  68. mima/objects/world/movable.py +399 -0
  69. mima/objects/world/oneway.py +195 -0
  70. mima/objects/world/pickup.py +157 -0
  71. mima/objects/world/switch.py +179 -0
  72. mima/objects/world/teleport.py +308 -0
  73. mima/py.typed +0 -0
  74. mima/scripts/__init__.py +2 -0
  75. mima/scripts/command.py +38 -0
  76. mima/scripts/commands/__init__.py +0 -0
  77. mima/scripts/commands/add_quest.py +19 -0
  78. mima/scripts/commands/change_map.py +34 -0
  79. mima/scripts/commands/close_dialog.py +9 -0
  80. mima/scripts/commands/equip_weapon.py +23 -0
  81. mima/scripts/commands/give_item.py +26 -0
  82. mima/scripts/commands/give_resource.py +51 -0
  83. mima/scripts/commands/move_map.py +152 -0
  84. mima/scripts/commands/move_to.py +49 -0
  85. mima/scripts/commands/oneway_move.py +58 -0
  86. mima/scripts/commands/parallel.py +66 -0
  87. mima/scripts/commands/play_sound.py +13 -0
  88. mima/scripts/commands/present_item.py +53 -0
  89. mima/scripts/commands/progress_quest.py +12 -0
  90. mima/scripts/commands/quit_game.py +8 -0
  91. mima/scripts/commands/save_game.py +14 -0
  92. mima/scripts/commands/screen_fade.py +83 -0
  93. mima/scripts/commands/serial.py +69 -0
  94. mima/scripts/commands/set_facing_direction.py +21 -0
  95. mima/scripts/commands/set_spawn_map.py +17 -0
  96. mima/scripts/commands/show_choices.py +52 -0
  97. mima/scripts/commands/show_dialog.py +118 -0
  98. mima/scripts/commands/take_coins.py +23 -0
  99. mima/scripts/script_processor.py +61 -0
  100. mima/standalone/__init__.py +0 -0
  101. mima/standalone/camera.py +153 -0
  102. mima/standalone/geometry.py +1318 -0
  103. mima/standalone/multicolumn_list.py +54 -0
  104. mima/standalone/pixel_font.py +84 -0
  105. mima/standalone/scripting.py +145 -0
  106. mima/standalone/spatial.py +186 -0
  107. mima/standalone/sprite.py +158 -0
  108. mima/standalone/tiled_map.py +1247 -0
  109. mima/standalone/transformed_view.py +433 -0
  110. mima/standalone/user_input.py +563 -0
  111. mima/states/__init__.py +0 -0
  112. mima/states/game_state.py +189 -0
  113. mima/states/memory.py +28 -0
  114. mima/states/quest.py +71 -0
  115. mima/types/__init__.py +0 -0
  116. mima/types/alignment.py +7 -0
  117. mima/types/blend.py +8 -0
  118. mima/types/damage.py +42 -0
  119. mima/types/direction.py +44 -0
  120. mima/types/gate_color.py +7 -0
  121. mima/types/graphic_state.py +23 -0
  122. mima/types/keys.py +64 -0
  123. mima/types/mode.py +9 -0
  124. mima/types/nature.py +12 -0
  125. mima/types/object.py +22 -0
  126. mima/types/player.py +9 -0
  127. mima/types/position.py +13 -0
  128. mima/types/start.py +7 -0
  129. mima/types/terrain.py +9 -0
  130. mima/types/tile_collision.py +11 -0
  131. mima/types/weapon_slot.py +6 -0
  132. mima/types/window.py +44 -0
  133. mima/usables/__init__.py +0 -0
  134. mima/usables/item.py +51 -0
  135. mima/usables/weapon.py +68 -0
  136. mima/util/__init__.py +1 -0
  137. mima/util/colors.py +50 -0
  138. mima/util/constants.py +55 -0
  139. mima/util/functions.py +38 -0
  140. mima/util/input_defaults.py +170 -0
  141. mima/util/logging.py +51 -0
  142. mima/util/property.py +8 -0
  143. mima/util/runtime_config.py +327 -0
  144. mima/util/trading_item.py +23 -0
  145. mima/view/__init__.py +0 -0
  146. mima/view/camera.py +192 -0
  147. mima/view/mima_mode.py +618 -0
  148. mima/view/mima_scene.py +231 -0
  149. mima/view/mima_view.py +12 -0
  150. mima/view/mima_window.py +244 -0
  151. mima_engine-0.4.0.dist-info/METADATA +47 -0
  152. mima_engine-0.4.0.dist-info/RECORD +153 -0
  153. mima_engine-0.4.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,603 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from typing import TYPE_CHECKING, Dict, List, Optional
5
+
6
+ import pygame
7
+
8
+ from ..types.blend import Blend
9
+ from ..util.colors import BLACK, WHITE, Color
10
+ from ..util.constants import (
11
+ BIG_FONT_NAME,
12
+ SMALL_FONT_HEIGHT,
13
+ SMALL_FONT_NAME,
14
+ SMALL_FONT_WIDTH,
15
+ )
16
+ from .pygame_assets import PygameAssets
17
+ from .pygame_audio import PygameAudio
18
+ from .pygame_camera import PygameCamera
19
+ from .pygame_events import SDL2_BACKGROUND_EVENTS, PygameUserInput
20
+
21
+ if TYPE_CHECKING:
22
+ from ..engine import MimaEngine
23
+ from ..types.keys import Key as K
24
+ from ..types.player import Player
25
+ from ..util import RuntimeConfig
26
+
27
+ LOG = logging.getLogger(__name__)
28
+
29
+ C_DISPLAY = "display"
30
+
31
+
32
+ class PygameBackend:
33
+ engine: MimaEngine
34
+
35
+ def __init__(self, rtc: RuntimeConfig, init_file: str, platform: str):
36
+ self.rtc = rtc
37
+ self.init_file: str = init_file
38
+ self.platform: str = platform
39
+ self.icon_path: str = ""
40
+ self.splash_path: str = ""
41
+
42
+ self.render_width: int
43
+ self.render_height: int
44
+ self.pixel_size: int
45
+ self.display_width: int
46
+ self.display_height: int
47
+ self.target_fps: int
48
+
49
+ self.clock: pygame.time.Clock
50
+ self.manual_scale: bool = False
51
+ self.terminate: bool = False
52
+ self.user_input: PygameUserInput
53
+ self.assets: PygameAssets
54
+ self.audio: PygameAudio
55
+
56
+ self.display: pygame.Surface
57
+ self._screen: pygame.Surface
58
+ self._filter: pygame.Surface
59
+ self._filter_color: Color = BLACK
60
+ self._filter_blend: Blend = Blend.DEFAULT
61
+ self._last_sprite_name: str = ""
62
+ self._last_sprite: pygame.Surface
63
+ self._cameras: Dict[str, PygameCamera] = {}
64
+
65
+ def init(
66
+ self,
67
+ keyboard_map: Optional[Dict[K, List[str]]] = None,
68
+ joystick_map: Optional[Dict[K, List[str]]] = None,
69
+ joy_to_player: Optional[Dict[int, Player]] = None,
70
+ ):
71
+ LOG.info("Initializing pygame backend.")
72
+ pygame.mixer.pre_init(44100, -16, 2, 2048)
73
+ pygame.init()
74
+
75
+ self.clock = pygame.time.Clock()
76
+ self.user_input = PygameUserInput(
77
+ keyboard_map, joystick_map, joy_to_player, self.platform
78
+ )
79
+ self.assets = PygameAssets(self.rtc, self.init_file)
80
+ self.audio = PygameAudio(self.assets)
81
+
82
+ def construct(
83
+ self,
84
+ width: int,
85
+ height: int,
86
+ pixel_size: int,
87
+ fullscreen: bool = False,
88
+ target_fps: int = 60,
89
+ resizeable: bool = False,
90
+ no_scaled_flag: bool = False,
91
+ ):
92
+ LOG.info("Constructing window.")
93
+ self.render_width, self.display_width = width, width
94
+ self.render_height, self.display_height = height, height
95
+ self.pixel_size = pixel_size
96
+ self.target_fps = target_fps
97
+
98
+ flags = pygame.HWSURFACE | pygame.DOUBLEBUF
99
+ if fullscreen:
100
+ flags = flags | pygame.FULLSCREEN
101
+ if not no_scaled_flag:
102
+ flags = flags | pygame.SCALED
103
+ self.pixel_size = -1 # Calculate later
104
+ elif pixel_size == 0:
105
+ flags = flags | pygame.SCALED
106
+ self.pixel_size = -1 # Calculate later
107
+ else:
108
+ self.display_width = self.render_width * self.pixel_size
109
+ self.display_height = self.render_height * self.pixel_size
110
+
111
+ self._screen = pygame.display.set_mode(
112
+ (self.display_width, self.display_height), flags
113
+ )
114
+
115
+ if pixel_size > 1:
116
+ self.manual_scale = True
117
+ self.new_camera(C_DISPLAY, self.render_width, self.render_height)
118
+ # self.display = pygame.Surface(
119
+ # (self.render_width, self.render_height)
120
+ # )
121
+ else:
122
+ self.new_camera(C_DISPLAY, self.display_width, self.display_height)
123
+ self._cameras[C_DISPLAY].view = self._screen
124
+ # self.display = self._screen
125
+
126
+ if self.platform == "PC":
127
+ if self.icon_path:
128
+ icon = pygame.image.load(self.icon_path).convert_alpha()
129
+ pygame.display.set_icon(icon)
130
+
131
+ if self.splash_path:
132
+ splash_screen = pygame.image.load(
133
+ self.splash_path
134
+ ).convert_alpha()
135
+ self.display.blit(splash_screen, (0, 0))
136
+ pygame.display.flip()
137
+
138
+ self.user_input.width = self.render_width
139
+ self.user_input.height = self.render_height
140
+ self.assets.load()
141
+
142
+ def clear(self, color: Color = Color(0, 0, 0)):
143
+ for camera in self._cameras.values():
144
+ camera.clear(color)
145
+
146
+ if self.manual_scale:
147
+ self._screen.fill(color.getRGBA())
148
+
149
+ def add_camera(self, name, camera):
150
+ self._cameras[name] = camera
151
+
152
+ def remove_camera(self, name):
153
+ return self._cameras.pop(name)
154
+
155
+ def new_camera(self, name, width, height):
156
+ self._cameras[name] = PygameCamera(int(width), int(height))
157
+
158
+ def get_camera(self, name):
159
+ return self._cameras[name]
160
+
161
+ def configure_filter(
162
+ self,
163
+ color: Color = BLACK,
164
+ blend_mode: Blend = Blend.DEFAULT,
165
+ camera: str = C_DISPLAY,
166
+ ):
167
+ if camera == "all":
168
+ for cam in self._cameras.values():
169
+ cam.configure_filter(color, blend_mode)
170
+ else:
171
+ self._cameras[camera].configure_filter(color, blend_mode)
172
+
173
+ def apply_filter(self, camera: str = C_DISPLAY):
174
+ if camera == "all":
175
+ for cam in self._cameras.values():
176
+ cam.apply_filter()
177
+ else:
178
+ self._cameras[camera].apply_filter()
179
+
180
+ def draw_partial_sprite(
181
+ self,
182
+ px: float,
183
+ py: float,
184
+ sprite_name: float,
185
+ sx: float,
186
+ sy: float,
187
+ width: float,
188
+ height: float,
189
+ camera_name: str = C_DISPLAY,
190
+ blend_mode: Blend = Blend.DEFAULT,
191
+ draw_to_filter: bool = False,
192
+ draw_to_ui: bool = False,
193
+ ):
194
+ if sprite_name:
195
+ if draw_to_filter:
196
+ display = self._cameras[camera_name].filter
197
+ elif draw_to_ui:
198
+ display = self._cameras[camera_name].ui
199
+ else:
200
+ display = self._cameras[camera_name].view
201
+
202
+ if sprite_name != self._last_sprite_name:
203
+ self._last_sprite_name = sprite_name
204
+ self._last_sprite = self.assets.get_sprite(sprite_name)
205
+
206
+ sprite = self._last_sprite
207
+ if sprite is not None:
208
+ try:
209
+ display.blit(
210
+ sprite,
211
+ pygame.Vector2(px, py),
212
+ pygame.Rect(sx, sy, width, height),
213
+ special_flags=(
214
+ 0
215
+ if blend_mode == Blend.DEFAULT
216
+ else blend_to_pygame_flag(blend_mode)
217
+ ),
218
+ )
219
+ except Exception:
220
+ LOG.exception(
221
+ f"Exception drawing sprite {sprite_name}: {px}, "
222
+ f"{py}, {sx}, {sy}, {width}, {height}"
223
+ )
224
+ raise
225
+
226
+ def draw_line(
227
+ self,
228
+ sx: float,
229
+ sy: float,
230
+ ex: float,
231
+ ey: float,
232
+ color: Color,
233
+ camera_name: str = C_DISPLAY,
234
+ blend_mode: Blend = Blend.DEFAULT,
235
+ draw_to_filter: bool = False,
236
+ draw_to_ui: bool = False,
237
+ width: int = 1,
238
+ ):
239
+ if draw_to_filter:
240
+ display = self._cameras[camera_name].filter
241
+ elif draw_to_ui:
242
+ display = self._cameras[camera_name].ui
243
+ else:
244
+ display = self._cameras[camera_name].view
245
+ if blend_mode == Blend.DEFAULT:
246
+ pygame.draw.line(
247
+ display, color.getRGBA(), (sx, sy), (ex, ey), width
248
+ )
249
+ else:
250
+ surf = pygame.Surface(
251
+ (ex, ey)
252
+ ) # FIXME: lines into negative directions
253
+ surf.fill(BLACK)
254
+ pygame.draw.line(surf, color.getRGBA(), (0, 0), (ex, ey), width)
255
+ rect = surf.get_rect(topleft=(0, 0))
256
+ display.blit(
257
+ surf, rect, special_flags=blend_to_pygame_flag(blend_mode)
258
+ )
259
+
260
+ def fill_circle(
261
+ self,
262
+ px: float,
263
+ py: float,
264
+ radius: float,
265
+ color: Color,
266
+ camera_name: str = C_DISPLAY,
267
+ blend_mode: Blend = Blend.DEFAULT,
268
+ draw_to_filter: bool = False,
269
+ draw_to_ui: bool = False,
270
+ ):
271
+ if draw_to_filter:
272
+ display = self._cameras[camera_name].filter
273
+ elif draw_to_ui:
274
+ display = self._cameras[camera_name].ui
275
+ else:
276
+ display = self._cameras[camera_name].view
277
+
278
+ if blend_mode == Blend.DEFAULT:
279
+ pygame.draw.circle(display, color.getRGBA(), (px, py), radius)
280
+ else:
281
+ surf = pygame.Surface((radius * 2, radius * 2))
282
+ surf.fill((0, 0, 0))
283
+ pygame.draw.circle(surf, color.getRGBA(), (radius, radius), radius)
284
+ rect = surf.get_rect(center=(px, py))
285
+ display.blit(
286
+ surf,
287
+ rect,
288
+ special_flags=blend_to_pygame_flag(blend_mode),
289
+ )
290
+
291
+ def draw_circle(
292
+ self,
293
+ px: float,
294
+ py: float,
295
+ radius: float,
296
+ color: Color,
297
+ camera_name: str = C_DISPLAY,
298
+ blend_mode: Blend = Blend.DEFAULT,
299
+ draw_to_filter: bool = False,
300
+ draw_to_ui: bool = False,
301
+ ):
302
+ if draw_to_filter:
303
+ display = self._cameras[camera_name].filter
304
+ elif draw_to_ui:
305
+ display = self._cameras[camera_name].ui
306
+ else:
307
+ display = self._cameras[camera_name].view
308
+
309
+ if blend_mode == Blend.DEFAULT:
310
+ pygame.draw.circle(
311
+ display, color.getRGBA(), (px, py), radius, width=1
312
+ )
313
+ else:
314
+ raise NotImplementedError(
315
+ "Draw circle is not implemented with blendmode"
316
+ )
317
+
318
+ def fill_rect(
319
+ self,
320
+ px: float,
321
+ py: float,
322
+ width: float,
323
+ height: float,
324
+ color: Color,
325
+ camera_name: str = C_DISPLAY,
326
+ blend_mode: Blend = Blend.DEFAULT,
327
+ draw_to_filter: bool = False,
328
+ draw_to_ui: bool = False,
329
+ ):
330
+ r, g, b, a = color.getRGBA()
331
+ if draw_to_filter:
332
+ display = self._cameras[camera_name].filter
333
+ elif draw_to_ui:
334
+ display = self._cameras[camera_name].ui
335
+ else:
336
+ display = self._cameras[camera_name].view
337
+
338
+ if a < 255:
339
+ surf = pygame.Surface((width, height))
340
+ surf.set_alpha(a)
341
+ surf.fill((r, g, b))
342
+ display.blit(surf, (px, py))
343
+ else:
344
+ pygame.draw.rect(
345
+ display,
346
+ (r, g, b),
347
+ pygame.Rect(px, py, width, height),
348
+ )
349
+
350
+ def draw_rect(
351
+ self,
352
+ px: float,
353
+ py: float,
354
+ width: float,
355
+ height: float,
356
+ color: Color,
357
+ camera_name: str = C_DISPLAY,
358
+ line_width: int = 1,
359
+ blend_mode: Blend = Blend.DEFAULT,
360
+ draw_to_filter: bool = False,
361
+ draw_to_ui: bool = False,
362
+ ):
363
+
364
+ if draw_to_filter:
365
+ display = self._cameras[camera_name].filter
366
+ elif draw_to_ui:
367
+ display = self._cameras[camera_name].ui
368
+ else:
369
+ display = self._cameras[camera_name].view
370
+ line_width = max(1, line_width)
371
+ pygame.draw.rect(
372
+ display,
373
+ color.getRGB(),
374
+ pygame.Rect(px, py, width, height),
375
+ width=line_width,
376
+ )
377
+
378
+ def draw_big_text(
379
+ self,
380
+ text: str,
381
+ px: float,
382
+ py: float,
383
+ color: Color = WHITE,
384
+ camera_name: str = C_DISPLAY,
385
+ blend_mode: Blend = Blend.DEFAULT,
386
+ draw_to_filter: bool = False,
387
+ draw_to_ui: bool = False,
388
+ ):
389
+ self.draw_text(
390
+ text,
391
+ px,
392
+ py,
393
+ self.rtc.big_font_name,
394
+ self.rtc.big_font_width,
395
+ self.rtc.big_font_height,
396
+ color,
397
+ camera_name,
398
+ blend_mode,
399
+ draw_to_filter,
400
+ draw_to_ui,
401
+ )
402
+
403
+ def draw_small_text(
404
+ self,
405
+ text: str,
406
+ px: float,
407
+ py: float,
408
+ color: Color = WHITE,
409
+ camera_name: str = C_DISPLAY,
410
+ blend_mode: Blend = Blend.DEFAULT,
411
+ draw_to_filter: bool = False,
412
+ draw_to_ui: bool = False,
413
+ ):
414
+ self.draw_text(
415
+ text,
416
+ px,
417
+ py,
418
+ self.rtc.small_font_name,
419
+ self.rtc.small_font_width,
420
+ self.rtc.small_font_height,
421
+ color,
422
+ camera_name,
423
+ blend_mode,
424
+ draw_to_filter,
425
+ draw_to_ui,
426
+ )
427
+
428
+ def draw_text(
429
+ self,
430
+ text: str,
431
+ px: float,
432
+ py: float,
433
+ font_name: str,
434
+ font_width: int,
435
+ font_height: int,
436
+ color: Color = WHITE,
437
+ camera_name: str = C_DISPLAY,
438
+ blend_mode: Blend = Blend.DEFAULT,
439
+ draw_to_filter: bool = False,
440
+ draw_to_ui: bool = False,
441
+ ):
442
+ if color != WHITE:
443
+ old_sprite = self.assets.get_sprite(font_name)
444
+ new_sprite = old_sprite.copy()
445
+ new_sprite.fill(
446
+ color.getRGBA(), special_flags=pygame.BLEND_RGBA_MIN
447
+ )
448
+ font_name = f"{font_name}_{color.short_name()}"
449
+ self.assets.new_sprite(font_name, new_sprite)
450
+
451
+ idx = 0
452
+ for c in text:
453
+ sx = ((ord(c) - 32) % 16) * font_width
454
+ sy = ((ord(c) - 32) // 16) * font_height
455
+ self.draw_partial_sprite(
456
+ px + idx * font_width,
457
+ py,
458
+ font_name,
459
+ sx,
460
+ sy,
461
+ font_width,
462
+ font_height,
463
+ camera_name,
464
+ blend_mode,
465
+ draw_to_filter,
466
+ draw_to_ui,
467
+ )
468
+ idx += 1
469
+
470
+ def draw_pixel(
471
+ self, px: float, py: float, color: Color, camera_name=C_DISPLAY
472
+ ):
473
+ display = self._cameras[camera_name].view
474
+ display.set_at((int(px), int(py)), color.getRGB())
475
+
476
+ def draw_camera(self, name, px, py, scale):
477
+ self.display.blit(
478
+ pygame.transform.scale_by(self._cameras[name], scale), (px, py)
479
+ )
480
+
481
+ def set_caption(self, text: str):
482
+ pygame.display.set_caption(text)
483
+
484
+ def keep_running(self) -> bool:
485
+ return not self.terminate
486
+
487
+ def process_events(self):
488
+ self.user_input.reset()
489
+
490
+ for event in pygame.event.get():
491
+ if event.type == pygame.QUIT:
492
+ self.terminate = True
493
+ break
494
+ if event.type == pygame.KEYDOWN:
495
+ if event.key == pygame.K_ESCAPE:
496
+ self.on_user_terminate()
497
+ continue
498
+ if event.type in SDL2_BACKGROUND_EVENTS:
499
+ self._handle_background(event)
500
+ continue
501
+
502
+ self.user_input.process(event)
503
+
504
+ def update_display(self, *cameras):
505
+ scale = self.display_width / self.render_width
506
+ for cam_name in cameras:
507
+ cam = self._cameras[cam_name]
508
+ # target_width = int(
509
+ # self._screen.get_width()
510
+ # + cam.render_width / self._screen.get_width()
511
+ # )
512
+ # target_height = int(
513
+ # self._screen.get_height()
514
+ # + cam.render_height / self._screen.get_height()
515
+ # )
516
+ # self._screen.blit(
517
+ # pygame.transform.scale_by(cam.view, scale),
518
+ # (cam.px * scale, cam.py * scale),
519
+ # )
520
+ self._screen.blit(
521
+ pygame.transform.scale(
522
+ cam.view,
523
+ (cam.render_width * scale, cam.render_height * scale),
524
+ ),
525
+ (cam.px * scale, cam.py * scale),
526
+ )
527
+ for cam_name in cameras:
528
+ cam = self._cameras[cam_name]
529
+ self._screen.blit(
530
+ pygame.transform.scale(
531
+ cam.ui,
532
+ (cam.render_width * scale, cam.render_height * scale),
533
+ ),
534
+ (cam.px * scale, cam.py * scale),
535
+ special_flags=(pygame.BLEND_ALPHA_SDL2),
536
+ )
537
+ # print(
538
+ # cam_name,
539
+ # (cam.px, cam.py),
540
+ # cam.view.get_size(),
541
+ # scale,
542
+ # # (target_width, target_height),
543
+ # # cam.camera_scale,
544
+ # # (
545
+ # # cam.view.get_width() * cam.camera_scale,
546
+ # # cam.view.get_height() * cam.camera_scale,
547
+ # # ),
548
+ # )
549
+ if not cameras and self.manual_scale:
550
+ self._screen.blit(
551
+ pygame.transform.scale(
552
+ self._cameras[C_DISPLAY].view,
553
+ (self.display_width, self.display_height),
554
+ ),
555
+ (0, 0),
556
+ )
557
+ self._screen.blit(
558
+ pygame.transform.scale(
559
+ self._cameras[C_DISPLAY].ui,
560
+ (self.display_width, self.display_height),
561
+ ),
562
+ (0, 0),
563
+ special_flags=(pygame.BLEND_ALPHA_SDL2),
564
+ )
565
+
566
+ pygame.display.flip()
567
+
568
+ def tick(self) -> float:
569
+ return self.clock.tick(self.target_fps) / 1000.0
570
+
571
+ def on_user_terminate(self):
572
+ self.engine.on_user_terminate()
573
+
574
+ def shutdown(self):
575
+ pygame.mixer.quit()
576
+ pygame.quit()
577
+
578
+ def _handle_background(self, event):
579
+ if event.type == 259:
580
+ self.engine.on_enter_background()
581
+ elif event.type == 260:
582
+ self.engine.on_entered_background()
583
+ elif event.type == 261:
584
+ self.engine.on_enter_foreground()
585
+ else:
586
+ self.engine.on_entered_foreground()
587
+
588
+ @property
589
+ def data_path(self):
590
+ return self.assets.data_path
591
+
592
+
593
+ def blend_to_pygame_flag(blend_mode: Blend):
594
+ if blend_mode == Blend.DEFAULT:
595
+ return 0
596
+ elif blend_mode == Blend.ADD:
597
+ return pygame.BLEND_RGBA_ADD
598
+ elif blend_mode == Blend.SUB:
599
+ return pygame.BLEND_RGBA_SUB
600
+ elif blend_mode == Blend.MULT:
601
+ return pygame.BLEND_RGBA_MULT
602
+ else:
603
+ return 0
@@ -0,0 +1,63 @@
1
+ from typing import Optional
2
+
3
+ import pygame
4
+
5
+ from ..types.blend import Blend
6
+ from ..util.colors import BLACK, WHITE, Color, ALPHA
7
+
8
+
9
+ class PygameCamera:
10
+ def __init__(self, width: int, height: int):
11
+ self.px: int = 0
12
+ self.py: int = 0
13
+ self.render_width = width
14
+ self.render_height = height
15
+ self.view: pygame.Surface = pygame.Surface((width, height))
16
+ self.filter: pygame.Surface = pygame.Surface((width, height))
17
+ self.ui: pygame.Surface = pygame.Surface((width, height))
18
+ self._filter_color: Color = BLACK
19
+ self._filter_blend: Blend = Blend.DEFAULT
20
+ self._ui_color: Color = ALPHA
21
+ self.camera_scale: float = 1.0
22
+
23
+ def clear(self, color: Color = BLACK):
24
+ self.view.fill(color.getRGBA())
25
+ self.filter.fill(self._filter_color.getRGBA())
26
+ self.ui.fill(self._ui_color.getRGBA())
27
+ self.ui.set_colorkey(self._ui_color.getRGBA())
28
+
29
+ def configure_filter(
30
+ self, color: Color = BLACK, blend_mode: Blend = Blend.DEFAULT
31
+ ):
32
+ self._filter_color = color
33
+ self._filter_blend = blend_mode
34
+
35
+ def apply_filter(self):
36
+ if self._filter_blend != Blend.DEFAULT:
37
+ self.view.blit(
38
+ self.filter,
39
+ pygame.Vector2(0, 0),
40
+ special_flags=blend_to_pygame_flag(self._filter_blend),
41
+ )
42
+
43
+ def change_zoom(self, width, height):
44
+ self.view = pygame.Surface((width, height))
45
+ self.filter = pygame.Surface((width, height))
46
+
47
+ def change_ui_zoom(self, width, height):
48
+ self.ui = pygame.Surface((width, height), flags=pygame.SRCALPHA)
49
+ self.ui.convert_alpha()
50
+ self.ui.set_colorkey(ALPHA.getRGBA())
51
+
52
+
53
+ def blend_to_pygame_flag(blend_mode: Blend):
54
+ if blend_mode == Blend.DEFAULT:
55
+ return 0
56
+ elif blend_mode == Blend.ADD:
57
+ return pygame.BLEND_RGBA_ADD
58
+ elif blend_mode == Blend.SUB:
59
+ return pygame.BLEND_RGBA_SUB
60
+ elif blend_mode == Blend.MULT:
61
+ return pygame.BLEND_RGBA_MULT
62
+ else:
63
+ return 0