mima-engine 0.1.4__py3-none-any.whl → 0.2.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.

Potentially problematic release.


This version of mima-engine might be problematic. Click here for more details.

Files changed (80) hide show
  1. mima/__init__.py +1 -1
  2. mima/backend/pygame_assets.py +15 -9
  3. mima/backend/pygame_audio.py +5 -2
  4. mima/backend/pygame_backend.py +255 -57
  5. mima/backend/pygame_camera.py +63 -0
  6. mima/backend/pygame_events.py +331 -85
  7. mima/collision.py +182 -111
  8. mima/engine.py +155 -15
  9. mima/maps/tiled/tiled_map.py +3 -3
  10. mima/maps/tiled/tiled_tileset.py +1 -0
  11. mima/maps/tilemap.py +78 -15
  12. mima/maps/tileset.py +8 -2
  13. mima/maps/transition_map.py +6 -8
  14. mima/mode_engine.py +80 -0
  15. mima/objects/animated_sprite.py +23 -15
  16. mima/objects/attributes.py +3 -0
  17. mima/objects/creature.py +64 -25
  18. mima/objects/dynamic.py +30 -8
  19. mima/objects/effects/colorize_screen.py +22 -6
  20. mima/objects/effects/debug_box.py +124 -0
  21. mima/objects/effects/light.py +21 -30
  22. mima/objects/effects/show_sprite.py +39 -0
  23. mima/objects/effects/walking_on_grass.py +25 -7
  24. mima/objects/effects/walking_on_water.py +17 -6
  25. mima/objects/loader.py +24 -13
  26. mima/objects/projectile.py +21 -6
  27. mima/objects/sprite.py +7 -8
  28. mima/objects/world/color_gate.py +5 -2
  29. mima/objects/world/color_switch.py +12 -6
  30. mima/objects/world/container.py +17 -8
  31. mima/objects/world/floor_switch.py +8 -4
  32. mima/objects/world/gate.py +8 -5
  33. mima/objects/world/light_source.py +11 -9
  34. mima/objects/world/logic_gate.py +8 -7
  35. mima/objects/world/movable.py +72 -28
  36. mima/objects/world/oneway.py +14 -9
  37. mima/objects/world/pickup.py +10 -5
  38. mima/objects/world/switch.py +28 -25
  39. mima/objects/world/teleport.py +76 -55
  40. mima/scene_engine.py +19 -20
  41. mima/scripts/command.py +16 -2
  42. mima/scripts/commands/change_map.py +23 -4
  43. mima/scripts/commands/equip_weapon.py +23 -0
  44. mima/scripts/commands/give_item.py +5 -3
  45. mima/scripts/commands/move_map.py +9 -9
  46. mima/scripts/commands/parallel.py +16 -3
  47. mima/scripts/commands/present_item.py +7 -5
  48. mima/scripts/commands/screen_fade.py +30 -12
  49. mima/scripts/commands/serial.py +30 -7
  50. mima/scripts/commands/set_spawn_map.py +6 -3
  51. mima/scripts/commands/show_choices.py +16 -7
  52. mima/scripts/commands/show_dialog.py +110 -3
  53. mima/scripts/script_processor.py +41 -20
  54. mima/states/game_state.py +2 -0
  55. mima/states/memory.py +28 -0
  56. mima/states/quest.py +2 -3
  57. mima/types/keys.py +48 -0
  58. mima/types/mode.py +4 -10
  59. mima/types/player.py +9 -0
  60. mima/types/position.py +13 -0
  61. mima/types/tile_collision.py +11 -0
  62. mima/types/window.py +44 -0
  63. mima/usables/item.py +1 -0
  64. mima/util/colors.py +5 -0
  65. mima/util/constants.py +6 -0
  66. mima/util/functions.py +27 -0
  67. mima/util/input_defaults.py +109 -0
  68. mima/util/runtime_config.py +234 -30
  69. mima/util/trading_item.py +20 -0
  70. mima/view/camera.py +160 -19
  71. mima/view/mima_mode.py +612 -0
  72. mima/view/mima_scene.py +225 -0
  73. mima/view/mima_view.py +12 -0
  74. mima/view/mima_window.py +153 -0
  75. {mima_engine-0.1.4.dist-info → mima_engine-0.2.0.dist-info}/METADATA +4 -2
  76. mima_engine-0.2.0.dist-info/RECORD +128 -0
  77. {mima_engine-0.1.4.dist-info → mima_engine-0.2.0.dist-info}/WHEEL +1 -1
  78. mima/view/scene.py +0 -322
  79. mima_engine-0.1.4.dist-info/RECORD +0 -114
  80. {mima_engine-0.1.4.dist-info → mima_engine-0.2.0.dist-info}/top_level.txt +0 -0
