arkparser 0.3.2__tar.gz → 0.3.3__tar.gz
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-0.3.2 → arkparser-0.3.3}/PKG-INFO +2 -2
- {arkparser-0.3.2 → arkparser-0.3.3}/README.md +1 -1
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/__init__.py +1 -1
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/data_models.py +45 -10
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/export.py +44 -1
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser.egg-info/PKG-INFO +2 -2
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser.egg-info/SOURCES.txt +1 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/pyproject.toml +1 -1
- arkparser-0.3.3/tests/test_cryopod_export.py +122 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/LICENSE +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/common/__init__.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/common/binary_reader.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/common/exceptions.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/common/map_config.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/common/normalization.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/common/types.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/common/version_detection.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/files/__init__.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/files/base.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/files/cloud_inventory.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/files/profile.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/files/tribe.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/files/world_save.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/game_objects/__init__.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/game_objects/container.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/game_objects/game_object.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/game_objects/location.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/properties/__init__.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/properties/base.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/properties/byte_property.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/properties/compound.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/properties/primitives.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/properties/registry.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/structs/__init__.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/structs/base.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/structs/colors.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/structs/misc.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/structs/property_list.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/structs/registry.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser/structs/vectors.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser.egg-info/dependency_links.txt +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser.egg-info/requires.txt +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/arkparser.egg-info/top_level.txt +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/setup.cfg +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_asa_header_position.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_binary_reader.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_binary_reader_layouts.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_cloud_inventory.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_current_stats.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_data_models.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_export.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_game_objects.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_profile.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_tribe.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_v13_property_layouts.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_version_detection.py +0 -0
- {arkparser-0.3.2 → arkparser-0.3.3}/tests/test_world_save.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arkparser
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Pure Python parser for ARK: Survival Evolved and ARK: Survival Ascended save files
|
|
5
5
|
Author: Vertyco
|
|
6
6
|
License-Expression: MIT
|
|
@@ -210,7 +210,7 @@ The legacy ASVExport.exe emitted only the visible 8 stats (`hp`, `stam`, `melee`
|
|
|
210
210
|
| `hp-m` .. `fort-m` (all 12) | added | `NumberOfMutationsAppliedTamed[i]` (mutation point counts per stat) |
|
|
211
211
|
| `c0` .. `c5` | legacy | `ColorSetIndices[i]` |
|
|
212
212
|
| `mut-f`, `mut-m` | legacy | **Ancestor-line totals.** `RandomMutationsFemale` and `RandomMutationsMale` — single integers counting the total number of mutations that occurred down the maternal and paternal ancestry lines respectively. These are *not* per-stat — they share the `-m` token with the per-stat mutation block below but mean a different thing. Kept under the legacy names for ASVExport parity. |
|
|
213
|
-
| `cryo` | legacy | `
|
|
213
|
+
| `cryo` | legacy | `True` for creatures embedded inside cryopod / soultrap / vivarium / dinoball items in the world save, `False` for actor-in-world tames. `export_tamed` walks `WorldSave.iter_cryopod_creatures()` and emits one ASV_Tamed record per embedded creature in addition to the actor-in-world tames; on busy PvE servers cryopodded tames are the majority of the roster (e.g. 10,277 of 11,054 on a live Ragnarok_WP). Cluster-uploaded tames also surface here (via `export_cluster_uploads`) with `cryo=True`. |
|
|
214
214
|
| `ccc` | legacy | `"{x} {y} {z}"` from `LocationData` |
|
|
215
215
|
| `dinoid` | legacy | string form of `id` |
|
|
216
216
|
| `isMating` | legacy | `bEnableTamedMating` |
|
|
@@ -176,7 +176,7 @@ The legacy ASVExport.exe emitted only the visible 8 stats (`hp`, `stam`, `melee`
|
|
|
176
176
|
| `hp-m` .. `fort-m` (all 12) | added | `NumberOfMutationsAppliedTamed[i]` (mutation point counts per stat) |
|
|
177
177
|
| `c0` .. `c5` | legacy | `ColorSetIndices[i]` |
|
|
178
178
|
| `mut-f`, `mut-m` | legacy | **Ancestor-line totals.** `RandomMutationsFemale` and `RandomMutationsMale` — single integers counting the total number of mutations that occurred down the maternal and paternal ancestry lines respectively. These are *not* per-stat — they share the `-m` token with the per-stat mutation block below but mean a different thing. Kept under the legacy names for ASVExport parity. |
|
|
179
|
-
| `cryo` | legacy | `
|
|
179
|
+
| `cryo` | legacy | `True` for creatures embedded inside cryopod / soultrap / vivarium / dinoball items in the world save, `False` for actor-in-world tames. `export_tamed` walks `WorldSave.iter_cryopod_creatures()` and emits one ASV_Tamed record per embedded creature in addition to the actor-in-world tames; on busy PvE servers cryopodded tames are the majority of the roster (e.g. 10,277 of 11,054 on a live Ragnarok_WP). Cluster-uploaded tames also surface here (via `export_cluster_uploads`) with `cryo=True`. |
|
|
180
180
|
| `ccc` | legacy | `"{x} {y} {z}"` from `LocationData` |
|
|
181
181
|
| `dinoid` | legacy | string form of `id` |
|
|
182
182
|
| `isMating` | legacy | `bEnableTamedMating` |
|
|
@@ -547,20 +547,29 @@ class CryopodCreature:
|
|
|
547
547
|
]
|
|
548
548
|
|
|
549
549
|
if len(floats) >= 22:
|
|
550
|
-
#
|
|
551
|
-
#
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
#
|
|
555
|
-
|
|
550
|
+
# ASA cryopod blob: 11 current stats + 11 max stats + extras (36 floats).
|
|
551
|
+
# ASE cryopod blob: 12 current stats + 12 max stats + 1 extra (25 floats).
|
|
552
|
+
# Without the per-format count, the ASA loop walks 12 names against an
|
|
553
|
+
# 11-wide current block, leaking max[0] (which equals current[0] at full
|
|
554
|
+
# HP) into current_stats["CraftingSkill"] and surfacing hp=craft on every
|
|
555
|
+
# cryopod tame in exports.
|
|
556
|
+
if len(floats) >= 36:
|
|
557
|
+
current_count = 11
|
|
558
|
+
max_offset = 11
|
|
559
|
+
else:
|
|
560
|
+
current_count = 12
|
|
561
|
+
max_offset = 12
|
|
562
|
+
|
|
563
|
+
# Current stats: only consume the per-format slot count
|
|
564
|
+
for i in range(min(current_count, len(stat_names))):
|
|
556
565
|
if i < len(floats):
|
|
557
|
-
cryo.current_stats[
|
|
566
|
+
cryo.current_stats[stat_names[i]] = floats[i]
|
|
558
567
|
|
|
559
|
-
# Max stats
|
|
560
|
-
for i
|
|
568
|
+
# Max stats: same count, starting at offset
|
|
569
|
+
for i in range(min(current_count, len(stat_names))):
|
|
561
570
|
max_idx = i + max_offset
|
|
562
571
|
if max_idx < len(floats):
|
|
563
|
-
cryo.max_stats[
|
|
572
|
+
cryo.max_stats[stat_names[i]] = floats[max_idx]
|
|
564
573
|
|
|
565
574
|
# Parse soft class for blueprint reference
|
|
566
575
|
soft_classes = normalize_indexed_list(custom_data.get("CustomDataSoftClasses"))
|
|
@@ -569,6 +578,32 @@ class CryopodCreature:
|
|
|
569
578
|
if isinstance(first_class, dict):
|
|
570
579
|
cryo.class_name = first_class.get("name", cryo.class_name)
|
|
571
580
|
|
|
581
|
+
# Populate raw creature_props / status_props so the export
|
|
582
|
+
# pipeline's _SyntheticGameObject adapter (which calls
|
|
583
|
+
# ``get_property_value``) sees the fields it expects. ARK
|
|
584
|
+
# cryopod CustomItemDatas blobs only carry a small subset of
|
|
585
|
+
# the original creature properties (no DinoID, no TribeID, no
|
|
586
|
+
# tamer string, etc.) — surface what we have, leave the rest
|
|
587
|
+
# absent so consumers can detect the gap.
|
|
588
|
+
_stat_to_idx = {
|
|
589
|
+
"Health": 0, "Stamina": 1, "Torpidity": 2, "Oxygen": 3,
|
|
590
|
+
"Food": 4, "Water": 5, "Temperature": 6, "Weight": 7,
|
|
591
|
+
"MeleeDamage": 8, "MovementSpeed": 9, "Fortitude": 10,
|
|
592
|
+
"CraftingSkill": 11,
|
|
593
|
+
}
|
|
594
|
+
if cryo.name:
|
|
595
|
+
cryo.creature_props["TamedName"] = cryo.name
|
|
596
|
+
for i, color in enumerate(cryo.colors[:6]):
|
|
597
|
+
cryo.creature_props[f"ColorSetIndices_{i}" if i > 0 else "ColorSetIndices"] = color
|
|
598
|
+
if cryo.level:
|
|
599
|
+
cryo.status_props["BaseCharacterLevel"] = cryo.level
|
|
600
|
+
for stat_name, value in cryo.current_stats.items():
|
|
601
|
+
idx = _stat_to_idx.get(stat_name)
|
|
602
|
+
if idx is None:
|
|
603
|
+
continue
|
|
604
|
+
key = "CurrentStatusValues" if idx == 0 else f"CurrentStatusValues_{idx}"
|
|
605
|
+
cryo.status_props[key] = value
|
|
606
|
+
|
|
572
607
|
return cryo
|
|
573
608
|
|
|
574
609
|
except Exception:
|
|
@@ -771,12 +771,55 @@ def _tamed_dict(
|
|
|
771
771
|
|
|
772
772
|
|
|
773
773
|
def export_tamed(save: t.Any, map_config: MapConfig | None = None) -> list[dict[str, t.Any]]:
|
|
774
|
+
"""Emit ASV_Tamed records.
|
|
775
|
+
|
|
776
|
+
Pre-conditions: ``save`` exposes ``get_tamed_creatures()`` (in-world
|
|
777
|
+
tames) and ideally ``iter_cryopod_creatures()`` (creatures whose actor
|
|
778
|
+
has been removed from the world and re-embedded inside a cryopod /
|
|
779
|
+
soultrap / vivarium / dinoball item).
|
|
780
|
+
|
|
781
|
+
Post-conditions: returned list combines (a) every in-world tame and
|
|
782
|
+
(b) every cryopod-embedded tame, with the latter carrying ``cryo=True``
|
|
783
|
+
and inheriting the cryopod item's world location for GPS fields.
|
|
784
|
+
"""
|
|
774
785
|
objects = _world_objects(save, "get_tamed_creatures", "tamed_objects")
|
|
775
786
|
lookup = _save_lookup(save)
|
|
776
|
-
|
|
787
|
+
results: list[dict[str, t.Any]] = [
|
|
777
788
|
_tamed_dict(obj, _status_for(obj, lookup), lookup, map_config, save)
|
|
778
789
|
for obj in objects
|
|
779
790
|
]
|
|
791
|
+
results.extend(_export_world_cryopods(save, map_config))
|
|
792
|
+
return results
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
def _export_world_cryopods(
|
|
796
|
+
save: t.Any,
|
|
797
|
+
map_config: MapConfig | None,
|
|
798
|
+
) -> list[dict[str, t.Any]]:
|
|
799
|
+
"""Build ASV_Tamed records for cryopod-embedded creatures on the map.
|
|
800
|
+
|
|
801
|
+
ARK strips the actor for any creature stuffed into a cryopod / soultrap
|
|
802
|
+
/ vivarium / dinoball and serialises a snapshot into the item's
|
|
803
|
+
``CustomItemDatas``. ``get_tamed_creatures()`` therefore misses them
|
|
804
|
+
entirely. We walk ``iter_cryopod_creatures()`` (when available),
|
|
805
|
+
decode each embedded blob, and produce ``ASV_Tamed`` entries with
|
|
806
|
+
``cryo=True`` and the cryopod item's location.
|
|
807
|
+
"""
|
|
808
|
+
iter_cryos = getattr(save, "iter_cryopod_creatures", None)
|
|
809
|
+
if not callable(iter_cryos):
|
|
810
|
+
return []
|
|
811
|
+
out: list[dict[str, t.Any]] = []
|
|
812
|
+
empty_lookup: dict[t.Any, t.Any] = {}
|
|
813
|
+
for item_obj, cryo in iter_cryos():
|
|
814
|
+
actor, status = _cryo_props_to_synthetic(cryo)
|
|
815
|
+
# Inherit the cryopod's world location so GPS fields populate.
|
|
816
|
+
actor.location = getattr(item_obj, "location", None)
|
|
817
|
+
record = _tamed_dict(actor, status, empty_lookup, map_config, save)
|
|
818
|
+
# The synthetic actor carries no IsInCryo property; force the legacy
|
|
819
|
+
# flag so consumers can distinguish in-world tames from stored ones.
|
|
820
|
+
record["cryo"] = True
|
|
821
|
+
out.append(record)
|
|
822
|
+
return out
|
|
780
823
|
|
|
781
824
|
|
|
782
825
|
class _SyntheticGameObject:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arkparser
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Pure Python parser for ARK: Survival Evolved and ARK: Survival Ascended save files
|
|
5
5
|
Author: Vertyco
|
|
6
6
|
License-Expression: MIT
|
|
@@ -210,7 +210,7 @@ The legacy ASVExport.exe emitted only the visible 8 stats (`hp`, `stam`, `melee`
|
|
|
210
210
|
| `hp-m` .. `fort-m` (all 12) | added | `NumberOfMutationsAppliedTamed[i]` (mutation point counts per stat) |
|
|
211
211
|
| `c0` .. `c5` | legacy | `ColorSetIndices[i]` |
|
|
212
212
|
| `mut-f`, `mut-m` | legacy | **Ancestor-line totals.** `RandomMutationsFemale` and `RandomMutationsMale` — single integers counting the total number of mutations that occurred down the maternal and paternal ancestry lines respectively. These are *not* per-stat — they share the `-m` token with the per-stat mutation block below but mean a different thing. Kept under the legacy names for ASVExport parity. |
|
|
213
|
-
| `cryo` | legacy | `
|
|
213
|
+
| `cryo` | legacy | `True` for creatures embedded inside cryopod / soultrap / vivarium / dinoball items in the world save, `False` for actor-in-world tames. `export_tamed` walks `WorldSave.iter_cryopod_creatures()` and emits one ASV_Tamed record per embedded creature in addition to the actor-in-world tames; on busy PvE servers cryopodded tames are the majority of the roster (e.g. 10,277 of 11,054 on a live Ragnarok_WP). Cluster-uploaded tames also surface here (via `export_cluster_uploads`) with `cryo=True`. |
|
|
214
214
|
| `ccc` | legacy | `"{x} {y} {z}"` from `LocationData` |
|
|
215
215
|
| `dinoid` | legacy | string form of `id` |
|
|
216
216
|
| `isMating` | legacy | `bEnableTamedMating` |
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Tests pinning ASA cryopod creature decode + export integration.
|
|
2
|
+
|
|
3
|
+
ARK strips the actor for every cryopodded / soultrapped / vivariumed /
|
|
4
|
+
dinoballed creature and embeds a serialised snapshot into the item's
|
|
5
|
+
``CustomItemDatas``. ``WorldSave.iter_cryopod_creatures`` walks these
|
|
6
|
+
items, and ``export_tamed`` then surfaces them as ``ASV_Tamed`` entries
|
|
7
|
+
with ``cryo=True``. Two regressions tested here:
|
|
8
|
+
|
|
9
|
+
- ASA cryopod blobs carry 11 current stats (no CraftingSkill in the
|
|
10
|
+
current block), 11 max stats, and 14 extras (36 floats total). The
|
|
11
|
+
pre-0.3.3 parser walked 12 names against an 11-wide block, leaking
|
|
12
|
+
``max[0]`` (Health) into ``current_stats["CraftingSkill"]`` and
|
|
13
|
+
surfacing ``hp == craft`` on every cryopod tame.
|
|
14
|
+
- ``from_asa_cryopod_data`` did not populate ``creature_props`` /
|
|
15
|
+
``status_props``; the ``_SyntheticGameObject`` adapter the exporter
|
|
16
|
+
uses then saw empty dicts and emitted records with ``lvl=1``,
|
|
17
|
+
``name=""``, all-zero colors, and ``current_stats: null``.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from arkparser.data_models import CryopodCreature
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _asa_blob(current: list[float], max_: list[float]) -> dict[str, object]:
|
|
26
|
+
"""Synthesise the smallest ASA cryopod CustomItemDatas entry we can.
|
|
27
|
+
|
|
28
|
+
36 floats total: 11 current + 11 max + 14 extras (zeros).
|
|
29
|
+
"""
|
|
30
|
+
assert len(current) == 11
|
|
31
|
+
assert len(max_) == 11
|
|
32
|
+
floats = current + max_ + [0.0] * 14
|
|
33
|
+
return {
|
|
34
|
+
"CustomDataName": "Dino",
|
|
35
|
+
"CustomDataStrings": [
|
|
36
|
+
"Argent_Character_BP_C_2094073921", # [0] class_name
|
|
37
|
+
"dada - Lvl 259 (Argentavis)", # [1] display
|
|
38
|
+
"37,0,37,0,0,0,", # [2] colors
|
|
39
|
+
"", "", "", "", "", "", "Argentavis", # [9] species
|
|
40
|
+
],
|
|
41
|
+
"CustomDataFloats": floats,
|
|
42
|
+
"CustomDataNames": [],
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_asa_cryopod_eleven_current_stats_does_not_leak_max_into_craft() -> None:
|
|
47
|
+
"""ASA cryopod blob: current[0..10] then max[0..10]. craft must come
|
|
48
|
+
from current[11] — which does not exist, so it must stay 0.0, NOT pick
|
|
49
|
+
up max[0]."""
|
|
50
|
+
current = [6206.46, 2411.2, 0.0, 750.0, 13153.03, 100.0, 0.0, 63.6, 6.69, 0.09, 0.0]
|
|
51
|
+
max_ = [6206.46, 2411.2, 0.0, 750.0, 13153.03, 100.0, 0.0, 63.6, 6.69, 0.09, 0.0]
|
|
52
|
+
cryo = CryopodCreature.from_asa_cryopod_data(_asa_blob(current, max_))
|
|
53
|
+
assert cryo is not None
|
|
54
|
+
assert "Health" in cryo.current_stats
|
|
55
|
+
assert cryo.current_stats["Health"] == 6206.46
|
|
56
|
+
# CraftingSkill must NOT be in current_stats on ASA cryopods (only
|
|
57
|
+
# 11 slots are populated). Pre-0.3.3 set it equal to max[0]=Health.
|
|
58
|
+
assert "CraftingSkill" not in cryo.current_stats
|
|
59
|
+
# Max block round-trips properly
|
|
60
|
+
assert cryo.max_stats["Health"] == 6206.46
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_asa_cryopod_populates_creature_and_status_props() -> None:
|
|
64
|
+
"""The exporter adapter (``_SyntheticGameObject``) reads via
|
|
65
|
+
``get_property_value`` keyed by ARK property name + index suffix. The
|
|
66
|
+
ASA cryopod decoder must mirror those keys into ``creature_props`` /
|
|
67
|
+
``status_props`` so the synthetic adapter surfaces tamed-name, level,
|
|
68
|
+
colors, and CurrentStatusValues per stat index."""
|
|
69
|
+
current = [3000.0, 800.0, 0.0, 500.0, 5000.0, 100.0, 0.0, 200.0, 2.5, 0.05, 0.0]
|
|
70
|
+
max_ = [3000.0] * 11
|
|
71
|
+
cryo = CryopodCreature.from_asa_cryopod_data(_asa_blob(current, max_))
|
|
72
|
+
assert cryo is not None
|
|
73
|
+
|
|
74
|
+
# TamedName from display-name parse
|
|
75
|
+
assert cryo.creature_props.get("TamedName") == "dada"
|
|
76
|
+
# Level from display-name parse
|
|
77
|
+
assert cryo.status_props.get("BaseCharacterLevel") == 259
|
|
78
|
+
# Colors at expected indices (first non-zero is c0=37, then c2=37)
|
|
79
|
+
assert cryo.creature_props.get("ColorSetIndices") == 37
|
|
80
|
+
assert cryo.creature_props.get("ColorSetIndices_2") == 37
|
|
81
|
+
# CurrentStatusValues indexed by EPrimalCharacterStatusValue
|
|
82
|
+
# 0=Health, 1=Stamina, 3=Oxygen, 4=Food, 7=Weight, 8=MeleeDamage
|
|
83
|
+
assert cryo.status_props.get("CurrentStatusValues") == 3000.0 # health
|
|
84
|
+
assert cryo.status_props.get("CurrentStatusValues_1") == 800.0 # stam
|
|
85
|
+
assert cryo.status_props.get("CurrentStatusValues_4") == 5000.0 # food
|
|
86
|
+
assert cryo.status_props.get("CurrentStatusValues_7") == 200.0 # weight
|
|
87
|
+
assert cryo.status_props.get("CurrentStatusValues_8") == 2.5 # melee
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_asa_cryopod_display_name_parses_level_and_species() -> None:
|
|
91
|
+
"""Display-name format ``"Name - Lvl N (Species)"`` must yield all three."""
|
|
92
|
+
cryo = CryopodCreature.from_asa_cryopod_data(_asa_blob([0.0] * 11, [0.0] * 11))
|
|
93
|
+
assert cryo is not None
|
|
94
|
+
assert cryo.name == "dada"
|
|
95
|
+
assert cryo.level == 259
|
|
96
|
+
# Species comes from strings[9] when present, falling back to display parse
|
|
97
|
+
assert cryo.species == "Argentavis"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_ase_cryopod_keeps_twelve_stat_layout() -> None:
|
|
101
|
+
"""ASE cryopod blobs carry 12 current stats + 12 max stats (25 floats).
|
|
102
|
+
The ASA fix must not break the ASE path: walking all 12 names is the
|
|
103
|
+
correct behaviour when ``len(floats) < 36``."""
|
|
104
|
+
# 25 floats = 12 current + 12 max + 1 extra
|
|
105
|
+
floats = [float(i + 1) for i in range(12)] + [float(i + 100) for i in range(12)] + [0.0]
|
|
106
|
+
blob = {
|
|
107
|
+
"CustomDataName": "Dino",
|
|
108
|
+
"CustomDataStrings": [
|
|
109
|
+
"Raptor_Character_BP_C", "Bluey - Lvl 30 (Raptor)", "0,0,0,0,0,0,",
|
|
110
|
+
],
|
|
111
|
+
"CustomDataFloats": floats,
|
|
112
|
+
"CustomDataNames": [],
|
|
113
|
+
}
|
|
114
|
+
cryo = CryopodCreature.from_asa_cryopod_data(blob)
|
|
115
|
+
assert cryo is not None
|
|
116
|
+
# All 12 current and 12 max should be populated on ASE blobs
|
|
117
|
+
assert len(cryo.current_stats) == 12
|
|
118
|
+
assert len(cryo.max_stats) == 12
|
|
119
|
+
assert cryo.current_stats["Health"] == 1.0
|
|
120
|
+
assert cryo.current_stats["CraftingSkill"] == 12.0
|
|
121
|
+
assert cryo.max_stats["Health"] == 100.0
|
|
122
|
+
assert cryo.max_stats["CraftingSkill"] == 111.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|