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.
- mima/__init__.py +4 -0
- mima/backend/__init__.py +1 -0
- mima/backend/pygame_assets.py +401 -0
- mima/backend/pygame_audio.py +78 -0
- mima/backend/pygame_backend.py +603 -0
- mima/backend/pygame_camera.py +63 -0
- mima/backend/pygame_events.py +695 -0
- mima/backend/touch_control_scheme_a.py +126 -0
- mima/backend/touch_control_scheme_b.py +132 -0
- mima/core/__init__.py +0 -0
- mima/core/collision.py +325 -0
- mima/core/database.py +58 -0
- mima/core/engine.py +367 -0
- mima/core/mode_engine.py +81 -0
- mima/core/scene_engine.py +81 -0
- mima/integrated/__init__.py +0 -0
- mima/integrated/entity.py +183 -0
- mima/integrated/layered_map.py +351 -0
- mima/integrated/sprite.py +156 -0
- mima/layered/__init__.py +0 -0
- mima/layered/assets.py +56 -0
- mima/layered/scene.py +415 -0
- mima/layered/shape.py +99 -0
- mima/layered/shaped_sprite.py +78 -0
- mima/layered/virtual_input.py +302 -0
- mima/maps/__init__.py +0 -0
- mima/maps/template.py +71 -0
- mima/maps/tile.py +20 -0
- mima/maps/tile_animation.py +7 -0
- mima/maps/tile_info.py +10 -0
- mima/maps/tile_layer.py +52 -0
- mima/maps/tiled/__init__.py +0 -0
- mima/maps/tiled/tiled_layer.py +48 -0
- mima/maps/tiled/tiled_map.py +95 -0
- mima/maps/tiled/tiled_object.py +79 -0
- mima/maps/tiled/tiled_objectgroup.py +25 -0
- mima/maps/tiled/tiled_template.py +49 -0
- mima/maps/tiled/tiled_tile.py +90 -0
- mima/maps/tiled/tiled_tileset.py +51 -0
- mima/maps/tilemap.py +216 -0
- mima/maps/tileset.py +39 -0
- mima/maps/tileset_info.py +9 -0
- mima/maps/transition_map.py +146 -0
- mima/objects/__init__.py +0 -0
- mima/objects/animated_sprite.py +217 -0
- mima/objects/attribute_effect.py +26 -0
- mima/objects/attributes.py +126 -0
- mima/objects/creature.py +384 -0
- mima/objects/dynamic.py +206 -0
- mima/objects/effects/__init__.py +0 -0
- mima/objects/effects/colorize_screen.py +60 -0
- mima/objects/effects/debug_box.py +133 -0
- mima/objects/effects/light.py +103 -0
- mima/objects/effects/show_sprite.py +50 -0
- mima/objects/effects/walking_on_grass.py +70 -0
- mima/objects/effects/walking_on_water.py +57 -0
- mima/objects/loader.py +111 -0
- mima/objects/projectile.py +111 -0
- mima/objects/sprite.py +116 -0
- mima/objects/world/__init__.py +0 -0
- mima/objects/world/color_gate.py +67 -0
- mima/objects/world/color_switch.py +101 -0
- mima/objects/world/container.py +175 -0
- mima/objects/world/floor_switch.py +109 -0
- mima/objects/world/gate.py +178 -0
- mima/objects/world/light_source.py +121 -0
- mima/objects/world/logic_gate.py +157 -0
- mima/objects/world/movable.py +399 -0
- mima/objects/world/oneway.py +195 -0
- mima/objects/world/pickup.py +157 -0
- mima/objects/world/switch.py +179 -0
- mima/objects/world/teleport.py +308 -0
- mima/py.typed +0 -0
- mima/scripts/__init__.py +2 -0
- mima/scripts/command.py +38 -0
- mima/scripts/commands/__init__.py +0 -0
- mima/scripts/commands/add_quest.py +19 -0
- mima/scripts/commands/change_map.py +34 -0
- mima/scripts/commands/close_dialog.py +9 -0
- mima/scripts/commands/equip_weapon.py +23 -0
- mima/scripts/commands/give_item.py +26 -0
- mima/scripts/commands/give_resource.py +51 -0
- mima/scripts/commands/move_map.py +152 -0
- mima/scripts/commands/move_to.py +49 -0
- mima/scripts/commands/oneway_move.py +58 -0
- mima/scripts/commands/parallel.py +66 -0
- mima/scripts/commands/play_sound.py +13 -0
- mima/scripts/commands/present_item.py +53 -0
- mima/scripts/commands/progress_quest.py +12 -0
- mima/scripts/commands/quit_game.py +8 -0
- mima/scripts/commands/save_game.py +14 -0
- mima/scripts/commands/screen_fade.py +83 -0
- mima/scripts/commands/serial.py +69 -0
- mima/scripts/commands/set_facing_direction.py +21 -0
- mima/scripts/commands/set_spawn_map.py +17 -0
- mima/scripts/commands/show_choices.py +52 -0
- mima/scripts/commands/show_dialog.py +118 -0
- mima/scripts/commands/take_coins.py +23 -0
- mima/scripts/script_processor.py +61 -0
- mima/standalone/__init__.py +0 -0
- mima/standalone/camera.py +153 -0
- mima/standalone/geometry.py +1318 -0
- mima/standalone/multicolumn_list.py +54 -0
- mima/standalone/pixel_font.py +84 -0
- mima/standalone/scripting.py +145 -0
- mima/standalone/spatial.py +186 -0
- mima/standalone/sprite.py +158 -0
- mima/standalone/tiled_map.py +1247 -0
- mima/standalone/transformed_view.py +433 -0
- mima/standalone/user_input.py +563 -0
- mima/states/__init__.py +0 -0
- mima/states/game_state.py +189 -0
- mima/states/memory.py +28 -0
- mima/states/quest.py +71 -0
- mima/types/__init__.py +0 -0
- mima/types/alignment.py +7 -0
- mima/types/blend.py +8 -0
- mima/types/damage.py +42 -0
- mima/types/direction.py +44 -0
- mima/types/gate_color.py +7 -0
- mima/types/graphic_state.py +23 -0
- mima/types/keys.py +64 -0
- mima/types/mode.py +9 -0
- mima/types/nature.py +12 -0
- mima/types/object.py +22 -0
- mima/types/player.py +9 -0
- mima/types/position.py +13 -0
- mima/types/start.py +7 -0
- mima/types/terrain.py +9 -0
- mima/types/tile_collision.py +11 -0
- mima/types/weapon_slot.py +6 -0
- mima/types/window.py +44 -0
- mima/usables/__init__.py +0 -0
- mima/usables/item.py +51 -0
- mima/usables/weapon.py +68 -0
- mima/util/__init__.py +1 -0
- mima/util/colors.py +50 -0
- mima/util/constants.py +55 -0
- mima/util/functions.py +38 -0
- mima/util/input_defaults.py +170 -0
- mima/util/logging.py +51 -0
- mima/util/property.py +8 -0
- mima/util/runtime_config.py +327 -0
- mima/util/trading_item.py +23 -0
- mima/view/__init__.py +0 -0
- mima/view/camera.py +192 -0
- mima/view/mima_mode.py +618 -0
- mima/view/mima_scene.py +231 -0
- mima/view/mima_view.py +12 -0
- mima/view/mima_window.py +244 -0
- mima_engine-0.4.0.dist-info/METADATA +47 -0
- mima_engine-0.4.0.dist-info/RECORD +153 -0
- mima_engine-0.4.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import math
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from ..types.direction import Direction
|
|
6
|
+
from ..types.graphic_state import GraphicState
|
|
7
|
+
|
|
8
|
+
LOG = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AnimatedSprite:
|
|
12
|
+
engine = None
|
|
13
|
+
sprite_sets = {}
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
# tileset_name: str,
|
|
18
|
+
# image_name: str,
|
|
19
|
+
sprite_name: str,
|
|
20
|
+
graphic_state: GraphicState = GraphicState.STANDING,
|
|
21
|
+
facing_direction: Direction = Direction.SOUTH,
|
|
22
|
+
):
|
|
23
|
+
|
|
24
|
+
self.name = sprite_name
|
|
25
|
+
self._last_direction: Direction = facing_direction
|
|
26
|
+
self._last_graphic_state: GraphicState = graphic_state
|
|
27
|
+
self.sprite_sheet = self.engine.assets.get_sprite_data(sprite_name)
|
|
28
|
+
|
|
29
|
+
if self.name:
|
|
30
|
+
data = self._get_data(
|
|
31
|
+
self._last_graphic_state, self._last_direction
|
|
32
|
+
)
|
|
33
|
+
self._frame_index: int = 0
|
|
34
|
+
self._timer: float = data["duration"][0]
|
|
35
|
+
self._ox = data["ox"][0]
|
|
36
|
+
self._oy = data["oy"][0]
|
|
37
|
+
self._image_name = data["image"][0]
|
|
38
|
+
self.width = data["width"][0]
|
|
39
|
+
self.height = data["height"][0]
|
|
40
|
+
else:
|
|
41
|
+
self.width = self.engine.rtc.tile_width
|
|
42
|
+
self.height = self.engine.rtc.tile_height
|
|
43
|
+
# # TODO: Handle ""
|
|
44
|
+
# if tileset_name and image_name and self._sprite_name:
|
|
45
|
+
# LOG.info(
|
|
46
|
+
# {
|
|
47
|
+
# "operation": "load sprite",
|
|
48
|
+
# "tileset": tileset_name,
|
|
49
|
+
# "image": image_name,
|
|
50
|
+
# "sprite": self._sprite_name,
|
|
51
|
+
# }
|
|
52
|
+
# )
|
|
53
|
+
# tileset = self.engine.assets.get_tileset(tileset_name)
|
|
54
|
+
|
|
55
|
+
# self.width = tileset.tile_width
|
|
56
|
+
# self.height = tileset.tile_height
|
|
57
|
+
|
|
58
|
+
# self._sprites: Dict[GraphicState, Dict[Direction, Dict[str, Any]]] = (
|
|
59
|
+
# self._load_sprites_from_tileset(tileset, self._sprite_name)
|
|
60
|
+
# )
|
|
61
|
+
|
|
62
|
+
# else:
|
|
63
|
+
# LOG.debug(
|
|
64
|
+
# "Sprite information uncomplete. Tileset=%s, Image=%s, Sprite="
|
|
65
|
+
# "%s. Will continue without sprite.",
|
|
66
|
+
# tileset_name,
|
|
67
|
+
# image_name,
|
|
68
|
+
# self._sprite_name,
|
|
69
|
+
# )
|
|
70
|
+
# self.name = self._tileset_name = self._sprite_name = ""
|
|
71
|
+
|
|
72
|
+
def update(
|
|
73
|
+
self,
|
|
74
|
+
elapsed_time: float,
|
|
75
|
+
direction: Direction = Direction.SOUTH,
|
|
76
|
+
graphic_state: GraphicState = GraphicState.STANDING,
|
|
77
|
+
):
|
|
78
|
+
if not self.name:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
data = self._get_data(graphic_state, direction)
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
direction == self._last_direction
|
|
85
|
+
and graphic_state == self._last_graphic_state
|
|
86
|
+
):
|
|
87
|
+
# No changes, normal case
|
|
88
|
+
self._timer -= elapsed_time
|
|
89
|
+
if self._timer <= 0.0:
|
|
90
|
+
self._frame_index = (self._frame_index + 1) % len(
|
|
91
|
+
data["duration"]
|
|
92
|
+
)
|
|
93
|
+
self._timer += data["duration"][self._frame_index]
|
|
94
|
+
self._ox = data["ox"][self._frame_index]
|
|
95
|
+
self._oy = data["oy"][self._frame_index]
|
|
96
|
+
self._image_name = data["image"][self._frame_index]
|
|
97
|
+
self.width = data["width"][self._frame_index]
|
|
98
|
+
self.height = data["height"][self._frame_index]
|
|
99
|
+
else:
|
|
100
|
+
self._frame_index = 0
|
|
101
|
+
# Something changed
|
|
102
|
+
# if graphic_state != self._last_graphic_state:
|
|
103
|
+
# State changed
|
|
104
|
+
|
|
105
|
+
self._timer = data["duration"][0]
|
|
106
|
+
self._ox = data["ox"][0]
|
|
107
|
+
self._oy = data["oy"][0]
|
|
108
|
+
self._image_name = data["image"][0]
|
|
109
|
+
self.width = data["width"][0]
|
|
110
|
+
self.height = data["height"][0]
|
|
111
|
+
|
|
112
|
+
self._last_direction = direction
|
|
113
|
+
self._last_graphic_state = graphic_state
|
|
114
|
+
|
|
115
|
+
def draw_self(
|
|
116
|
+
self,
|
|
117
|
+
px: float,
|
|
118
|
+
py: float,
|
|
119
|
+
camera_name: str = "display",
|
|
120
|
+
absolute_position: bool = False,
|
|
121
|
+
draw_to_ui: bool = False,
|
|
122
|
+
):
|
|
123
|
+
if not self.name:
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
if not absolute_position:
|
|
127
|
+
px *= self.engine.rtc.tile_width
|
|
128
|
+
py *= self.engine.rtc.tile_height
|
|
129
|
+
px, py = math.floor(px), math.floor(py)
|
|
130
|
+
|
|
131
|
+
self.engine.backend.draw_partial_sprite(
|
|
132
|
+
px,
|
|
133
|
+
py,
|
|
134
|
+
self._image_name,
|
|
135
|
+
self._ox * self.width,
|
|
136
|
+
self._oy * self.height,
|
|
137
|
+
self.width,
|
|
138
|
+
self.height,
|
|
139
|
+
camera_name,
|
|
140
|
+
draw_to_ui=draw_to_ui,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def _load_sprites_from_tileset(self, tileset, sprite_name):
|
|
144
|
+
if sprite_name in AnimatedSprite.sprite_sets:
|
|
145
|
+
# Caching
|
|
146
|
+
return AnimatedSprite.sprite_sets[sprite_name]
|
|
147
|
+
|
|
148
|
+
sprites = {}
|
|
149
|
+
|
|
150
|
+
for tile in tileset.tiles:
|
|
151
|
+
if tile.sprite_name != sprite_name:
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
if tile.animated:
|
|
155
|
+
data = {"duration": [], "ox": [], "oy": []}
|
|
156
|
+
for frame in tile._frames:
|
|
157
|
+
data["duration"].append(frame.duration)
|
|
158
|
+
data["ox"].append(frame.frame_id % tileset.columns)
|
|
159
|
+
data["oy"].append(frame.frame_id // tileset.columns)
|
|
160
|
+
else:
|
|
161
|
+
data = {
|
|
162
|
+
"duration": [1000],
|
|
163
|
+
"ox": [tile.tile_id % tileset.columns],
|
|
164
|
+
"oy": [tile.tile_id // tileset.columns],
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
sprites.setdefault(tile.graphic_state, {})
|
|
168
|
+
sprites[tile.graphic_state][tile.facing_direction] = data
|
|
169
|
+
LOG.debug(
|
|
170
|
+
{
|
|
171
|
+
"operation": "add frames",
|
|
172
|
+
"image": self.name,
|
|
173
|
+
"sprite": sprite_name,
|
|
174
|
+
"graphic_state": tile.graphic_state.name,
|
|
175
|
+
"direction": tile.facing_direction.name,
|
|
176
|
+
"frame_data": data,
|
|
177
|
+
}
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
AnimatedSprite.sprite_sets[sprite_name] = sprites
|
|
181
|
+
return sprites
|
|
182
|
+
# for tile in tileset.tiles
|
|
183
|
+
# Check non-animated tiles if necessary
|
|
184
|
+
|
|
185
|
+
def reset(self):
|
|
186
|
+
self._frame_index = 0
|
|
187
|
+
self._timer = 0.0
|
|
188
|
+
|
|
189
|
+
def _get_data(self, graphic_state, direction):
|
|
190
|
+
if graphic_state == GraphicState.DEFEATED:
|
|
191
|
+
graphic_state = graphic_state.DEAD
|
|
192
|
+
|
|
193
|
+
data = self.sprite_sheet.get(
|
|
194
|
+
graphic_state, self.sprite_sheet.get(GraphicState.STANDING, {})
|
|
195
|
+
)
|
|
196
|
+
data = data.get(direction, data.get(Direction.SOUTH, {}))
|
|
197
|
+
if not data:
|
|
198
|
+
try:
|
|
199
|
+
LOG.debug(
|
|
200
|
+
"Animation of sprite %s is empty for %s, %s ",
|
|
201
|
+
self.name,
|
|
202
|
+
graphic_state.name,
|
|
203
|
+
direction.name,
|
|
204
|
+
)
|
|
205
|
+
except Exception:
|
|
206
|
+
# print(graphic_state, direction)
|
|
207
|
+
LOG.exception(graphic_state, direction)
|
|
208
|
+
raise
|
|
209
|
+
data = {
|
|
210
|
+
"ox": [0],
|
|
211
|
+
"oy": [0],
|
|
212
|
+
"duration": [1.0],
|
|
213
|
+
"image": [""],
|
|
214
|
+
"width": [0],
|
|
215
|
+
"height": [0],
|
|
216
|
+
}
|
|
217
|
+
return data
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .attributes import Attributes
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Effect(Attributes):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__()
|
|
7
|
+
|
|
8
|
+
self.effect_id: str = ""
|
|
9
|
+
self.duration: float = 0.0
|
|
10
|
+
self.redundant: bool = False
|
|
11
|
+
self.health_cost: float = 0.0
|
|
12
|
+
self.magic_cost: float = 0.0
|
|
13
|
+
self.stamina_cost: float = 0.0
|
|
14
|
+
|
|
15
|
+
def update(self, elapsed_time: float):
|
|
16
|
+
self.duration -= self.elapsed_time
|
|
17
|
+
if self.duration < 0.0:
|
|
18
|
+
self.redundant = True
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def from_dict(data):
|
|
22
|
+
attr = Effect()
|
|
23
|
+
for key, val in data.items():
|
|
24
|
+
setattr(attr, key, val)
|
|
25
|
+
|
|
26
|
+
return attr
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from ..types.damage import Damage
|
|
4
|
+
from ..util.constants import ATTRIBUTE_TIMER
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Attributes:
|
|
8
|
+
"""A class holding all the attributes of an object.
|
|
9
|
+
|
|
10
|
+
Attributes
|
|
11
|
+
----------
|
|
12
|
+
|
|
13
|
+
health: float
|
|
14
|
+
The current health of the object.
|
|
15
|
+
health_max: float
|
|
16
|
+
The maximum health of the object.
|
|
17
|
+
speed: float
|
|
18
|
+
The current speed value of the object. If different run speeds
|
|
19
|
+
are not relevant for the object, this is the only value to
|
|
20
|
+
consider. Otherwise, this value might be changed by objects
|
|
21
|
+
itself to reflect different states.
|
|
22
|
+
speed_mod: float
|
|
23
|
+
A modification of the speed used by the game engine, e.g., when
|
|
24
|
+
walking in shallow water
|
|
25
|
+
walk_speed: float
|
|
26
|
+
The speed value when the objects moves slow, e.g., when it is
|
|
27
|
+
walking.
|
|
28
|
+
run_speed: float
|
|
29
|
+
The speed value when the object moves fast, e.g., when it is
|
|
30
|
+
running.
|
|
31
|
+
acceleration: float
|
|
32
|
+
The acceleration of the object when it starts to move. Ranges
|
|
33
|
+
between 0 and 1
|
|
34
|
+
friction: float
|
|
35
|
+
The friction of the object when it stops moving. Ranges between
|
|
36
|
+
0 and 1.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
self.health: float = 10.0
|
|
42
|
+
self.health_max: float = 10.0
|
|
43
|
+
self.health_per_second: float = 0.0
|
|
44
|
+
self.magic: float = 10.0
|
|
45
|
+
self.magic_max: float = 10.0
|
|
46
|
+
self.magic_per_second: float = 0.1
|
|
47
|
+
self.stamina: float = 10.0
|
|
48
|
+
self.stamina_max: float = 10.0
|
|
49
|
+
self.stamina_per_second: float = 2.0
|
|
50
|
+
self.speed: float = 1.0
|
|
51
|
+
self.speed_mod: float = 1.0
|
|
52
|
+
self.walk_speed: float = 1.0
|
|
53
|
+
self.run_speed: float = 1.0
|
|
54
|
+
# self.current_speed: float = 0.0 # Acceleration and Friction
|
|
55
|
+
self.knock_speed: float = 5.0
|
|
56
|
+
self.acceleration: float = 15.0
|
|
57
|
+
self.friction: float = 15.0
|
|
58
|
+
|
|
59
|
+
self.timer: float = 0.25
|
|
60
|
+
self.gravity_vz: float = 40.0
|
|
61
|
+
self.light_radius: float = 32
|
|
62
|
+
|
|
63
|
+
self.coins: int = 0
|
|
64
|
+
self.coins_max: int = 100_000
|
|
65
|
+
self.keys: int = 0
|
|
66
|
+
self.keys_max: int = 100_000
|
|
67
|
+
self.bombs: int = 0
|
|
68
|
+
self.bombs_max: int = 5
|
|
69
|
+
self.arrows: int = 0
|
|
70
|
+
self.arrows_max: int = 15
|
|
71
|
+
|
|
72
|
+
self.body_damage: int = 0
|
|
73
|
+
|
|
74
|
+
self.strength: int = 0
|
|
75
|
+
self.dexterity: int = 0
|
|
76
|
+
self.intelligence: int = 0
|
|
77
|
+
self.wisdom: int = 0
|
|
78
|
+
self.initiative: int = 0
|
|
79
|
+
|
|
80
|
+
self.sell_factor: float = 1.0
|
|
81
|
+
self.buy_factor: float = 1.0
|
|
82
|
+
|
|
83
|
+
self.experience: int = 0
|
|
84
|
+
self.defense: Dict[Damage, int] = {dt: 0 for dt in Damage}
|
|
85
|
+
|
|
86
|
+
def update(self, elapsed_time: float):
|
|
87
|
+
self.timer -= elapsed_time
|
|
88
|
+
if self.timer <= 0.0:
|
|
89
|
+
self.timer += ATTRIBUTE_TIMER
|
|
90
|
+
|
|
91
|
+
self.health = min(
|
|
92
|
+
self.health + self.health_per_second * ATTRIBUTE_TIMER,
|
|
93
|
+
self.health_max,
|
|
94
|
+
)
|
|
95
|
+
self.magic = min(
|
|
96
|
+
self.magic + self.magic_per_second * ATTRIBUTE_TIMER,
|
|
97
|
+
self.magic_max,
|
|
98
|
+
)
|
|
99
|
+
self.stamina = min(
|
|
100
|
+
self.stamina + self.stamina_per_second * ATTRIBUTE_TIMER,
|
|
101
|
+
self.stamina_max,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def from_dict(data):
|
|
106
|
+
attr = Attributes()
|
|
107
|
+
|
|
108
|
+
for key, val in data.items():
|
|
109
|
+
if "defense" in key:
|
|
110
|
+
def_key = key.split("_", 1)[1]
|
|
111
|
+
attr.defense[def_key] = val
|
|
112
|
+
setattr(attr, key, val)
|
|
113
|
+
|
|
114
|
+
return attr
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def health_percent(self):
|
|
118
|
+
return self.health / self.health_max
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def magic_percent(self):
|
|
122
|
+
return self.magic / self.magic_max
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def stamina_percent(self):
|
|
126
|
+
return self.stamina / self.stamina_max
|