mima/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.4"
1
+ __version__ = "0.2.0"
@@ -12,6 +12,7 @@ from ..maps.tiled.tiled_template import TiledTemplate
12
12
  from ..maps.tiled.tiled_tileset import TiledTileset
13
13
 
14
14
  if TYPE_CHECKING:
15
+ from ..maps.tilemap import Tilemap
15
16
  from ..util import RuntimeConfig
16
17
 
17
18
  LOG = logging.getLogger(__name__)
@@ -111,9 +112,9 @@ class PygameAssets:
111
112
  name = os.path.split(snd)[1].split(".")[0]
112
113
  self._load_sound(name, snd)
113
114
 
114
- for csv in self._assets_to_load.get("csv", []):
115
- name = os.path.split(csv)[1].split(".")[0]
116
- self._load_csv(name, csv)
115
+ for csvf in self._assets_to_load.get("csv", []):
116
+ name = os.path.split(csvf)[1].split(".")[0]
117
+ self._load_csv(name, csvf)
117
118
 
118
119
  def _load_csv(self, name, filename):
119
120
  if not os.path.isfile(filename):
@@ -220,9 +221,9 @@ class PygameAssets:
220
221
  return name
221
222
  try:
222
223
  self._maps[name] = TiledMap(name, filename)
223
- except KeyError as err:
224
- print(f"Failed to load map={name}")
225
- raise err
224
+ except KeyError:
225
+ LOG.exception(f"Failed to load map={name}")
226
+ raise
226
227
 
227
228
  return name
228
229
 
@@ -282,19 +283,24 @@ class PygameAssets:
282
283
  if name is None:
283
284
  return None
284
285
  else:
285
- if self.rtc.flags["use_color"]:
286
+ if self.rtc.flags.get("use_color", False):
286
287
  name_color = f"{name}_color"
287
288
  else:
288
289
  name_color = name
289
290
  return self._sprites.get(name_color, self._sprites[name])
290
291
 
292
+ def new_map(self, name, tilemap: Tilemap):
293
+ self._maps[name] = tilemap
294
+
291
295
  def new_sprite(self, name, surface: pygame.Surface):
292
296
  self._sprites[name] = surface
293
297
 
294
298
  def get_tileset(self, name):
295
299
  if name not in self._tilesets:
296
- LOG.warning("Could not find tileset %s.", name)
297
- return self._tilesets["simple_sheet"]
300
+ msg = f"Could not find tileset '{name}'."
301
+ raise ValueError(msg)
302
+ # LOG.warning("Could not find tileset %s.", name)
303
+ # return self._tilesets["simple_sheet"]
298
304
  return self._tilesets[name]
299
305
 
300
306
  def get_template(self, name):
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  from typing import TYPE_CHECKING, List, Tuple
4
5
 
5
6
  import pygame
@@ -7,6 +8,8 @@ import pygame
7
8
  if TYPE_CHECKING:
8
9
  from .pygame_assets import PygameAssets
9
10
 
