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
mima/engine.py ADDED
@@ -0,0 +1,197 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from abc import ABC, abstractmethod
5
+ from typing import Dict
6
+
7
+ from .backend.pygame_assets import PygameAssets
8
+ from .backend.pygame_audio import PygameAudio
9
+ from .backend.pygame_backend import PygameBackend
10
+ from .backend.pygame_events import PygameUserInput
11
+ from .maps.template import Template
12
+ from .maps.tilemap import Tilemap
13
+ from .objects.animated_sprite import AnimatedSprite
14
+ from .objects.creature import Creature
15
+ from .objects.dynamic import Dynamic
16
+ from .objects.sprite import Sprite
17
+ from .scripts import Command, ScriptProcessor
18
+ from .states.quest import Quest
19
+ from .types.mode import Mode
20
+ from .types.gate_color import GateColor
21
+ from .usables.item import Item
22
+ from .util import RuntimeConfig
23
+ from .util.logging import install_trace_logger
24
+ from .view.camera import Camera
25
+ from .view.scene import Scene
26
+
27
+ LOG = logging.getLogger(__name__)
28
+
29
+
30
+ class MimaEngine(ABC):
31
+ def __init__(
32
+ self, init_file: str, platform: str = "PC", caption: str = "MimaEngine"
33
+ ):
34
+ self.rtc = RuntimeConfig()
35
+ install_trace_logger()
36
+
37
+ self.backend: PygameBackend = PygameBackend(
38
+ self.rtc, init_file, platform
39
+ )
40
+
41
+ self._caption: str = caption
42
+ self.seconds_total: float = 0.0
43
+ self.app_fps: float = 0.0
44
+ self.game_fps: float = 0.0
45
+ self.elapsed_time: float = 0.00022
46
+ self._app_time: float = 0.0
47
+
48
+ self.mode: Mode = Mode.LOADING
49
+ self.gate_color: GateColor = GateColor.RED
50
+ self.n_gate_colors = 2
51
+ self.script: ScriptProcessor = None
52
+ self.player: Creature
53
+ self.quests: List[Quest] = []
54
+ self._items: Dict[str, Item] = {}
55
+
56
+ def construct(
57
+ self,
58
+ width: int,
59
+ height: int,
60
+ pixel_size: int,
61
+ fullscreen: bool = False,
62
+ target_fps: int = 60,
63
+ resizable: bool = False,
64
+ ):
65
+ """Initialize backend and create a window."""
66
+ AnimatedSprite.engine = self
67
+ Camera.engine = self
68
+ Command.engine = self
69
+ Dynamic.engine = self
70
+ PygameBackend.engine = self
71
+ Quest.engine = self
72
+ Scene.engine = self
73
+ ScriptProcessor.engine = self
74
+ Sprite.engine = self
75
+ Template.engine = self
76
+ Tilemap.engine = self
77
+ Item.engine = self
78
+
79
+ self.script = ScriptProcessor()
80
+ self.backend.init()
81
+ self.backend.construct(
82
+ width, height, pixel_size, fullscreen, target_fps, resizable
83
+ )
84
+ return True
85
+
86
+ def start(self):
87
+ """Start the main loop"""
88
+ app_frames = 0
89
+ game_frames = 0
90
+ app_seconds = 0.0
91
+ game_seconds = 0.0
92
+ app_frames_total = 0
93
+ game_frames_total = 0
94
+ self.seconds_total = 0.0
95
+
96
+ if self.on_user_create():
97
+ while self.backend.keep_running():
98
+ self.backend.set_caption(
99
+ f"{self._caption} ({self.game_fps:.2f}/{self.app_fps:.2f} fps)"
100
+ )
101
+ self.backend.process_events()
102
+
103
+ if not self.backend.keep_running():
104
+ break
105
+
106
+ if not self.on_user_update(self.elapsed_time):
107
+ print("Error in on_user_update")
108
+ break
109
+
110
+ self.backend.update_display()
111
+
112
+ self._app_time = self.backend.tick()
113
+ self.elapsed_time = min(self._app_time, 1.0 / 30.0)
114
+
115
+ app_seconds += self._app_time
116
+ game_seconds += self.elapsed_time
117
+ app_frames += 1
118
+ game_frames += 1
119
+
120
+ if game_seconds >= 1.0:
121
+ game_frames_total += game_frames
122
+ self.game_fps = game_frames
123
+ game_frames = 0
124
+ game_seconds -= 1.0
125
+ if app_seconds >= 1.0:
126
+ app_frames_total += app_frames
127
+ self.seconds_total += app_seconds
128
+ self.app_fps = app_frames
129
+ app_frames = 0
130
+ app_seconds -= 1.0
131
+
132
+ print(
133
+ f"App/Game Frames total: {app_frames_total}/{game_frames_total}"
134
+ )
135
+ print(f"Seconds total: {self.seconds_total:.3f}")
136
+ print(
137
+ f"Average App/Game FPS: {app_frames_total/self.seconds_total:.3f}/"
138
+ f"{game_frames_total/self.seconds_total:.3f}"
139
+ )
140
+
141
+ self.backend.shutdown()
142
+
143
+ @abstractmethod
144
+ def on_user_update(self, elapsed_time: float) -> bool:
145
+ """Update."""
146
+ raise NotImplementedError()
147
+
148
+ @abstractmethod
149
+ def on_user_create(self) -> bool:
150
+ raise NotImplementedError()
151
+
152
+ def on_user_terminate(self) -> bool:
153
+ self.backend.terminate = True
154
+ return True
155
+
156
+ @property
157
+ def assets(self) -> PygameAssets:
158
+ return self.backend.assets
159
+
160
+ @property
161
+ def audio(self) -> PygameAudio:
162
+ return self.backend.audio
163
+
164
+ @property
165
+ def keys(self) -> PygameUserInput:
166
+ return self.backend.user_input
167
+
168
+ def get_map(self, map_name: str):
169
+ return self.backend.assets.get_map(map_name)
170
+
171
+ def load_item(self, item: Item):
172
+ LOG.debug(f"Loading item {item.name}.")
173
+ self._items[item.name] = item
174
+
175
+ def get_item(self, item_id: str):
176
+ try:
177
+ return self._items[item_id]
178
+ except KeyError:
179
+ LOG.error(f"Item '{item_id}' is not defined!")
180
+ raise
181
+
182
+ def progress_quest(self, quest_name: str, new_state: int):
183
+ for quest in self.quests:
184
+ if quest.name == quest_name:
185
+ quest.state = new_state
186
+
187
+ def on_enter_background(self):
188
+ LOG.debug("About to enter background")
189
+
190
+ def on_entered_background(self):
191
+ LOG.debug("Entered background")
192
+
193
+ def on_enter_foreground(self):
194
+ LOG.debug("About to enter foreground")
195
+
196
+ def on_entered_foreground(self):
197
+ LOG.debug("Entered foreground")
mima/maps/__init__.py ADDED
File without changes
mima/maps/template.py ADDED
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Dict, Optional
4
+
5
+ from ..util.functions import strtobool
6
+ from ..util.property import Property
7
+
8
+ if TYPE_CHECKING:
9
+ from ..engine import MimaEngine
10
+
11
+
12
+ class Template:
13
+ engine: MimaEngine
14
+
15
+ def __init__(self, name: str):
16
+ self.name: str = name
17
+ self.properties: Dict[str, Property] = {}
18
+
19
+ def get_string(self, key: str, default_val: str = "") -> str:
20
+ if key in self.properties:
21
+ return self.properties[key].value
22
+ else:
23
+ return default_val
24
+
25
+ def get_int(self, key: str, default_val: int = 0) -> int:
26
+ if key in self.properties:
27
+ return int(self.properties[key].value)
28
+ else:
29
+ return default_val
30
+
31
+ def get_float(self, key: str, default_val: float = 0.0) -> float:
32
+ if key in self.properties:
33
+ return float(self.properties[key].value)
34
+ else:
35
+ return default_val
36
+
37
+ def get_bool(self, key: str, default_val: bool = False) -> bool:
38
+ if key in self.properties:
39
+ return bool(strtobool(self.properties[key].value))
40
+ else:
41
+ return default_val
mima/maps/tile.py ADDED
@@ -0,0 +1,20 @@
1
+ from ..types.direction import Direction
2
+ from ..types.graphic_state import GraphicState
3
+ from ..types.terrain import Terrain
4
+
5
+
6
+ class Tile:
7
+ def __init__(self):
8
+ self.basic_tile_id: int = 0
9
+ self.tile_id: int = 0
10
+ self.solid: bool = False
11
+ self.animated: bool = False
12
+ self.pz: float = 0.0
13
+ self.z_height: float = 0.0
14
+ self.terrain: Terrain = Terrain.DEFAULT
15
+ self.facing_direction: Direction = Direction.SOUTH
16
+ self.graphic_state: GraphicState = GraphicState.STANDING
17
+ self.sprite_name: str = ""
18
+
19
+ def update(self, elapsed_time: float) -> bool:
20
+ return True
@@ -0,0 +1,7 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class TileAnimation:
6
+ frame_id: int
7
+ duration: float
mima/maps/tile_info.py ADDED
@@ -0,0 +1,10 @@
1
+ from dataclasses import dataclass
2
+
3
+ from .tile import Tile
4
+ from .tileset import Tileset
5
+
6
+
7
+ @dataclass
8
+ class TileInfo:
9
+ tileset: Tileset
10
+ tile: Tile
@@ -0,0 +1,52 @@
1
+ import math
2
+ from typing import List
3
+
4
+
5
+ class TileLayer:
6
+ def __init__(self):
7
+ self.name: str = "Unnamed Layer"
8
+ self.layer_id: int = 0
9
+ self.width: int = 0
10
+ self.height: int = 0
11
+ self.layer_pos: int = 0
12
+ self.speed_x: float = 0.0
13
+ self.speed_y: float = 0.0
14
+ self.layer_ox: float = 0.0
15
+ self.layer_oy: float = 0.0
16
+ self.parallax_x: float = 0.0 # Not used yet
17
+ self.parallax_y: float = 0.0 # Not used yet
18
+
19
+ self.indices: List[int] = []
20
+
21
+ def update(self, elapsed_time: float):
22
+ self.layer_ox += self.speed_x * elapsed_time
23
+ self.layer_oy += self.speed_y * elapsed_time
24
+
25
+ if self.layer_ox > self.width:
26
+ self.layer_ox -= self.width
27
+ if self.layer_ox < 0:
28
+ self.layer_ox += self.width
29
+ if self.layer_oy > self.height:
30
+ self.layer_oy -= self.height
31
+ if self.layer_oy < 0:
32
+ self.layer_oy += self.height
33
+
34
+ def get_index(self, px: float, py: float) -> int:
35
+ if self.layer_ox != 0.0:
36
+ px = math.floor(px - self.layer_ox)
37
+ if px > self.width:
38
+ px -= self.width
39
+ while px < 0:
40
+ px += self.width
41
+
42
+ if self.layer_oy != 0.0:
43
+ py = math.floor(py - self.layer_oy)
44
+ if py > self.height:
45
+ py -= self.height
46
+ while py < 0:
47
+ py += self.height
48
+
49
+ if 0 <= px < self.width and 0 <= py < self.height:
50
+ return self.indices[py * self.width + px]
51
+ else:
52
+ return 0
File without changes
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ import csv
4
+ from io import StringIO
5
+ from typing import TYPE_CHECKING, List
6
+
7
+ from ..tile_layer import TileLayer
8
+
9
+ if TYPE_CHECKING:
10
+ from xml.etree.ElementTree import Element
11
+
12
+
13
+ class TiledLayer(TileLayer):
14
+ def __init__(self, l_xtree: Element):
15
+ super().__init__()
16
+
17
+ self.name: str = l_xtree.attrib["name"]
18
+ self.layer_id: int = int(l_xtree.attrib["id"])
19
+ self.type: str = l_xtree.attrib.get("class", "NormalLayer")
20
+ if "Foreground" in self.type:
21
+ # FIXME: Hack, propertytypes.json should be read and applied
22
+ self.layer_pos = 1
23
+ self.width: int = int(l_xtree.attrib["width"])
24
+ self.height: int = int(l_xtree.attrib["height"])
25
+ self.indices: List[int] = []
26
+
27
+ layer_data = l_xtree.findall("data")[0]
28
+ reader = csv.reader(StringIO(layer_data.text), delimiter=",")
29
+
30
+ for row in reader:
31
+ if len(row) <= 0:
32
+ continue
33
+
34
+ for entry in row:
35
+ try:
36
+ self.indices.append(int(entry))
37
+ except ValueError:
38
+ pass # Empty string
39
+
40
+ property_data = l_xtree.findall("properties")
41
+ for pdata in property_data:
42
+ properties = pdata.findall("property")
43
+
44
+ for prop in properties:
45
+ if prop.attrib["name"] == "layer":
46
+ self.layer_pos = int(prop.attrib["value"])
47
+ if prop.attrib["name"] == "speed":
48
+ self.speed_x = float(prop.attrib.get("value", 0.0))
@@ -0,0 +1,95 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os
5
+ from typing import List
6
+ from xml.etree import ElementTree
7
+
8
+ from ..tilemap import Tilemap
9
+ from ..tileset_info import TilesetInfo
10
+ from .tiled_layer import TiledLayer
11
+ from .tiled_objectgroup import TiledObjectgroup
12
+ from ...util.property import Property
13
+
14
+ # if TYPE_CHECKING:
15
+ # from ..engine import Avare
16
+
17
+
18
+ LOG = logging.getLogger(__name__)
19
+
20
+
21
+ class TiledMap(Tilemap):
22
+ def __init__(self, name: str, filename: str = ""):
23
+ super().__init__(name)
24
+
25
+ if filename == "":
26
+ filename = f"{name}.tmx"
27
+
28
+ if not os.path.isfile(filename):
29
+ filename = os.path.join(
30
+ self.engine.backend.data_path,
31
+ "maps",
32
+ os.path.split(filename)[-1],
33
+ )
34
+
35
+ self._objects: List[TiledObjectgroup] = []
36
+
37
+ if not os.path.isfile(filename):
38
+ filename = os.path.join(
39
+ self.engine.backend.data_path, "maps", filename
40
+ )
41
+
42
+ LOG.info("Loading map %s from TMX file '%s' ...", name, filename)
43
+ tree = ElementTree.parse(filename)
44
+ LOG.debug("Loaded file %s successfully.", filename)
45
+ root = tree.getroot()
46
+
47
+ LOG.debug("Loading map properties ...")
48
+ self.width = int(root.attrib["width"])
49
+ self.height = int(root.attrib["height"])
50
+ self.tile_width = int(root.attrib["tilewidth"])
51
+ self.tile_height = int(root.attrib["tileheight"])
52
+
53
+ LOG.debug("Loading properties ...")
54
+ properties = root.findall("properties")
55
+ if properties:
56
+ properties = properties[0].findall("property")
57
+ for p in properties:
58
+ pname = p.attrib["name"]
59
+ self.properties[pname] = Property(
60
+ name=pname,
61
+ dtype=p.attrib.get("type", "str"),
62
+ value=p.attrib["value"]
63
+ )
64
+
65
+ LOG.debug("Loading tilesets ...")
66
+ tilesets = root.findall("tileset") # Only one tileset
67
+ for tileset in tilesets:
68
+ tname = os.path.split(tileset.attrib["source"])[-1][:-4]
69
+ first_gid = int(tileset.attrib["firstgid"])
70
+ self._tilesets.append(
71
+ TilesetInfo(
72
+ tileset=self.engine.assets.get_tileset(tname),
73
+ first_gid=first_gid,
74
+ )
75
+ )
76
+
77
+ LOG.debug("Loading layers ...")
78
+ layers = root.findall("layer")
79
+ for layer in layers:
80
+ self._layers.append(TiledLayer(layer))
81
+
82
+ LOG.debug("Loading objects ...")
83
+ objectgroups = root.findall("objectgroup")
84
+ for objectgroup in objectgroups:
85
+ self._objects.append(TiledObjectgroup(objectgroup))
86
+
87
+ LOG.info("Map %s successfully loaded.", self.name)
88
+
89
+ @property
90
+ def objects(self):
91
+ all_objects = []
92
+ for group in self._objects:
93
+ all_objects += group.objects
94
+
95
+ return all_objects
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import TYPE_CHECKING
5
+
6
+ from ...util.property import Property
7
+ from ..template import Template
8
+
9
+ if TYPE_CHECKING:
10
+ from xml.etree.ElementTree import Element
11
+
12
+ from .tiled_template import TiledTemplate
13
+ from .tiled_tileset import TiledTileset
14
+
15
+
16
+ class TiledObject(Template):
17
+ def __init__(self, o_xtree: Element):
18
+ super().__init__("TiledObject")
19
+ self.object_id: int = int(o_xtree.attrib["id"])
20
+
21
+ self.name: str = o_xtree.attrib.get("name", "Unnamed")
22
+ self.type: str = o_xtree.attrib.get(
23
+ "type", o_xtree.attrib.get("class", "Untyped")
24
+ )
25
+ self.px: float = float(o_xtree.attrib["x"])
26
+ self.py: float = float(o_xtree.attrib["y"])
27
+
28
+ tsource = o_xtree.attrib.get("template", None)
29
+ if tsource is not None:
30
+ tname = os.path.split(tsource)[-1].split(".")[0]
31
+ tpl: TiledTemplate = self.engine.assets.get_template(tname)
32
+
33
+ self.name = tpl.oname
34
+ self.type = tpl.otype
35
+ self.width = tpl.width
36
+ self.height = tpl.height
37
+ # Templates' y positions are bottom instead of top
38
+ self.py -= self.height
39
+
40
+ for key, prop in tpl.properties.items():
41
+ self.properties[key] = Property(
42
+ name=prop.name, dtype=prop.dtype, value=prop.value
43
+ )
44
+
45
+ ts: TiledTileset = self.engine.assets.get_tileset(tpl.tileset_name)
46
+ self.properties["tileset_name"] = Property(
47
+ name="tileset_name", dtype="str", value=tpl.tileset_name
48
+ )
49
+ self.properties["image_name"] = Property(
50
+ name="image_name", dtype="str", value=ts.sprite_name
51
+ )
52
+ self.properties["sprite_offset_x"] = Property(
53
+ name="sprite_offset_x",
54
+ dtype="int",
55
+ value=f"{(tpl.gid - tpl.first_gid) % ts.columns}",
56
+ )
57
+
58
+ self.properties["sprite_offset_y"] = Property(
59
+ name="sprite_offset_y",
60
+ dtype="int",
61
+ value=f"{(tpl.gid - tpl.first_gid) // ts.columns}",
62
+ )
63
+ else:
64
+ self.width = float(o_xtree.attrib["width"])
65
+ self.height = float(o_xtree.attrib["height"])
66
+
67
+ if self.type == "container":
68
+ self.engine.total_chests += 1
69
+ props = o_xtree.findall("properties")
70
+ if props:
71
+ props = props[0].findall("property")
72
+
73
+ for p in props:
74
+ pname = p.attrib["name"]
75
+ self.properties[pname] = Property(
76
+ name=pname,
77
+ dtype=p.attrib.get("type", "str"),
78
+ value=p.attrib["value"],
79
+ )
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List
4
+
5
+ from .tiled_object import TiledObject
6
+
7
+ if TYPE_CHECKING:
8
+ from xml.etree.ElementTree import Element
9
+
10
+
11
+ class TiledObjectgroup:
12
+ def __init__(self, o_xtree: Element):
13
+ self.name: str = o_xtree.attrib["name"]
14
+ self.layer_id: int = int(o_xtree.attrib["id"])
15
+
16
+ self._objects: List[TiledObject] = []
17
+
18
+ objects = o_xtree.findall("object")
19
+
20
+ for obj in objects:
21
+ self._objects.append(TiledObject(obj))
22
+
23
+ @property
24
+ def objects(self):
25
+ return self._objects
@@ -0,0 +1,49 @@
1
+ import logging
2
+ import os
3
+ from xml.etree import ElementTree
4
+
5
+ from ...util.property import Property
6
+ from ..template import Template
7
+
8
+ LOG = logging.getLogger(__name__)
9
+
10
+
11
+ class TiledTemplate(Template):
12
+ def __init__(self, name: str, filename: str):
13
+ super().__init__(name)
14
+
15
+ LOG.info(
16
+ "Loading template %s from TX file at '%s' ...",
17
+ name,
18
+ filename,
19
+ )
20
+
21
+ tree = ElementTree.parse(filename)
22
+ LOG.debug("Loaded file %s successfully.", filename)
23
+
24
+ root = tree.getroot()
25
+ tileset = root.findall("tileset")[0]
26
+ LOG.debug("Loading tileset properties ...")
27
+ self.first_gid = int(tileset.attrib["firstgid"])
28
+ self.tileset_name = os.path.split(tileset.attrib["source"])[-1].split(
29
+ "."
30
+ )[0]
31
+
32
+ obj = root.findall("object")[0]
33
+ self.oname = obj.attrib.get("name", "Unnamed")
34
+ self.otype = obj.attrib.get("type", obj.attrib.get("class", "Untyped"))
35
+ self.gid = int(obj.attrib["gid"])
36
+ self.width = int(obj.attrib["width"])
37
+ self.height = int(obj.attrib["height"])
38
+
39
+ props = obj.findall("properties")
40
+ if props:
41
+ props = props[0].findall("property")
42
+
43
+ for p in props:
44
+ pname = p.attrib["name"]
45
+ self.properties[pname] = Property(
46
+ name=pname,
47
+ dtype=p.attrib.get("type", "str"),
48
+ value=p.attrib["value"],
49
+ )