arkparser 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.
Files changed (46) hide show
  1. arkparser/__init__.py +117 -0
  2. arkparser/common/__init__.py +72 -0
  3. arkparser/common/binary_reader.py +402 -0
  4. arkparser/common/exceptions.py +99 -0
  5. arkparser/common/map_config.py +166 -0
  6. arkparser/common/types.py +249 -0
  7. arkparser/common/version_detection.py +195 -0
  8. arkparser/data_models.py +801 -0
  9. arkparser/export.py +485 -0
  10. arkparser/files/__init__.py +25 -0
  11. arkparser/files/base.py +309 -0
  12. arkparser/files/cloud_inventory.py +259 -0
  13. arkparser/files/profile.py +205 -0
  14. arkparser/files/tribe.py +155 -0
  15. arkparser/files/world_save.py +699 -0
  16. arkparser/game_objects/__init__.py +32 -0
  17. arkparser/game_objects/container.py +180 -0
  18. arkparser/game_objects/game_object.py +273 -0
  19. arkparser/game_objects/location.py +87 -0
  20. arkparser/models/__init__.py +29 -0
  21. arkparser/models/character.py +227 -0
  22. arkparser/models/creature.py +642 -0
  23. arkparser/models/item.py +207 -0
  24. arkparser/models/player.py +263 -0
  25. arkparser/models/stats.py +226 -0
  26. arkparser/models/structure.py +176 -0
  27. arkparser/models/tribe.py +291 -0
  28. arkparser/properties/__init__.py +77 -0
  29. arkparser/properties/base.py +329 -0
  30. arkparser/properties/byte_property.py +230 -0
  31. arkparser/properties/compound.py +1125 -0
  32. arkparser/properties/primitives.py +803 -0
  33. arkparser/properties/registry.py +236 -0
  34. arkparser/py.typed +0 -0
  35. arkparser/structs/__init__.py +60 -0
  36. arkparser/structs/base.py +63 -0
  37. arkparser/structs/colors.py +108 -0
  38. arkparser/structs/misc.py +133 -0
  39. arkparser/structs/property_list.py +101 -0
  40. arkparser/structs/registry.py +140 -0
  41. arkparser/structs/vectors.py +221 -0
  42. arkparser-0.1.0.dist-info/METADATA +833 -0
  43. arkparser-0.1.0.dist-info/RECORD +46 -0
  44. arkparser-0.1.0.dist-info/WHEEL +5 -0
  45. arkparser-0.1.0.dist-info/licenses/LICENSE +21 -0
  46. arkparser-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,207 @@