11
+ LOG = logging.getLogger(__name__)
12
+
10
13
 
11
14
  class PygameAudio:
12
15
  def __init__(self, assets: PygameAssets):
@@ -45,8 +48,8 @@ class PygameAudio:
45
48
  self._play_music(name)
46
49
  self.current_track = name
47
50
  self.is_playing = True
48
- except Exception as err:
49
- print(f"Could not load {name}: {err}")
51
+ except Exception:
52
+ LOG.exception(f"Could not load {name}.")
50
53
 
51
54
  def _play_music(self, name: str, repeat: int = -1):
52
55
  pygame.mixer.music.load(self.assets.get_music(name))
@@ -1,30 +1,33 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import TYPE_CHECKING
4
+ from typing import TYPE_CHECKING, Dict, List, Optional
5
5
 
6
6
  import pygame
7
7
 
8
8
  from ..types.blend import Blend
9
9
  from ..util.colors import BLACK, WHITE, Color
10
10
  from ..util.constants import (
11
- BIG_FONT_HEIGHT,
12
11
  BIG_FONT_NAME,
13
- BIG_FONT_WIDTH,
14
12
  SMALL_FONT_HEIGHT,
15
13
  SMALL_FONT_NAME,
16
14
  SMALL_FONT_WIDTH,
17
15
  )
18
16
  from .pygame_assets import PygameAssets
19
17
  from .pygame_audio import PygameAudio
18
+ from .pygame_camera import PygameCamera
20
19
  from .pygame_events import SDL2_BACKGROUND_EVENTS, PygameUserInput
21
20
 
22
21
  if TYPE_CHECKING:
23
22
  from ..engine import MimaEngine
23
+ from ..types.keys import Key as K
24
+ from ..types.player import Player
24
25
  from ..util import RuntimeConfig
25
26
 
26
27
  LOG = logging.getLogger(__name__)
27
28
 
29
+ C_DISPLAY = "display"
30
+
28
31
 
29
32
  class PygameBackend:
30
33
  engine: MimaEngine
@@ -57,14 +60,22 @@ class PygameBackend:
57
60
  self._filter_blend: Blend = Blend.DEFAULT
58
61
  self._last_sprite_name: str = ""
59
62
  self._last_sprite: pygame.Surface
63
+ self._cameras: Dict[str, PygameCamera] = {}
60
64
 
61
- def init(self):
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
+ ):
62
71
  LOG.info("Initializing pygame backend.")
63
72
  pygame.mixer.pre_init(44100, -16, 2, 2048)
64
73
  pygame.init()
65
74
 
66
75
  self.clock = pygame.time.Clock()
67
- self.user_input = PygameUserInput(platform=self.platform)
76
+ self.user_input = PygameUserInput(
77
+ keyboard_map, joystick_map, joy_to_player, self.platform
78
+ )
68
79
  self.assets = PygameAssets(self.rtc, self.init_file)
69
80
  self.audio = PygameAudio(self.assets)
70
81
 
@@ -100,11 +111,14 @@ class PygameBackend:
100
111
 
101
112
  if pixel_size > 1:
102
113
  self.manual_scale = True
103
- self.display = pygame.Surface(
104
- (self.render_width, self.render_height)
105
- )
114
+ self.new_camera(C_DISPLAY, self.render_width, self.render_height)
115
+ # self.display = pygame.Surface(
116
+ # (self.render_width, self.render_height)
117
+ # )
106
118
  else:
107
- self.display = self._screen
119
+ self.new_camera(C_DISPLAY, self.display_width, self.display_height)
120
+ self._cameras[C_DISPLAY].view = self._screen
121
+ # self.display = self._screen
108
122
 
109
123
  if self.platform == "PC":
110
124
  if self.icon_path:
@@ -121,26 +135,44 @@ class PygameBackend:
121
135
  self.user_input.width = self.render_width
122
136
  self.user_input.height = self.render_height
