mima-engine 0.1.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 (114) hide show
  1. mima/__init__.py +1 -0
  2. mima/backend/__init__.py +1 -0
  3. mima/backend/pygame_assets.py +345 -0
  4. mima/backend/pygame_audio.py +75 -0
  5. mima/backend/pygame_backend.py +399 -0
  6. mima/backend/pygame_events.py +430 -0
  7. mima/collision.py +237 -0
  8. mima/engine.py +197 -0
  9. mima/maps/__init__.py +0 -0
  10. mima/maps/template.py +41 -0
  11. mima/maps/tile.py +20 -0
  12. mima/maps/tile_animation.py +7 -0
  13. mima/maps/tile_info.py +10 -0
  14. mima/maps/tile_layer.py +52 -0
  15. mima/maps/tiled/__init__.py +0 -0
  16. mima/maps/tiled/tiled_layer.py +48 -0
  17. mima/maps/tiled/tiled_map.py +95 -0
  18. mima/maps/tiled/tiled_object.py +79 -0
  19. mima/maps/tiled/tiled_objectgroup.py +25 -0
  20. mima/maps/tiled/tiled_template.py +49 -0
  21. mima/maps/tiled/tiled_tile.py +90 -0
  22. mima/maps/tiled/tiled_tileset.py +45 -0
  23. mima/maps/tilemap.py +159 -0
  24. mima/maps/tileset.py +32 -0
  25. mima/maps/tileset_info.py +9 -0
  26. mima/maps/transition_map.py +148 -0
  27. mima/objects/__init__.py +0 -0
  28. mima/objects/animated_sprite.py +198 -0
  29. mima/objects/attribute_effect.py +26 -0
  30. mima/objects/attributes.py +123 -0
  31. mima/objects/creature.py +332 -0
  32. mima/objects/dynamic.py +182 -0
  33. mima/objects/effects/__init__.py +0 -0
  34. mima/objects/effects/colorize_screen.py +36 -0
  35. mima/objects/effects/light.py +107 -0
  36. mima/objects/effects/walking_on_grass.py +38 -0
  37. mima/objects/effects/walking_on_water.py +41 -0
  38. mima/objects/loader.py +103 -0
  39. mima/objects/projectile.py +86 -0
  40. mima/objects/sprite.py +110 -0
  41. mima/objects/world/__init__.py +0 -0
  42. mima/objects/world/color_gate.py +68 -0
  43. mima/objects/world/color_switch.py +105 -0
  44. mima/objects/world/container.py +171 -0
  45. mima/objects/world/floor_switch.py +111 -0
  46. mima/objects/world/gate.py +174 -0
  47. mima/objects/world/light_source.py +124 -0
  48. mima/objects/world/logic_gate.py +163 -0
  49. mima/objects/world/movable.py +338 -0
  50. mima/objects/world/oneway.py +168 -0
  51. mima/objects/world/pickup.py +88 -0
  52. mima/objects/world/switch.py +165 -0
  53. mima/objects/world/teleport.py +288 -0
  54. mima/scene_engine.py +79 -0
  55. mima/scripts/__init__.py +2 -0
  56. mima/scripts/command.py +24 -0
  57. mima/scripts/commands/__init__.py +0 -0
  58. mima/scripts/commands/add_quest.py +19 -0
  59. mima/scripts/commands/change_map.py +15 -0
  60. mima/scripts/commands/close_dialog.py +8 -0
  61. mima/scripts/commands/give_item.py +24 -0
  62. mima/scripts/commands/give_resource.py +51 -0
  63. mima/scripts/commands/move_map.py +152 -0
  64. mima/scripts/commands/move_to.py +49 -0
  65. mima/scripts/commands/oneway_move.py +57 -0
  66. mima/scripts/commands/parallel.py +53 -0
  67. mima/scripts/commands/play_sound.py +13 -0
  68. mima/scripts/commands/present_item.py +51 -0
  69. mima/scripts/commands/progress_quest.py +12 -0
  70. mima/scripts/commands/quit_game.py +8 -0
  71. mima/scripts/commands/save_game.py +13 -0
  72. mima/scripts/commands/screen_fade.py +65 -0
  73. mima/scripts/commands/serial.py +46 -0
  74. mima/scripts/commands/set_facing_direction.py +21 -0
  75. mima/scripts/commands/set_spawn_map.py +14 -0
  76. mima/scripts/commands/show_choices.py +43 -0
  77. mima/scripts/commands/show_dialog.py +11 -0
  78. mima/scripts/commands/take_coins.py +23 -0
  79. mima/scripts/script_processor.py +40 -0
  80. mima/states/__init__.py +0 -0
  81. mima/states/game_state.py +162 -0
  82. mima/states/quest.py +72 -0
  83. mima/types/__init__.py +0 -0
  84. mima/types/alignment.py +7 -0
  85. mima/types/blend.py +8 -0
  86. mima/types/damage.py +42 -0
  87. mima/types/direction.py +44 -0
  88. mima/types/gate_color.py +7 -0
  89. mima/types/graphic_state.py +22 -0
  90. mima/types/keys.py +16 -0
  91. mima/types/mode.py +15 -0
  92. mima/types/nature.py +12 -0
  93. mima/types/object.py +22 -0
  94. mima/types/start.py +7 -0
  95. mima/types/terrain.py +9 -0
  96. mima/types/weapon_slot.py +6 -0
  97. mima/usables/__init__.py +0 -0
  98. mima/usables/item.py +31 -0
  99. mima/usables/weapon.py +48 -0
  100. mima/util/__init__.py +1 -0
  101. mima/util/colors.py +45 -0
  102. mima/util/constants.py +47 -0
  103. mima/util/functions.py +13 -0
  104. mima/util/input_defaults.py +49 -0
  105. mima/util/logging.py +51 -0
  106. mima/util/property.py +8 -0
  107. mima/util/runtime_config.py +133 -0
  108. mima/view/__init__.py +0 -0
  109. mima/view/camera.py +51 -0
  110. mima/view/scene.py +350 -0
  111. mima_engine-0.1.0.dist-info/METADATA +14 -0
  112. mima_engine-0.1.0.dist-info/RECORD +114 -0
  113. mima_engine-0.1.0.dist-info/WHEEL +5 -0
  114. mima_engine-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,107 @@
