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.
- arkparser/__init__.py +117 -0
- arkparser/common/__init__.py +72 -0
- arkparser/common/binary_reader.py +402 -0
- arkparser/common/exceptions.py +99 -0
- arkparser/common/map_config.py +166 -0
- arkparser/common/types.py +249 -0
- arkparser/common/version_detection.py +195 -0
- arkparser/data_models.py +801 -0
- arkparser/export.py +485 -0
- arkparser/files/__init__.py +25 -0
- arkparser/files/base.py +309 -0
- arkparser/files/cloud_inventory.py +259 -0
- arkparser/files/profile.py +205 -0
- arkparser/files/tribe.py +155 -0
- arkparser/files/world_save.py +699 -0
- arkparser/game_objects/__init__.py +32 -0
- arkparser/game_objects/container.py +180 -0
- arkparser/game_objects/game_object.py +273 -0
- arkparser/game_objects/location.py +87 -0
- arkparser/models/__init__.py +29 -0
- arkparser/models/character.py +227 -0
- arkparser/models/creature.py +642 -0
- arkparser/models/item.py +207 -0
- arkparser/models/player.py +263 -0
- arkparser/models/stats.py +226 -0
- arkparser/models/structure.py +176 -0
- arkparser/models/tribe.py +291 -0
- arkparser/properties/__init__.py +77 -0
- arkparser/properties/base.py +329 -0
- arkparser/properties/byte_property.py +230 -0
- arkparser/properties/compound.py +1125 -0
- arkparser/properties/primitives.py +803 -0
- arkparser/properties/registry.py +236 -0
- arkparser/py.typed +0 -0
- arkparser/structs/__init__.py +60 -0
- arkparser/structs/base.py +63 -0
- arkparser/structs/colors.py +108 -0
- arkparser/structs/misc.py +133 -0
- arkparser/structs/property_list.py +101 -0
- arkparser/structs/registry.py +140 -0
- arkparser/structs/vectors.py +221 -0
- arkparser-0.1.0.dist-info/METADATA +833 -0
- arkparser-0.1.0.dist-info/RECORD +46 -0
- arkparser-0.1.0.dist-info/WHEEL +5 -0
- arkparser-0.1.0.dist-info/licenses/LICENSE +21 -0
- arkparser-0.1.0.dist-info/top_level.txt +1 -0
arkparser/models/item.py
ADDED
|
@@ -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
|