123
137
  self.assets.load()
124
- # self.assets.load_items()
125
138
 
126
139
  def clear(self, color: Color = Color(0, 0, 0)):
127
- self.display.fill(color.getRGBA())
128
- self._filter = pygame.Surface((self.render_width, self.render_height))
129
- self._filter.fill(self._filter_color.getRGBA())
140
+ for camera in self._cameras.values():
141
+ camera.clear(color)
142
+
143
+ if self.manual_scale:
144
+ self._screen.fill(color.getRGBA())
145
+
146
+ def add_camera(self, name, camera):
147
+ self._cameras[name] = camera
148
+
149
+ def remove_camera(self, name):
150
+ return self._cameras.pop(name)
151
+
152
+ def new_camera(self, name, width, height):
153
+ self._cameras[name] = PygameCamera(int(width), int(height))
154
+
155
+ def get_camera(self, name):
156
+ return self._cameras[name]
130
157
 
131
158
  def configure_filter(
132
- self, color: Color = BLACK, blend_mode: Blend = Blend.DEFAULT
159
+ self,
160
+ color: Color = BLACK,
161
+ blend_mode: Blend = Blend.DEFAULT,
162
+ camera: str = C_DISPLAY,
133
163
  ):
134
- self._filter_color = color
135
- self._filter_blend = blend_mode
136
-
137
- def apply_filter(self):
138
- if self._filter_blend != Blend.DEFAULT:
139
- self.display.blit(
140
- self._filter,
141
- pygame.Vector2(0, 0),
142
- special_flags=blend_to_pygame_flag(self._filter_blend),
143
- )
164
+ if camera == "all":
165
+ for cam in self._cameras.values():
166
+ cam.configure_filter(color, blend_mode)
167
+ else:
168
+ self._cameras[camera].configure_filter(color, blend_mode)
169
+
170
+ def apply_filter(self, camera: str = C_DISPLAY):
171
+ if camera == "all":
172
+ for cam in self._cameras.values():
173
+ cam.apply_filter()
174
+ else:
175
+ self._cameras[camera].apply_filter()
144
176
 
145
177
  def draw_partial_sprite(
146
178
  self,
@@ -151,14 +183,18 @@ class PygameBackend:
151
183
  sy: float,
152
184
  width: float,
153
185
  height: float,
186
+ camera_name: str = C_DISPLAY,
154
187
  blend_mode: Blend = Blend.DEFAULT,
155
188
  draw_to_filter: bool = False,
189
+ draw_to_ui: bool = False,
156
190
  ):
157
191
  if sprite_name:
158
192
  if draw_to_filter:
159
- display = self._filter
193
+ display = self._cameras[camera_name].filter
194
+ elif draw_to_ui:
195
+ display = self._cameras[camera_name].ui
160
196
  else:
161
- display = self.display
197
+ display = self._cameras[camera_name].view
162
198
 
163
199
  if sprite_name != self._last_sprite_name:
164
200
  self._last_sprite_name = sprite_name
@@ -171,22 +207,52 @@ class PygameBackend:
171
207
  sprite,
172
208
  pygame.Vector2(px, py),
173
209
  pygame.Rect(sx, sy, width, height),
174
- special_flags=0
175
- if blend_mode == Blend.DEFAULT
176
- else blend_to_pygame_flag(blend_mode),
210
+ special_flags=(
211
+ 0
212
+ if blend_mode == Blend.DEFAULT
213
+ else blend_to_pygame_flag(blend_mode)
214
+ ),
177
215
  )