1
+ from typing import List
2
+
3
+ from ...types.alignment import Alignment
4
+ from ...types.blend import Blend
5
+ from ...util.colors import BLACK, DARK_GREY, VERY_LIGHT_GREY, WHITE
6
+ from ...util.constants import TILE_HEIGHT, TILE_WIDTH
7
+ from ..dynamic import Dynamic
8
+ from ..projectile import Projectile
9
+
10
+
11
+ class Light(Projectile):
12
+ def __init__(
13
+ self,
14
+ follow: Dynamic,
15
+ max_size: int = 64,
16
+ fixed_size: bool = False,
17
+ update_from_target: bool = False,
18
+ ):
19
+ super().__init__(0, 0, 0, 0, 0, Alignment.GOOD)
20
+ self.layer = 1
21
+ self.sprite.name = "light_small"
22
+ self.sprite.width = 48
23
+ self.sprite.height = 48
24
+ self.solid_vs_map = False
25
+ self._follow: Dynamic = follow
26
+ self._fixed_size: bool = fixed_size
27
+ self._update_from_target: bool = update_from_target
28
+
29
+ self._timer: float = 0.2
30
+ self._timer_reset: float = 0.2
31
+ self._size_idx: int = 0
32
+
33
+ self._sizes: List[int]
34
+ self._max_size: int
35
+
36
+ self._prepare_light(max_size)
37
+
38
+ # print(f"Light({id(self)}, {self.layer}, {self.sprite.name}, {self.sprite.width, self.sprite.height}, {self._follow}, {self._max_size}, {self._fixed_size}, {self._sizes}, {self._size_idx})")
39
+
40
+ def update(self, elapsed_time: float, target: Dynamic = None):
41
+ self.px = (
42
+ self._follow.px + self._follow.sprite.width / TILE_WIDTH * 0.5
43
+ )
44
+ self.py = (
45
+ self._follow.py + self._follow.sprite.height / TILE_HEIGHT * 0.5
46
+ )
47
+
48
+ rad = self._follow.light_radius()
49
+ if self._max_size != rad:
50
+ self._prepare_light(rad)
51
+
52
+ if self._follow.redundant:
53
+ self.kill()
54
+
55
+ if self._fixed_size:
56
+ return
57
+
58
+ self._timer -= elapsed_time
59
+ if self._timer <= 0.0:
60
+ self._timer += self._timer_reset
61
+ self._size_idx = (self._size_idx + 1) % len(self._sizes)
62
+
63
+ # self.sprite.update(elapsed_time, self.facing_direction, self.graphic_state)
64
+
65
+ def draw_self(self, ox: float, oy: float):
66
+ # color = Color(self.color.red, self.color.green, self.color.blue, self.alpha)
67
+ # self.engine.backend.fill_rect(self.px, self.py, WIDTH, HEIGHT, color)
68
+ # self.engine.backend.draw_partial_sprite(
69
+ # (self.px - ox) * TILE_WIDTH,
70
+ # (self.py - oy) * TILE_WIDTH,
71
+ # self.sprite.name,
72
+ # 0,
73
+ # 0,
74
+ # self.sprite.width,
75
+ # self.sprite.height,
76
+ # draw_to_filter=True,
77
+ # )
78
+ # print(f"Drawing light {self} of {self._follow} at {(self.px - ox + self._follow.extra_ox) * TILE_WIDTH,(self.py - oy + self._follow.extra_oy) * TILE_HEIGHT}")
79
+ self.engine.backend.fill_circle(
80
+ (self.px - ox + self._follow.extra_ox) * TILE_WIDTH,
81
+ (self.py - oy + self._follow.extra_oy) * TILE_HEIGHT,
82
+ self._sizes[self._size_idx] * 0.65, # 0.3125 *
83
+ DARK_GREY,
84
+ blend_mode=Blend.SUB,
85
+ draw_to_filter=True,
86
+ )
87
+ self.engine.backend.fill_circle(
88
+ (self.px - ox + self._follow.extra_ox) * TILE_WIDTH,
89
+ (self.py - oy + self._follow.extra_oy) * TILE_HEIGHT,
90
+ self._sizes[self._size_idx] * 0.5, # 0.3125 *
91
+ VERY_LIGHT_GREY,
92
+ blend_mode=Blend.SUB,
93
+ draw_to_filter=True,
94
+ )
95
+
96
+ def _prepare_light(self, max_size):
97
+
98
+ self._max_size = max_size
99
+ self._sizes = [max_size]
100
+ if not self._fixed_size:
101
+ self._sizes.extend(
102
+ [
103
+ int(max_size * 0.97),
104
+ int(max_size * 0.94),
105
+ int(max_size * 0.97),
106
+ ]
107
+ )
@@ -0,0 +1,38 @@
1
+ from ...types.direction import Direction
2
+ from ...types.graphic_state import GraphicState
3
+ from ..dynamic import Dynamic
4
+ from ..projectile import Projectile
5
+
6
+
7
+ class WalkingOnGrass(Projectile):
8
+ def __init__(self, follow: Dynamic):
9
+ super().__init__(follow.px, follow.py, 0, 0, 1.0, follow.alignment)
10
+ self.layer = 0
11
+ self.sprite.name = "simple_sheet"
12
+ self.sprite.ox = 32
13
+ self.sprite.oy = 10
14
+ self.sprite.num_frames = 2
15
+ self.sprite.timer = 0.2
16
+ self.sprite.timer_reset = 0.2
17
+ self._follow = follow
18
+ self.renew: bool = True
19
+ self.solid_vs_map = False
20
+
21
+ def update(self, elapsed_time: float, target: Dynamic = None):
22
+ if not self.renew:
23
+ self.kill()
24
+
25
+ self.px = self._follow.px
26
+ self.py = self._follow.py
27
+
28
+ if self._follow.graphic_state == GraphicState.STANDING:
29
+ elapsed_time = 0
30
+ self.sprite.update(elapsed_time, Direction.SOUTH, GraphicState.STANDING)
31
+
32
+ self.renew = False
33
+
34
+ def draw_self(self, ox: float, oy: float):
35
+ if self.sprite.name is None or self.sprite.name == "" or self.redundant:
36
+ return
37
+
38
+ self.sprite.draw_self(self.px - ox, self.py - oy)
@@ -0,0 +1,41 @@
1
+ from ...types.direction import Direction
2
+ from ...types.graphic_state import GraphicState
3
+ from ..dynamic import Dynamic
4
+ from .walking_on_grass import WalkingOnGrass
5
+
6
+
7
+ class WalkingOnWater(WalkingOnGrass):
8
+ def __init__(self, follow: Dynamic):
9
+ super().__init__(follow)
10
+ self.sprite.name = "simple_sheet"
11
+ self.sprite.ox = 32
12
+ self.sprite.oy = 9
13
+ self.sprite.num_frames = 2
14
+ self.sprite.timer = 0.2
15
+ self.sprite.timer_reset = 0.2
16
+ self._follow = follow
17
+ self.renew: bool = True
18
+ self.solid_vs_map = False
19
+
20
+ def update(self, elapsed_time: float, target: Dynamic = None):
21
+ if not self.renew:
22
+ self.kill()
23
+
24
+ self.px = (
25
+ self._follow.px + self._follow.sprite.width / 2 - self.sprite.width / 2
26
+ )
27
+ self.py = (
28
+ self._follow.py + self._follow.sprite.height / 2 - self.sprite.width / 2
29
+ )
30
+
31
+ if self._follow.graphic_state == GraphicState.STANDING:
32
+ elapsed_time = 0
33
+ self.sprite.update(elapsed_time, Direction.SOUTH, GraphicState.STANDING)
34
+
35
+ self.renew = False
36
+
37
+ def draw_self(self, ox: float, oy: float):
38
+ if self.sprite.name is None or self.sprite.name == "" or self.redundant:
39
+ return
40
+
41
+ self.sprite.draw_self(self.px - ox, self.py - oy)
mima/objects/loader.py ADDED
@@ -0,0 +1,103 @@
1
+ import logging
2
+
3
+ from ..types.object import ObjectType
4
+ from ..util.constants import TILE_HEIGHT, TILE_WIDTH
5
+ from .world.container import Container
6
+ from .world.floor_switch import FloorSwitch
7
+ from .world.gate import Gate
8
+ from .world.light_source import LightSource
9
+ from .world.logic_gate import LogicGate
10
+ from .world.movable import Movable
11
+ from .world.oneway import Oneway
12
+ from .world.color_switch import ColorSwitch
13
+ from .world.color_gate import ColorGate
14
+
15
+ # from .world.pickup import Pickup
16
+ from .world.switch import Switch
17
+ from .world.teleport import Teleport
18
+
19
+ LOG = logging.getLogger(__name__)
20
+
21
+
22
+ class ObjectLoader:
23
+ def __init__(self, object_dispatcher, creature_dispatcher):
24
+ self._base_dispatcher = {
25
+ "color_switch": ColorSwitch.load_from_tiled_object,
26
+ "color_gate": ColorGate.load_from_tiled_object,
27
+ "container": Container.load_from_tiled_object,
28
+ "floor_switch": FloorSwitch.load_from_tiled_object,
29
+ "gate": Gate.load_from_tiled_object,
30
+ "light_source": LightSource.load_from_tiled_object,
31
+ "logic_gate": LogicGate.load_from_tiled_object,
32
+ "movable": Movable.load_from_tiled_object,
33
+ "oneway": Oneway.load_from_tiled_object,
34
+ "switch": Switch.load_from_tiled_object,
35
+ "teleport": Teleport.load_from_tiled_object,
36
+ "creature": self.load_creature_from_tiled_object,
37
+ }
38
+ self._cstm_object_dispatcher = object_dispatcher
39
+ self._cstm_creature_dispatcher = creature_dispatcher
40
+ # self._current_map = None
41
+
42
+ def populate_dynamics(self, tilemap, dynamics):
43
+ # self._current_map = tilemap
44
+ for obj in tilemap.objects:
45
+ px = obj.px / TILE_WIDTH
46
+ py = obj.py / TILE_HEIGHT
47
+ width = max(obj.width / TILE_WIDTH, 1.0)
48
+ height = max(obj.height / TILE_HEIGHT, 1.0)
49
+
50
+ LOG.debug(
51
+ f"Loading object {obj.name} ({obj.object_id}): {obj.type}) "
52
+ f"at {px, py}."
53
+ )
54
+ if obj.type in self._cstm_object_dispatcher:
55
+ dispatcher = self._cstm_object_dispatcher
56
+ else:
57
+ dispatcher = self._base_dispatcher
58
+
59
+ try:
60
+ dynamics.extend(
61
+ dispatcher[obj.type](obj, px, py, width, height)
62
+ )
63
+ except (KeyError, ValueError):
64
+ LOG.exception(
65
+ f"Failed to load '{obj.name}' ({obj.object_id}: {obj.type})"
66
+ )
67
+ raise
68
+
69
+ # Connect listener IDs to actual listeners
70
+ for dyn in dynamics:
71
+ if dyn.type in [
72
+ ObjectType.SWITCH,
73
+ ObjectType.FLOOR_SWITCH,
74
+ ObjectType.LOGIC_GATE,
75
+ ]:
76
+ for listener_id in dyn.listener_ids:
77
+ for listener in dynamics:
78
+ if listener.dyn_id == listener_id:
79
+ dyn.listeners.append(listener)
80
+
81
+ if listener.type == ObjectType.LOGIC_GATE:
82
+ if listener.input_id1 == -1:
83
+ listener.input_id1 = dyn.dyn_id
84
+ elif listener.input_id2 == -1:
85
+ listener.input_id2 = dyn.dyn_id
86
+ else:
87
+ LOG.warning(
88
+ f"Object with ID {dyn.dyn_id} "
89
+ "has no free input port at "
90
+ f"Logic Gate {listener.dyn_id}"
91
+ )
92
+
93
+ def load_creature_from_tiled_object(self, obj, px, py, width, height):
94
+ creature_type = obj.get_string("creature_type")
95
+ try:
96
+ return self._cstm_creature_dispatcher[creature_type](
97
+ obj, px, py, width, height
98
+ )
99
+ except (KeyError, ValueError):
100
+ LOG.exception(
101
+ f"Failed to load '{obj.name}' ({obj.object_id}: {obj.type})"
102
+ )
103
+ raise
@@ -0,0 +1,86 @@
1
+ from ..types.alignment import Alignment
2
+ from ..types.damage import Damage
3
+ from ..types.object import ObjectType
4
+ from .dynamic import Dynamic
5
+
6
+
7
+ class Projectile(Dynamic):
8
+ def __init__(
9
+ self,
10
+ px: float,
11
+ py: float,
12
+ vx: float,
13
+ vy: float,
14
+ duration: float,
15
+ alignment: Alignment,
16
+ name: str = "Projectile",
17
+ ):
18
+ super().__init__(name, px, py)
19
+
20
+ self.type = ObjectType.PROJECTILE
21
+ self.vx = vx
22
+ self.vy = vy
23
+ self.pz = 0.5
24
+ self.duration = duration
25
+ self.alignment = alignment
26
+
27
+ self.solid_vs_dyn = False
28
+ self.solid_vs_map = True
29
+ self.is_projectile = True # redundant
30
+ self.attackable = False
31
+ self.one_hit = False
32
+ self.inherit_pos = False
33
+ self.gravity = False
34
+ self.change_solid_vs_map_timer = 0
35
+ self.dtype = Damage.BODY
36
+ self.damage = 0
37
+
38
+ def update(self, elapsed_time: float, target: Dynamic = None):
39
+ self.duration -= elapsed_time
40
+ if self.duration <= 0.0:
41
+ self.kill()
42
+
43
+ if self.change_solid_vs_map_timer > 0:
44
+ self.change_solid_vs_map_timer -= elapsed_time
45
+ if self.change_solid_vs_map_timer <= 0:
46
+ self.solid_vs_map = not self.solid_vs_map
47
+ self.change_solid_vs_map_timer = 0
48
+
49
+ self.speed = self.attributes.speed
50
+
51
+ self.sprite.update(
52
+ elapsed_time, self.facing_direction, self.graphic_state
53
+ )
54
+
55
+ def draw_self(self, ox: float, oy: float):
56
+ if (
57
+ self.sprite.name is None
58
+ or self.sprite.name == ""
59
+ or self.redundant
60
+ ):
61
+ return
62
+
63
+ self.sprite.draw_self(self.px - ox, self.py - oy)
64
+
65
+ def on_death(self) -> bool:
66
+ if self.spawn_on_death:
67
+ for do in self.spawn_on_death:
68
+ if do.type == ObjectType.PROJECTILE:
69
+ if self.inherit_pos:
70
+ do.px = self.px
71
+ do.py = self.py
72
+ self.engine.scene.add_projectile(do)
73
+ else:
74
+ self.engine.scene.add_dynamic(do)
75
+
76
+ return False
77
+
78
+ def cancel(self):
79
+ if self.spawn_on_death:
80
+ for do in self.spawn_on_death:
81
+ if do.type == ObjectType.PROJECTILE:
82
+ do.cancel() # Projectile will kill itself
83
+ else:
84
+ do.kill()
85
+ self.kill()
86
+ return True
mima/objects/sprite.py ADDED
@@ -0,0 +1,110 @@
1
+ from ..types.direction import Direction
2
+ from ..types.graphic_state import GraphicState
3
+ from ..util.constants import (
4
+ DEFAULT_GRAPHIC_TIMER,
5
+ DEFAULT_GRAPHIC_TIMER_DAMAGED,
6
+ DEFAULT_GRAPHIC_TIMER_DEAD,
7
+ DEFAULT_GRAPHIC_TIMER_STANDING,
8
+ DEFAULT_GRAPHIC_TIMER_WALKING,
9
+ DEFAULT_SPRITE_HEIGHT,
10
+ DEFAULT_SPRITE_WIDTH,
11
+ TILE_HEIGHT,
12
+ TILE_WIDTH,
13
+ )
14
+
15
+
16
+ class Sprite:
17
+ engine = None
18
+
19
+ def __init__(self, name: str = ""):
20
+ self.name = name
21
+ self.ox: int = 0
22
+ self.oy: int = 0
23
+ self.width: int = DEFAULT_SPRITE_WIDTH
24
+ self.height: int = DEFAULT_SPRITE_HEIGHT
25
+ self.num_frames: int = 1
26
+ self.frame_index: int = 0
27
+
28
+ self.timer: float = 0.25
29
+ self.timer_reset: float = 0.5
30
+
31
+ self.last_direction: Direction = Direction.SOUTH
32
+ self.last_graphic_state: GraphicState = GraphicState.STANDING
33
+
34
+ def update(
35
+ self,
36
+ elapsed_time: float,
37
+ direction: Direction = Direction.SOUTH,
38
+ graphic_state: GraphicState = GraphicState.STANDING,
39
+ ):
40
+ if (
41
+ direction == self.last_direction
42
+ and graphic_state == self.last_graphic_state
43
+ ):
44
+ # Nothing has changed, normal case
45
+ self.timer -= elapsed_time
46
+
47
+ if self.timer <= 0.0:
48
+ self.timer += self.timer_reset
49
+ self.frame_index = (self.frame_index + 1) % self.num_frames
50
+
51
+ else:
52
+ # Something changed
53
+ if graphic_state != self.last_graphic_state:
54
+ # State changed
55
+ if graphic_state == GraphicState.STANDING:
56
+ self.timer_reset = DEFAULT_GRAPHIC_TIMER_STANDING
57
+ elif graphic_state == GraphicState.WALKING:
58
+ self.timer_reset = DEFAULT_GRAPHIC_TIMER_WALKING
59
+ elif graphic_state == GraphicState.DAMAGED:
60
+ self.timer_reset = DEFAULT_GRAPHIC_TIMER_DAMAGED
61
+ elif graphic_state == GraphicState.DEAD:
62
+ self.timer_reset = DEFAULT_GRAPHIC_TIMER_DEAD
63
+ elif graphic_state == GraphicState.PUSHING:
64
+ self.timer_reset = DEFAULT_GRAPHIC_TIMER_WALKING
65
+ else:
66
+ self.timer_reset = DEFAULT_GRAPHIC_TIMER
67
+
68
+ # self.timer = self.timer_reset
69
+ # self.frame_index = 0
70
+
71
+ self.timer = self.timer_reset
72
+ self.frame_index = 0
73
+
74
+ self.last_direction = direction
75
+ self.last_graphic_state = graphic_state
76
+
77
+ def draw_self(self, px: float, py: float):
78
+ if self.name == "":
79
+ return
80
+
81
+ sheet_ox = sheet_oy = 0
82
+ state_value = self.last_graphic_state.value
83
+
84
+ if self.last_graphic_state == GraphicState.CELEBRATING:
85
+ sheet_ox = (self.ox + self.frame_index) * self.width
86
+ sheet_oy = (self.oy + state_value) * self.height
87
+ elif self.last_graphic_state == GraphicState.DEAD:
88
+ sheet_ox = (self.ox + 2 + self.frame_index) * self.width
89
+ sheet_oy = (self.oy + state_value) * self.height
90
+ else:
91
+ if self.last_graphic_state == GraphicState.PUSHING:
92
+ state_value -= 1
93
+ sheet_ox = (
94
+ self.ox + 2 * self.last_direction.value + self.frame_index
95
+ ) * self.width
96
+ sheet_oy = (self.oy + state_value) * self.height
97
+
98
+ self.engine.backend.draw_partial_sprite(
99
+ px * TILE_WIDTH,
100
+ py * TILE_HEIGHT,
101
+ self.name,
102
+ sheet_ox,
103
+ sheet_oy,
104
+ self.width,
105
+ self.height,
106
+ )
107
+
108
+ def reset(self):
109
+ self.frame_index = 0
110
+ self.timer = 0.0
File without changes
@@ -0,0 +1,68 @@
1
+ from typing import List, Optional, Union
2
+
3
+ from ...types.direction import Direction
4
+ from ...types.gate_color import GateColor
5
+ from ...types.graphic_state import GraphicState
6
+ from ...types.nature import Nature
7
+ from ...types.object import ObjectType
8
+ from ..dynamic import Dynamic
9
+ from .gate import Gate
10
+
11
+
12
+ class ColorGate(Gate):
13
+
14
+ def __init__(
15
+ self,
16
+ px: float,
17
+ py: float,
18
+ tileset_name: str,
19
+ image_name: str,
20
+ sprite_name: str,
21
+ graphic_state: GraphicState,
22
+ facing_direction: Direction,
23
+ color: GateColor,
24
+ dyn_id: int = -1,
25
+ name: str = "ColorGate",
26
+ ):
27
+ super().__init__(
28
+ px,
29
+ py,
30
+ tileset_name,
31
+ image_name,
32
+ sprite_name,
33
+ graphic_state,
34
+ facing_direction,
35
+ False,
36
+ dyn_id,
37
+ name,
38
+ )
39
+ self.color = color
40
+ self.type = ObjectType.COLOR_GATE
41
+
42
+ def update(self, elapsed_time, float, target: Optional[Dynamic] = None):
43
+ self.open = self.engine.gate_color == self.color
44
+ super().update(elapsed_time, target)
45
+
46
+ def on_interaction(self, target: Dynamic, nature: Nature):
47
+ return False
48
+
49
+ @staticmethod
50
+ def load_from_tiled_object(obj, px, py, width, height):
51
+ gate = ColorGate(
52
+ px=px,
53
+ py=py,
54
+ tileset_name=obj.get_string("tileset_name"),
55
+ image_name=obj.get_string("tileset_name"),
56
+ sprite_name=obj.get_string("sprite_name"),
57
+ graphic_state=GraphicState[
58
+ obj.get_string("graphic_state", "closed").upper()
59
+ ],
60
+ facing_direction=Direction[
61
+ obj.get_string("facing_direction", "south").upper()
62
+ ],
63
+ color=GateColor[obj.get_string("color", "red").upper()],
64
+ dyn_id=obj.object_id,
65
+ name=obj.name,
66
+ )
67
+
68
+ return [gate]
@@ -0,0 +1,105 @@
1
+ from typing import List, Optional, Union
2
+
3
+ from ...types.direction import Direction
4
+ from ...types.gate_color import GateColor
5
+ from ...types.graphic_state import GraphicState
6
+ from ...types.nature import Nature
7
+ from ...types.object import ObjectType
8
+ from ...util.constants import TILE_HEIGHT, TILE_WIDTH
9
+ from ..dynamic import Dynamic
10
+ from .switch import Switch
11
+
12
+
13
+ class ColorSwitch(Switch):
14
+ def __init__(
15
+ self,
16
+ px: float,
17
+ py: float,
18
+ tileset_name: str,
19
+ image_name: str,
20
+ sprite_name: str,
21
+ facing_direction: Direction,
22
+ graphic_state: GraphicState,
23
+ color: GateColor,
24
+ initial_signal=True,
25
+ dyn_id=-1,
26
+ name="ColorSwitch",
27
+ ):
28
+
29
+ super().__init__(
30
+ px,
31
+ py,
32
+ tileset_name,
33
+ image_name,
34
+ sprite_name,
35
+ facing_direction,
36
+ graphic_state,
37
+ initial_signal,
38
+ dyn_id,
39
+ name,
40
+ )
41
+ self.type = ObjectType.COLOR_SWITCH
42
+
43
+ self.color = color
44
+ self.graphic_state = (
45
+ GraphicState.CLOSED
46
+ if color == self.engine.gate_color
47
+ else GraphicState.OPEN
48
+ )
49
+ # self.signal = self.graphic_state == GraphicState.CLOSED
50
+ self.send_initial_signal = False
51
+
52
+ def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
53
+ self.graphic_state = (
54
+ GraphicState.CLOSED
55
+ if self.color == self.engine.gate_color
56
+ else GraphicState.OPEN
57
+ )
58
+ # self.signal = self.graphic_state == GraphicState.CLOSED
59
+ #
60
+ self.sprite.update(
61
+ elapsed_time, self.facing_direction, self.graphic_state
62
+ )
63
+
64
+ def on_interaction(self, target: Dynamic, nature: Nature):
65
+ if (
66
+ nature == Nature.TALK
67
+ and target.type == ObjectType.PLAYER
68
+ and self.visible
69
+ ):
70
+ if self.engine.gate_color != self.color:
71
+ self.engine.gate_color = self.color
72
+ else:
73
+ self.engine.gate_color = GateColor(
74
+ (self.color.value + 1) % self.engine.n_gate_colors
75
+ )
76
+ self.state_changed = True
77
+ self.engine.audio.play_sound("switch")
78
+ return True
79
+
80
+ return False
81
+
82
+ @staticmethod
83
+ def load_from_tiled_object(obj, px, py, width, height):
84
+ switch = ColorSwitch(
85
+ px=px,
86
+ py=py,
87
+ tileset_name=obj.get_string("tileset_name"),
88
+ image_name=obj.get_string("tileset_name"),
89
+ sprite_name=obj.get_string("sprite_name"),
90
+ graphic_state=GraphicState[
91
+ obj.get_string("graphic_state", "closed").upper()
92
+ ],
93
+ facing_direction=Direction[
94
+ obj.get_string("facing_direction", "south").upper()
95
+ ],
96
+ color=GateColor[obj.get_string("color", "red").upper()],
97
+ initial_signal=False,
98
+ dyn_id=obj.object_id,
99
+ name=obj.name,
100
+ )
101
+
102
+ switch.sprite.width = int(width * TILE_WIDTH)
103
+ switch.sprite.height = int(height * TILE_HEIGHT)
104
+
105
+ return [switch]