1
+ """
2
+ Item model class - Inventory items.
3
+
4
+ Wraps GameObject with intuitive attribute access for item data.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import typing as t
10
+ from dataclasses import dataclass, field
11
+
12
+
13
+ @dataclass
14
+ class Item:
15
+ """
16
+ An inventory item.
17
+
18
+ Wraps a GameObject representing an item with intuitive property access.
19
+
20
+ Attributes:
21
+ class_name: Blueprint class name.
22
+ name: Custom name (if renamed).
23
+ quantity: Stack quantity.
24
+ quality: Item quality/tier.
25
+ durability: Current durability.
26
+ is_blueprint: True if this is a blueprint.
27
+ is_engram: True if this is an engram.
28
+
29
+ Example:
30
+ >>> item = Item.from_game_object(obj)
31
+ >>> print(f"{item.class_name} x{item.quantity}")
32
+ >>> if item.is_blueprint:
33
+ ... print(" (Blueprint)")
34
+ """
35
+
36
+ _game_object: t.Any = field(default=None, repr=False)
37
+
38
+ @classmethod
39
+ def from_game_object(cls, game_object: t.Any) -> Item:
40
+ """
41
+ Create an Item from a GameObject.
42
+
43
+ Args:
44
+ game_object: The item's game object.
45
+
46
+ Returns:
47
+ An Item instance.
48
+ """
49
+ return cls(_game_object=game_object)
50
+
51
+ @property
52
+ def class_name(self) -> str:
53
+ """Blueprint class name."""
54
+ return self._game_object.class_name if self._game_object else ""
55
+
56
+ @property
57
+ def guid(self) -> str:
58
+ """Unique identifier (ASA only)."""
59
+ return self._game_object.guid if self._game_object else ""
60
+
61
+ @property
62
+ def name(self) -> str:
63
+ """Custom name (if renamed by player)."""
64
+ if not self._game_object:
65
+ return ""
66
+ return self._game_object.get_property_value("CustomItemName", default="") or ""
67
+
68
+ @property
69
+ def description(self) -> str:
70
+ """Custom description (if modified)."""
71
+ if not self._game_object:
72
+ return ""
73
+ return self._game_object.get_property_value("CustomItemDescription", default="") or ""
74
+
75
+ @property
76
+ def quantity(self) -> int:
77
+ """Stack quantity."""
78
+ if not self._game_object:
79
+ return 1
80
+ val = self._game_object.get_property_value("ItemQuantity", default=1)
81
+ return int(val) if val else 1
82
+
83
+ @property
84
+ def quality(self) -> float:
85
+ """Item quality rating."""
86
+ if not self._game_object:
87
+ return 0.0
88
+ val = self._game_object.get_property_value("ItemRating", default=0.0)
89
+ return float(val) if val else 0.0
90
+
91
+ @property
92
+ def quality_index(self) -> int:
93
+ """
94
+ Quality tier index.
95
+
96
+ 0 = Primitive, 1 = Ramshackle, 2 = Apprentice,
97
+ 3 = Journeyman, 4 = Mastercraft, 5 = Ascendant
98
+ """
99
+ if not self._game_object:
100
+ return 0
101
+ val = self._game_object.get_property_value("ItemQualityIndex", default=0)
102
+ return int(val) if val else 0
103
+
104
+ @property
105
+ def quality_name(self) -> str:
106
+ """Quality tier name."""
107
+ names = [
108
+ "Primitive",
109
+ "Ramshackle",
110
+ "Apprentice",
111
+ "Journeyman",
112
+ "Mastercraft",
113
+ "Ascendant",
114
+ ]
115
+ idx = self.quality_index
116
+ return names[idx] if 0 <= idx < len(names) else "Unknown"
117
+
118
+ @property
119
+ def durability(self) -> float:
120
+ """Current durability."""
121
+ if not self._game_object:
122
+ return 0.0
123
+ val = self._game_object.get_property_value("SavedDurability", default=0.0)
124
+ return float(val) if val else 0.0
125
+
126
+ @property
127
+ def is_blueprint(self) -> bool:
128
+ """True if this is a blueprint."""
129
+ if not self._game_object:
130
+ return False
131
+ return self._game_object.get_property_value("bIsBlueprint", default=False)
132
+
133
+ @property
134
+ def is_engram(self) -> bool:
135
+ """True if this is an engram."""
136
+ if not self._game_object:
137
+ return False
138
+ return self._game_object.get_property_value("bIsEngram", default=False)
139
+
140
+ @property
141
+ def is_equipped(self) -> bool:
142
+ """True if currently equipped."""
143
+ if not self._game_object:
144
+ return False
145
+ return self._game_object.get_property_value("bIsEquipped", default=False)
146
+
147
+ @property
148
+ def stat_values(self) -> list[int]:
149
+ """
150
+ Item stat values (for armor, weapons, etc.).
151
+
152
+ Returns:
153
+ List of stat modifier values.
154
+ """
155
+ if not self._game_object:
156
+ return []
157
+ values = []
158
+ for i in range(8):
159
+ val = self._game_object.get_property_value("ItemStatValues", index=i, default=0)
160
+ values.append(int(val) if val else 0)
161
+ return values
162
+
163
+ @property
164
+ def crafting_skill_bonus(self) -> float:
165
+ """Crafting skill bonus applied during crafting."""
166
+ if not self._game_object:
167
+ return 0.0
168
+ val = self._game_object.get_property_value("CraftingSkillBonusMultiplier", default=0.0)
169
+ return float(val) if val else 0.0
170
+
171
+ def get_property(self, name: str, index: int = 0, default: t.Any = None) -> t.Any:
172
+ """
173
+ Get a raw property value from the underlying game object.
174
+
175
+ Args:
176
+ name: Property name.
177
+ index: Array index for repeated properties.
178
+ default: Value to return if not found.
179
+
180
+ Returns:
181
+ The property value.
182
+ """
183
+ if self._game_object:
184
+ return self._game_object.get_property_value(name, default=default, index=index)
185
+ return default
186
+
187
+ def to_dict(self) -> dict[str, t.Any]:
188
+ """Convert to dictionary."""
189
+ result: dict[str, t.Any] = {
190
+ "class_name": self.class_name,
191
+ "guid": self.guid,
192
+ "quantity": self.quantity,
193
+ "quality": self.quality,
194
+ "quality_name": self.quality_name,
195
+ "is_blueprint": self.is_blueprint,
196
+ }
197
+ if self.name:
198
+ result["name"] = self.name
199
+ if self.durability:
200
+ result["durability"] = self.durability
201
+ return result
202
+
203
+ def __repr__(self) -> str:
204
+ name = self.name or self.class_name
205
+ if self.quantity > 1:
206
+ return f"Item({name!r}, quantity={self.quantity})"
207
+ return f"Item({name!r})"
@@ -0,0 +1,263 @@
1
+ """
2
+ Player model class - ARK player/profile data.
3
+
4
+ Wraps profile data with intuitive attribute access.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import typing as t
10
+ from dataclasses import dataclass, field
11
+
12
+ from .stats import CreatureStats, Location
13
+
14
+
15
+ @dataclass
16
+ class Player:
17
+ """
18
+ An ARK player character.
19
+
20
+ Wraps profile and character data with intuitive property access.
21
+
22
+ Attributes:
23
+ name: Player character name.
24
+ level: Character level.
25
+ experience: Experience points.
26
+ tribe_id: Tribe ID the player belongs to.
27
+ tribe_name: Name of the player's tribe.
28
+ stats: Player stat points.
29
+
30
+ Example:
31
+ >>> player = Player.from_game_object(obj, status_obj)
32
+ >>> print(f"{player.name} - Level {player.level}")
33
+ >>> print(f"Health: {player.stats.health}")
34
+ """
35
+
36
+ _game_object: t.Any = field(default=None, repr=False)
37
+ _status_object: t.Any = field(default=None, repr=False)
38
+
39
+ # Cached values
40
+ _stats: CreatureStats | None = field(default=None, repr=False)
41
+
42
+ @classmethod
43
+ def from_game_object(
44
+ cls,
45
+ game_object: t.Any,
46
+ status_object: t.Any = None,
47
+ ) -> Player:
48
+ """
49
+ Create a Player from a GameObject.
50
+
51
+ Args:
52
+ game_object: The player's main game object.
53
+ status_object: The player's status component (for stats).
54
+
55
+ Returns:
56
+ A Player instance.
57
+ """
58
+ return cls(_game_object=game_object, _status_object=status_object)
59
+
60
+ @property
61
+ def guid(self) -> str:
62
+ """Unique identifier (ASA only)."""
63
+ return self._game_object.guid if self._game_object else ""
64
+
65
+ @property
66
+ def player_id(self) -> int:
67
+ """Player data ID (unique per player)."""
68
+ if not self._game_object:
69
+ return 0
70
+ val = self._game_object.get_property_value("PlayerDataID", default=0)
71
+ return int(val) if val else 0
72
+
73
+ @property
74
+ def name(self) -> str:
75
+ """Player character name."""
76
+ if not self._game_object:
77
+ return ""
78
+ return self._game_object.get_property_value("PlayerName", default="") or ""
79
+
80
+ @property
81
+ def steam_name(self) -> str:
82
+ """Steam/platform username."""
83
+ if not self._game_object:
84
+ return ""
85
+ return self._game_object.get_property_value("PlatformProfileName", default="") or ""
86
+
87
+ @property
88
+ def tribe_id(self) -> int:
89
+ """Tribe ID the player belongs to."""
90
+ if not self._game_object:
91
+ return 0
92
+ val = self._game_object.get_property_value("TribeID", default=0)
93
+ return int(val) if val else 0
94
+
95
+ @property
96
+ def tribe_name(self) -> str:
97
+ """Name of the player's tribe."""
98
+ if not self._game_object:
99
+ return ""
100
+ return self._game_object.get_property_value("TribeName", default="") or ""
101
+
102
+ @property
103
+ def is_female(self) -> bool:
104
+ """True if the character is female."""
105
+ if not self._game_object:
106
+ return False
107
+ return self._game_object.get_property_value("bIsFemale", default=False)
108
+
109
+ @property
110
+ def gender(self) -> str:
111
+ """Gender as string ('Female' or 'Male')."""
112
+ return "Female" if self.is_female else "Male"
113
+
114
+ @property
115
+ def base_level(self) -> int:
116
+ """Base character level."""
117
+ if self._status_object:
118
+ return self._status_object.get_property_value("BaseCharacterLevel", default=1)
119
+ return 1
120
+
121
+ @property
122
+ def extra_level(self) -> int:
123
+ """Extra levels (ascension levels, etc.)."""
124
+ if self._status_object:
125
+ val = self._status_object.get_property_value("ExtraCharacterLevel", default=0)
126
+ return int(val) if val else 0
127
+ return 0
128
+
129
+ @property
130
+ def level(self) -> int:
131
+ """Total character level."""
132
+ return self.base_level + self.extra_level
133
+
134
+ @property
135
+ def experience(self) -> float:
136
+ """Current experience points."""
137
+ if self._status_object:
138
+ val = self._status_object.get_property_value("ExperiencePoints", default=0.0)
139
+ return float(val) if val else 0.0
140
+ return 0.0
141
+
142
+ @property
143
+ def stats(self) -> CreatureStats:
144
+ """
145
+ Player stat points.
146
+
147
+ Uses the same 12-stat system as creatures.
148
+ """
149
+ if self._stats is None:
150
+ points = []
151
+ if self._status_object:
152
+ for i in range(12):
153
+ val = self._status_object.get_property_value("NumberOfLevelUpPointsApplied", index=i, default=0)
154
+ points.append(int(val) if val else 0)
155
+ self._stats = CreatureStats.from_array(points)
156
+ return self._stats
157
+
158
+ @property
159
+ def engram_points(self) -> int:
160
+ """Total engram points available."""
161
+ if not self._game_object:
162
+ return 0
163
+ val = self._game_object.get_property_value("TotalEngramPoints", default=0)
164
+ return int(val) if val else 0
165
+
166
+ @property
167
+ def location(self) -> Location | None:
168
+ """World position and rotation."""
169
+ if self._game_object and self._game_object.location:
170
+ loc = self._game_object.location
171
+ return Location(
172
+ x=loc.x,
173
+ y=loc.y,
174
+ z=loc.z,
175
+ pitch=getattr(loc, "pitch", 0.0),
176
+ yaw=getattr(loc, "yaw", 0.0),
177
+ roll=getattr(loc, "roll", 0.0),
178
+ )
179
+ return None
180
+
181
+ @property
182
+ def last_server(self) -> str:
183
+ """Last server the player was on."""
184
+ if not self._game_object:
185
+ return ""
186
+ return self._game_object.get_property_value("LastServerSavedOn", default="") or ""
187
+
188
+ @property
189
+ def steam_id(self) -> str:
190
+ """Steam/platform unique ID (from UniqueID or file)."""
191
+ if not self._game_object:
192
+ return ""
193
+ val = self._game_object.get_property_value("UniqueID", default="")
194
+ if val:
195
+ return str(val)
196
+ return ""
197
+
198
+ @property
199
+ def data_file(self) -> str:
200
+ """Profile data file name (e.g., '2535445137750472.arkprofile')."""
201
+ sid = self.steam_id
202
+ if sid:
203
+ return f"{sid}.arkprofile"
204
+ pid = self.player_id
205
+ if pid:
206
+ return f"{pid}.arkprofile"
207
+ return ""
208
+
209
+ def get_property(self, name: str, index: int = 0, default: t.Any = None) -> t.Any:
210
+ """
211
+ Get a raw property value from the underlying game object.
212
+
213
+ Args:
214
+ name: Property name.
215
+ index: Array index for repeated properties.
216
+ default: Value to return if not found.
217
+
218
+ Returns:
219
+ The property value.
220
+ """
221
+ if self._game_object:
222
+ return self._game_object.get_property_value(name, default=default, index=index)
223
+ return default
224
+
225
+ def to_dict(self) -> dict[str, t.Any]:
226
+ """Convert to dictionary matching C# ASV_Players export format."""
227
+ result: dict[str, t.Any] = {
228
+ "playerid": self.player_id,
229
+ "steam": self.steam_name,
230
+ "name": self.name,
231
+ "tribeid": self.tribe_id,
232
+ "tribe": self.tribe_name,
233
+ "sex": self.gender,
234
+ "lvl": self.level,
235
+ # Flat stat fields matching C# export
236
+ "hp": self.stats.health,
237
+ "stam": self.stats.stamina,
238
+ "melee": self.stats.melee,
239
+ "weight": self.stats.weight,
240
+ "speed": self.stats.speed,
241
+ "food": self.stats.food,
242
+ "water": self.stats.water,
243
+ "oxy": self.stats.oxygen,
244
+ "craft": self.stats.crafting,
245
+ "fort": self.stats.fortitude,
246
+ "stats": self.stats.to_dict(),
247
+ "engram_points": self.engram_points,
248
+ }
249
+ if self.steam_id:
250
+ result["steamid"] = self.steam_id
251
+ if self.data_file:
252
+ result["dataFile"] = self.data_file
253
+ if self.location:
254
+ result["location"] = self.location.to_dict()
255
+ result["ccc"] = self.location.ccc
256
+ if self.location.latitude is not None:
257
+ result["lat"] = self.location.latitude
258
+ if self.location.longitude is not None:
259
+ result["lon"] = self.location.longitude
260
+ return result
261
+
262
+ def __repr__(self) -> str:
263
+ return f"Player({self.name!r}, level={self.level})"
@@ -0,0 +1,226 @@
1
+ """
2
+ Stats helper classes for creatures and players.
3
+
4
+ Provides named attribute access to the 12 ARK stat indices.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import typing as t
10
+ from dataclasses import dataclass, field
11
+
12
+ if t.TYPE_CHECKING:
13
+ from arkparser.common.map_config import MapConfig
14
+
15
+
16
+ @dataclass
17
+ class CreatureStats:
18
+ """
19
+ Creature stats with named attribute access.
20
+
21
+ ARK uses 12 stat indices. This class provides friendly access:
22
+ - Index 0: Health
23
+ - Index 1: Stamina
24
+ - Index 2: Torpidity
25
+ - Index 3: Oxygen
26
+ - Index 4: Food
27
+ - Index 5: Water
28
+ - Index 6: Temperature
29
+ - Index 7: Weight
30
+ - Index 8: Melee Damage
31
+ - Index 9: Movement Speed
32
+ - Index 10: Fortitude
33
+ - Index 11: Crafting Skill
34
+
35
+ Attributes:
36
+ health: Health stat points.
37
+ stamina: Stamina stat points.
38
+ torpidity: Torpidity stat points.
39
+ oxygen: Oxygen stat points.
40
+ food: Food stat points.
41
+ water: Water stat points.
42
+ temperature: Temperature stat points.
43
+ weight: Weight stat points.
44
+ melee: Melee damage stat points.
45
+ speed: Movement speed stat points.
46
+ fortitude: Fortitude stat points.
47
+ crafting: Crafting skill stat points.
48
+ """
49
+
50
+ health: int = 0
51
+ stamina: int = 0
52
+ torpidity: int = 0
53
+ oxygen: int = 0
54
+ food: int = 0
55
+ water: int = 0
56
+ temperature: int = 0
57
+ weight: int = 0
58
+ melee: int = 0
59
+ speed: int = 0
60
+ fortitude: int = 0
61
+ crafting: int = 0
62
+
63
+ @classmethod
64
+ def from_array(cls, points: list[int] | None) -> CreatureStats:
65
+ """
66
+ Create from an array of stat points.
67
+
68
+ Args:
69
+ points: Array of 12 stat point values (can be None or shorter).
70
+
71
+ Returns:
72
+ CreatureStats instance with the stat values.
73
+ """
74
+ if points is None:
75
+ return cls()
76
+
77
+ pts = list(points) + [0] * (12 - len(points)) if len(points) < 12 else points
78
+
79
+ return cls(
80
+ health=pts[0],
81
+ stamina=pts[1],
82
+ torpidity=pts[2],
83
+ oxygen=pts[3],
84
+ food=pts[4],
85
+ water=pts[5],
86
+ temperature=pts[6],
87
+ weight=pts[7],
88
+ melee=pts[8],
89
+ speed=pts[9],
90
+ fortitude=pts[10],
91
+ crafting=pts[11],
92
+ )
93
+
94
+ def to_array(self) -> list[int]:
95
+ """Convert to array format."""
96
+ return [
97
+ self.health,
98
+ self.stamina,
99
+ self.torpidity,
100
+ self.oxygen,
101
+ self.food,
102
+ self.water,
103
+ self.temperature,
104
+ self.weight,
105
+ self.melee,
106
+ self.speed,
107
+ self.fortitude,
108
+ self.crafting,
109
+ ]
110
+
111
+ @property
112
+ def total(self) -> int:
113
+ """Total stat points (excluding torpidity)."""
114
+ return self.health + self.stamina + self.oxygen + self.food + self.water + self.weight + self.melee + self.speed
115
+
116
+ def to_dict(self) -> dict[str, int]:
117
+ """Convert to dictionary."""
118
+ return {
119
+ "health": self.health,
120
+ "stamina": self.stamina,
121
+ "torpidity": self.torpidity,
122
+ "oxygen": self.oxygen,
123
+ "food": self.food,
124
+ "water": self.water,
125
+ "temperature": self.temperature,
126
+ "weight": self.weight,
127
+ "melee": self.melee,
128
+ "speed": self.speed,
129
+ "fortitude": self.fortitude,
130
+ "crafting": self.crafting,
131
+ }
132
+
133
+
134
+ @dataclass
135
+ class Location:
136
+ """
137
+ 3D position with optional rotation and GPS conversion.
138
+
139
+ Attributes:
140
+ x: X coordinate (UE world space).
141
+ y: Y coordinate (UE world space).
142
+ z: Z coordinate (UE world space).
143
+ pitch: Pitch rotation.
144
+ yaw: Yaw rotation.
145
+ roll: Roll rotation.
146
+ """
147
+
148
+ x: float = 0.0
149
+ y: float = 0.0
150
+ z: float = 0.0
151
+ pitch: float = 0.0
152
+ yaw: float = 0.0
153
+ roll: float = 0.0
154
+ _map_config: MapConfig | None = field(default=None, repr=False, compare=False)
155
+
156
+ def with_map(self, map_config: MapConfig) -> Location:
157
+ """
158
+ Return a new Location with a map config attached for GPS conversion.
159
+
160
+ Args:
161
+ map_config: The map configuration to use.
162
+
163
+ Returns:
164
+ A new Location with GPS conversion enabled.
165
+ """
166
+ return Location(
167
+ x=self.x,
168
+ y=self.y,
169
+ z=self.z,
170
+ pitch=self.pitch,
171
+ yaw=self.yaw,
172
+ roll=self.roll,
173
+ _map_config=map_config,
174
+ )
175
+
176
+ @property
177
+ def latitude(self) -> float | None:
178
+ """
179
+ Latitude coordinate (requires map configuration).
180
+
181
+ Set via ``with_map()`` or by passing map_config to the savegame parser.
182
+
183
+ Formula: lat_shift + (y / lat_div)
184
+ """
185
+ if self._map_config is None:
186
+ return None
187
+ return self._map_config.ue_to_lat(self.y)
188
+
189
+ @property
190
+ def longitude(self) -> float | None:
191
+ """
192
+ Longitude coordinate (requires map configuration).
193
+
194
+ Set via ``with_map()`` or by passing map_config to the savegame parser.
195
+
196
+ Formula: lon_shift + (x / lon_div)
197
+ """
198
+ if self._map_config is None:
199
+ return None
200
+ return self._map_config.ue_to_lon(self.x)
201
+
202
+ @property
203
+ def ccc(self) -> str:
204
+ """
205
+ CCC string (cheat setplayerpos format).
206
+
207
+ Returns:
208
+ Space-separated "x y z" coordinate string.
209
+ """
210
+ return f"{self.x} {self.y} {self.z}"
211
+
212
+ def to_dict(self) -> dict[str, float]:
213
+ """Convert to dictionary."""
214
+ result: dict[str, float] = {
215
+ "x": self.x,
216
+ "y": self.y,
217
+ "z": self.z,
218
+ "pitch": self.pitch,
219
+ "yaw": self.yaw,
220
+ "roll": self.roll,
221
+ }
222
+ if self.latitude is not None:
223
+ result["lat"] = self.latitude
224
+ if self.longitude is not None:
225
+ result["lon"] = self.longitude
226
+ return result