178
- except Exception as e:
179
- print(
180
- f"Exception drawing sprite {sprite_name}: {px}, {py}, {sx}, {sy}, {width}, {height}"
216
+ except Exception:
217
+ LOG.exception(
218
+ f"Exception drawing sprite {sprite_name}: {px}, "
219
+ f"{py}, {sx}, {sy}, {width}, {height}"
181
220
  )
182
- raise e
183
- # else:
184
- # print(f"Draw sprite {sprite_name} at {(px, py)}.")
221
+ raise
185
222
 
186
223
  def draw_line(
187
- self, sx: float, sy: float, ex: float, ey: float, color: Color
224
+ self,
225
+ sx: float,
226
+ sy: float,
227
+ ex: float,
228
+ ey: float,
229
+ color: Color,
230
+ camera_name: str = C_DISPLAY,
231
+ blend_mode: Blend = Blend.DEFAULT,
232
+ draw_to_filter: bool = False,
233
+ draw_to_ui: bool = False,
234
+ width: int = 1,
188
235
  ):
189
- pygame.draw.line(self.display, color.getRGBA(), (sx, sy), (ex, ey))
236
+ if draw_to_filter:
237
+ display = self._cameras[camera_name].filter
238
+ elif draw_to_ui:
239
+ display = self._cameras[camera_name].ui
240
+ else:
241
+ display = self._cameras[camera_name].view
242
+ if blend_mode == Blend.DEFAULT:
243
+ pygame.draw.line(
244
+ display, color.getRGBA(), (sx, sy), (ex, ey), width
245
+ )
246
+ else:
247
+ surf = pygame.Surface(
248
+ (ex, ey)
249
+ ) # FIXME: lines into negative directions
250
+ surf.fill(BLACK)
251
+ pygame.draw.line(surf, color.getRGBA(), (0, 0), (ex, ey), width)
252
+ rect = surf.get_rect(topleft=(0, 0))
253
+ display.blit(
254
+ surf, rect, special_flags=blend_to_pygame_flag(blend_mode)
255
+ )
190
256
 
191
257
  def fill_circle(
192
258
  self,
@@ -194,13 +260,17 @@ class PygameBackend:
194
260
  py: float,
195
261
  radius: float,
196
262
  color: Color,
263
+ camera_name: str = C_DISPLAY,
197
264
  blend_mode: Blend = Blend.DEFAULT,
198
265
  draw_to_filter: bool = False,
266
+ draw_to_ui: bool = False,
199
267
  ):
200
268
  if draw_to_filter:
201
- display = self._filter
269
+ display = self._cameras[camera_name].filter
270
+ elif draw_to_ui:
271
+ display = self._cameras[camera_name].ui
202
272
  else:
203
- display = self.display
273
+ display = self._cameras[camera_name].view
204
274
 
205
275
  if blend_mode == Blend.DEFAULT:
206
276
  pygame.draw.circle(display, color.getRGBA(), (px, py), radius)
@@ -215,10 +285,32 @@ class PygameBackend:
215
285
  special_flags=blend_to_pygame_flag(blend_mode),
216
286
  )
217
287
 
218
- def draw_circle(self, px: float, py: float, radius: float, color: Color):
219
- pygame.draw.circle(
220
- self.display, color.getRGBA(), (px, py), radius, width=1
221
- )
288
+ def draw_circle(
289
+ self,
290
+ px: float,
291
+ py: float,
292
+ radius: float,
293
+ color: Color,
294
+ camera_name: str = C_DISPLAY,
295
+ blend_mode: Blend = Blend.DEFAULT,
296
+ draw_to_filter: bool = False,
297
+ draw_to_ui: bool = False,
298
+ ):
299
+ if draw_to_filter:
300
+ display = self._cameras[camera_name].filter
301
+ elif draw_to_ui:
302
+ display = self._cameras[camera_name].ui
303
+ else:
304
+ display = self._cameras[camera_name].view
305
+
306
+ if blend_mode == Blend.DEFAULT:
307
+ pygame.draw.circle(
308
+ display, color.getRGBA(), (px, py), radius, width=1
309
+ )
310
+ else:
311
+ raise NotImplementedError(
312
+ "Draw circle is not implemented with blendmode"
313
+ )
222
314
 
223
315
  def fill_rect(
224
316
  self,
@@ -227,13 +319,18 @@ class PygameBackend:
227
319
  width: float,
228
320
  height: float,
229
321
  color: Color,
322
+ camera_name: str = C_DISPLAY,
323
+ blend_mode: Blend = Blend.DEFAULT,
230
324
  draw_to_filter: bool = False,
325
+ draw_to_ui: bool = False,
231
326
  ):
232
327
  r, g, b, a = color.getRGBA()
233
328
  if draw_to_filter:
234
- display = self._filter
329
+ display = self._cameras[camera_name].filter
330
+ elif draw_to_ui:
331
+ display = self._cameras[camera_name].ui
235
332
  else:
236
- display = self.display
333
+ display = self._cameras[camera_name].view
237
334
 
238
335
  if a < 255:
239
336
  surf = pygame.Surface((width, height))
@@ -254,40 +351,75 @@ class PygameBackend:
254
351
  width: float,
255
352
  height: float,
256
353
  color: Color,
354
+ camera_name: str = C_DISPLAY,
257
355
  line_width: int = 1,
356
+ blend_mode: Blend = Blend.DEFAULT,
357
+ draw_to_filter: bool = False,
358
+ draw_to_ui: bool = False,
258
359
  ):
360
+
361
+ if draw_to_filter:
362
+ display = self._cameras[camera_name].filter
363
+ elif draw_to_ui:
364
+ display = self._cameras[camera_name].ui
365
+ else:
366
+ display = self._cameras[camera_name].view
259
367
  line_width = max(1, line_width)
260
368
  pygame.draw.rect(
261
- self.display,
369
+ display,
262
370
  color.getRGB(),
263
371
  pygame.Rect(px, py, width, height),
264
372
  width=line_width,
265
373
  )
266
374
 
267
375
  def draw_big_text(
268
- self, text: str, px: float, py: float, color: Color = WHITE
376
+ self,
377
+ text: str,
378
+ px: float,
379
+ py: float,
380
+ color: Color = WHITE,
381
+ camera_name: str = C_DISPLAY,
382
+ blend_mode: Blend = Blend.DEFAULT,
383
+ draw_to_filter: bool = False,
384
+ draw_to_ui: bool = False,
269
385
  ):
270
386
  self.draw_text(
271
387
  text,
272
388
  px,
273
389
  py,
274
- BIG_FONT_NAME,
275
- BIG_FONT_WIDTH,
276
- BIG_FONT_HEIGHT,
390
+ self.rtc.big_font_name,
391
+ self.rtc.big_font_width,
392
+ self.rtc.big_font_height,
277
393
  color,
394
+ camera_name,
395
+ blend_mode,
396
+ draw_to_filter,
397
+ draw_to_ui,
278
398
  )
279
399
 
280
400
  def draw_small_text(
281
- self, text: str, px: float, py: float, color: Color = WHITE
401
+ self,
402
+ text: str,
403
+ px: float,
404
+ py: float,
405
+ color: Color = WHITE,
406
+ camera_name: str = C_DISPLAY,
407
+ blend_mode: Blend = Blend.DEFAULT,
408
+ draw_to_filter: bool = False,
409
+ draw_to_ui: bool = False,
282
410
  ):
283
411
  self.draw_text(
284
412
  text,
285
413
  px,
286
414
  py,
287
- SMALL_FONT_NAME,
288
- SMALL_FONT_WIDTH,
289
- SMALL_FONT_HEIGHT,
415
+ self.rtc.small_font_name,
416
+ self.rtc.small_font_width,
417
+ self.rtc.small_font_height,
290
418
  color,
419
+ camera_name,
420
+ blend_mode,
421
+ draw_to_filter,
422
+ draw_to_ui,
291
423
  )
292
424
 
293
425
  def draw_text(
@@ -299,6 +431,10 @@ class PygameBackend:
299
431
  font_width: int,
300
432
  font_height: int,
301
433
  color: Color = WHITE,
434
+ camera_name: str = C_DISPLAY,
435
+ blend_mode: Blend = Blend.DEFAULT,
436
+ draw_to_filter: bool = False,
437
+ draw_to_ui: bool = False,
302
438
  ):
303
439
  if color != WHITE:
304
440
  old_sprite = self.assets.get_sprite(font_name)
@@ -321,12 +457,21 @@ class PygameBackend:
321
457
  sy,
322
458
  font_width,
323
459
  font_height,
460
+ camera_name,
461
+ blend_mode,
462
+ draw_to_filter,
463
+ draw_to_ui,
324
464
  )
325
465
  idx += 1
326
466
 
327
467
  def draw_pixel(self, px: float, py: float, color: Color):
328
468
  self.display.set_at((int(px), int(py)), color.getRGB())
329
469
 
470
+ def draw_camera(self, name, px, py, scale):
471
+ self.display.blit(
472
+ pygame.transform.scale_by(self._cameras[name], scale), (px, py)
473
+ )
474
+
330
475
  def set_caption(self, text: str):
331
476
  pygame.display.set_caption(text)
332
477
 
@@ -350,13 +495,66 @@ class PygameBackend:
350
495
 
351
496
  self.user_input.process(event)
352
497
 
353
- def update_display(self):
354
- if self.manual_scale:
498
+ def update_display(self, *cameras):
499
+ scale = self.display_width / self.render_width
500
+ for cam_name in cameras:
501
+ cam = self._cameras[cam_name]
502
+ # target_width = int(
503
+ # self._screen.get_width()
504
+ # + cam.render_width / self._screen.get_width()
505
+ # )
506
+ # target_height = int(
507
+ # self._screen.get_height()
508
+ # + cam.render_height / self._screen.get_height()
509
+ # )
510
+ # self._screen.blit(
511
+ # pygame.transform.scale_by(cam.view, scale),
512
+ # (cam.px * scale, cam.py * scale),
513
+ # )
514
+ self._screen.blit(
515
+ pygame.transform.scale(
516
+ cam.view,
517
+ (cam.render_width * scale, cam.render_height * scale),
518
+ ),
519
+ (cam.px * scale, cam.py * scale),
520
+ )
521
+ for cam_name in cameras:
522
+ cam = self._cameras[cam_name]
523
+ self._screen.blit(
524
+ pygame.transform.scale(
525
+ cam.ui,
526
+ (cam.render_width * scale, cam.render_height * scale),
527
+ ),
528
+ (cam.px * scale, cam.py * scale),
529
+ special_flags=(pygame.BLEND_ALPHA_SDL2),
530
+ )
531
+ # print(
532
+ # cam_name,
533
+ # (cam.px, cam.py),
534
+ # cam.view.get_size(),
535
+ # scale,
536
+ # # (target_width, target_height),
537
+ # # cam.camera_scale,
538
+ # # (
539
+ # # cam.view.get_width() * cam.camera_scale,
540
+ # # cam.view.get_height() * cam.camera_scale,
541
+ # # ),
542
+ # )
543
+ if not cameras and self.manual_scale:
544
+ self._screen.blit(
545
+ pygame.transform.scale(
546
+ self._cameras[C_DISPLAY].view,
547
+ (self.display_width, self.display_height),
548
+ ),
549
+ (0, 0),
550
+ )
355
551
  self._screen.blit(
356
552
  pygame.transform.scale(
357
- self.display, (self.display_width, self.display_height)
553
+ self._cameras[C_DISPLAY].ui,
554
+ (self.display_width, self.display_height),
358
555
  ),
359
556
  (0, 0),
557
+ special_flags=(pygame.BLEND_ALPHA_SDL2),
360
558
  )
361
559
 
362
560
  pygame.display.flip()
@@ -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