arkparse 0.2.2__tar.gz → 0.2.4__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.
Files changed (170) hide show
  1. {arkparse-0.2.2 → arkparse-0.2.4}/.gitignore +1 -0
  2. {arkparse-0.2.2 → arkparse-0.2.4}/PKG-INFO +1 -1
  3. {arkparse-0.2.2 → arkparse-0.2.4}/pyproject.toml +1 -1
  4. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/base_api.py +29 -5
  5. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/dino_api.py +70 -68
  6. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/equipment_api.py +1 -1
  7. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/general_api.py +12 -5
  8. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/json_api.py +122 -22
  9. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/player_api.py +8 -2
  10. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/structure_api.py +50 -52
  11. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/ark_tribe.py +17 -1
  12. arkparse-0.2.4/src/arkparse/assets/RAGNAROK.PNG +0 -0
  13. arkparse-0.2.4/src/arkparse/assets/THE_ISLAND.PNG +0 -0
  14. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/classes/dinos.py +48 -7
  15. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/bases/base.py +3 -0
  16. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/dinos/dino.py +20 -5
  17. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/dinos/dino_ai_controller.py +1 -1
  18. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/dinos/stats.py +61 -0
  19. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/dinos/tamed_dino.py +38 -10
  20. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/__armor_defaults.py +3 -2
  21. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/__equipment_with_armor.py +1 -1
  22. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/__equipment_with_durability.py +1 -1
  23. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/misc/__parsed_object_base.py +11 -0
  24. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/misc/dino_owner.py +9 -5
  25. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/misc/inventory.py +0 -1
  26. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/structures/structure.py +1 -1
  27. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/structures/structure_with_inventory.py +11 -6
  28. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/ark_property.py +2 -3
  29. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_item_net_id.py +2 -2
  30. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_property_insertor.py +24 -1
  31. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/ark_property.py +3 -1
  32. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/actor_transform.py +12 -0
  33. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_item_net_id.py +2 -2
  34. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_my_persistent_buff_datas.py +9 -1
  35. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_rotator.py +12 -5
  36. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/player/ark_character_config.py +18 -0
  37. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/player/ark_character_stats.py +32 -0
  38. arkparse-0.2.4/src/arkparse/player/ark_persistent_buff_data.py +22 -0
  39. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/player/ark_player.py +34 -2
  40. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/saves/asa_save.py +11 -2
  41. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/saves/save_context.py +5 -0
  42. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/utils/heatmap_visualization.py +6 -4
  43. arkparse-0.2.2/src/arkparse/assets/RAGNAROK.PNG +0 -0
  44. arkparse-0.2.2/src/arkparse/player/ark_persistent_buff_data.py +0 -12
  45. {arkparse-0.2.2 → arkparse-0.2.4}/LICENSE +0 -0
  46. {arkparse-0.2.2 → arkparse-0.2.4}/README.md +0 -0
  47. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/__init__.py +0 -0
  48. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/__init__.py +0 -0
  49. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/rcon_api.py +0 -0
  50. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/api/stackable_api.py +0 -0
  51. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/ABERRATION.PNG +0 -0
  52. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/__init__.py +0 -0
  53. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/heavy_inventory +0 -0
  54. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/tamable_dinos.txt +0 -0
  55. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/armor +0 -0
  56. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/armor_bp +0 -0
  57. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/armor_bp_n.json +0 -0
  58. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/armor_n.json +0 -0
  59. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/saddle +0 -0
  60. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/saddle_bp +0 -0
  61. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/saddle_bp_n.json +0 -0
  62. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/saddle_n.json +0 -0
  63. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/shield +0 -0
  64. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/shield_bp +0 -0
  65. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/shield_bp_n.json +0 -0
  66. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/shield_n.json +0 -0
  67. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/weapon +0 -0
  68. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/weapon_bp +0 -0
  69. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/weapon_bp_n.json +0 -0
  70. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/equipment/weapon_n.json +0 -0
  71. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/inventory/with_item_n.json +0 -0
  72. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/stackable/stackable +0 -0
  73. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/assets/templates/stackable/stackable_n.json +0 -0
  74. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/classes/__init__.py +0 -0
  75. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/classes/consumables.py +0 -0
  76. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/classes/equipment.py +0 -0
  77. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/classes/placed_structures.py +0 -0
  78. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/classes/player.py +0 -0
  79. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/classes/resources.py +0 -0
  80. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/enums/__init__.py +0 -0
  81. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/enums/ark_dino_trait.py +0 -0
  82. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/enums/ark_enum.py +0 -0
  83. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/enums/ark_equipment_stat.py +0 -0
  84. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/enums/ark_item_quality.py +0 -0
  85. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/enums/ark_map.py +0 -0
  86. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/enums/ark_stat.py +0 -0
  87. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/ftp/__init__.py +0 -0
  88. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/ftp/ark_ftp_client.py +0 -0
  89. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/helpers/dino/is_wild_tamed.py +0 -0
  90. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/logging/__init__.py +0 -0
  91. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/logging/ark_save_logger.py +0 -0
  92. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/__init__.py +0 -0
  93. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/ark_game_object.py +0 -0
  94. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/cryopods/cryopod.py +0 -0
  95. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/dinos/__init__.py +0 -0
  96. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/dinos/baby.py +0 -0
  97. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/dinos/tamed_baby.py +0 -0
  98. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/__equipment.py +0 -0
  99. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/__init__.py +0 -0
  100. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/armor.py +0 -0
  101. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/saddle.py +0 -0
  102. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/shield.py +0 -0
  103. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/equipment/weapon.py +0 -0
  104. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/misc/inventory_item.py +0 -0
  105. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/misc/object_crafter.py +0 -0
  106. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/misc/object_owner.py +0 -0
  107. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/stackables/__init__.py +0 -0
  108. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/stackables/_stackable.py +0 -0
  109. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/stackables/ammo.py +0 -0
  110. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/stackables/resource.py +0 -0
  111. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/object_model/structures/__init__.py +0 -0
  112. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/__init__.py +0 -0
  113. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_base_value_parser.py +0 -0
  114. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_base_value_validator.py +0 -0
  115. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_binary_reader_base.py +0 -0
  116. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_byte_operator.py +0 -0
  117. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/__init__.py +0 -0
  118. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/_property_insertor.py +0 -0
  119. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/_property_parser.py +0 -0
  120. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/_property_replacer.py +0 -0
  121. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/ark_binary_parser.py +0 -0
  122. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/ark_value_type.py +0 -0
  123. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/__init__.py +0 -0
  124. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_dino_ancestor_entry.py +0 -0
  125. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_my_persistent_buff_datas.py +0 -0
  126. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_object_property.py +0 -0
  127. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_struct_type.py +0 -0
  128. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_tracked_actor_id_category_pair_with_bool.py +0 -0
  129. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_unique_net_id_repl.py +0 -0
  130. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_vector.py +0 -0
  131. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_legacy_parsing/struct/ark_vector_bool_pair.py +0 -0
  132. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_property_parser.py +0 -0
  133. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/_property_replacer.py +0 -0
  134. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/ark_archive.py +0 -0
  135. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/ark_binary_parser.py +0 -0
  136. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/ark_object.py +0 -0
  137. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/ark_property_container.py +0 -0
  138. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/ark_set.py +0 -0
  139. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/ark_value_type.py +0 -0
  140. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/array_property.py +0 -0
  141. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/game_object_reader_configuration.py +0 -0
  142. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/__init__.py +0 -0
  143. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_color.py +0 -0
  144. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_crafting_resource_requirement.py +0 -0
  145. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_custom_item_data.py +0 -0
  146. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_dino_ancestor_entry.py +0 -0
  147. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_gacha_resource_struct.py +0 -0
  148. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_gene_trait_struct.py +0 -0
  149. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_gigantoraptor_bonded_struct.py +0 -0
  150. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_int_point.py +0 -0
  151. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_key_value_pair.py +0 -0
  152. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_linear_color.py +0 -0
  153. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_object_property.py +0 -0
  154. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_player_death_reason.py +0 -0
  155. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_primal_saddle_structure.py +0 -0
  156. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_quat.py +0 -0
  157. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_server_custom_folder.py +0 -0
  158. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_struct_type.py +0 -0
  159. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_tracked_actor_id_category_pair_with_bool.py +0 -0
  160. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_unique_net_id_repl.py +0 -0
  161. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_vector.py +0 -0
  162. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/ark_vector_bool_pair.py +0 -0
  163. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/object_reference.py +0 -0
  164. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/parsing/struct/unknown_struct.py +0 -0
  165. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/saves/__init__.py +0 -0
  166. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/saves/header_location.py +0 -0
  167. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/utils/__init__.py +0 -0
  168. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/utils/import_file.py +0 -0
  169. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/utils/json_utils.py +0 -0
  170. {arkparse-0.2.2 → arkparse-0.2.4}/src/arkparse/utils/temp_files.py +0 -0
@@ -85,6 +85,7 @@ ipython_config.py
85
85
 
86
86
  **/binary-reader/**
87
87
  src/binary_reader/
88
+ tests/temp**
88
89
 
89
90
  # pyenv
90
91
  # For a library or package, you might want to ignore these files since the code is
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arkparse
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: A package to parse and modify ark save files
5
5
  Project-URL: Homepage, https://github.com/VincentHenauGithub/ark-save-parser
6
6
  Author-email: Vincent Henau <vincent.henau.github@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "arkparse"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  dependencies = [
9
9
  "pytz>=2025.2",
10
10
  "rcon>=2.4.9",
@@ -15,6 +15,7 @@ from arkparse.parsing.struct import ActorTransform
15
15
  from arkparse.parsing import ArkBinaryParser
16
16
  from arkparse.object_model import ArkGameObject
17
17
  from arkparse.utils import ImportFile
18
+ from arkparse.logging import ArkSaveLogger
18
19
 
19
20
  class BaseApi(StructureApi):
20
21
  def __init__(self, save, map: ArkMap):
@@ -33,8 +34,8 @@ class BaseApi(StructureApi):
33
34
  closest_dist = dist
34
35
 
35
36
  return closest
36
-
37
- def get_base_at(self, coords: MapCoords, radius: float = 0.3, owner_tribe_id = None) -> Base:
37
+
38
+ def get_base_at(self, coords: MapCoords, radius: float = 0.3, owner_tribe_id = None, keystone: Structure = None) -> Base:
38
39
  structures = self.get_at_location(self.map, coords, radius)
39
40
  if structures is None or len(structures) == 0:
40
41
  return None
@@ -50,10 +51,9 @@ class BaseApi(StructureApi):
50
51
  if owner_tribe_id is not None:
51
52
  all_structures = {k: v for k, v in all_structures.items() if v.owner.tribe_id == owner_tribe_id}
52
53
 
53
- keystone = self.__get_closest_to(all_structures, coords)
54
-
54
+ if keystone is None:
55
+ keystone = self.__get_closest_to(all_structures, coords)
55
56
  keystone_owner = keystone.owner if keystone is not None else None
56
-
57
57
  filtered_structures = {k: v for k, v in all_structures.items() if v.owner == keystone_owner}
58
58
 
59
59
  return Base(keystone.object.uuid, filtered_structures) if keystone is not None else None
@@ -168,6 +168,30 @@ class BaseApi(StructureApi):
168
168
  base.move_to(location, self.save)
169
169
 
170
170
  return base
171
+
172
+ def get_all_bases(self, only_connected: bool = False, radius: float = 0.3) -> List[Base]:
173
+ all_bases: List[Base] = []
174
+ all_structures: Dict[UUID, Structure] = super().get_all()
175
+ visited_structures: List[UUID] = []
176
+
177
+ for key, structure in all_structures.items():
178
+ base = None
179
+ if key in visited_structures:
180
+ continue
181
+
182
+ if only_connected:
183
+ connected = self.get_connected_structures({key: structure})
184
+ base = Base(structure.uuid, connected)
185
+ all_bases.append(base)
186
+ else:
187
+ base = self.get_base_at(structure.location.as_map_coords(self.map), radius, structure.owner.tribe_id, structure)
188
+
189
+ for structure in base.structures.values():
190
+ visited_structures.append(structure.uuid)
191
+
192
+ ArkSaveLogger.api_log(f"Parsed base at {'Unknown' if base.location is None else base.keystone.location.as_map_coords(self.map)} with {len(base.structures)} structures, owner: {base.owner}")
193
+
194
+ return all_bases
171
195
 
172
196
 
173
197
 
@@ -81,66 +81,69 @@ class DinoApi:
81
81
 
82
82
  ArkSaveLogger.api_log(f"Found {len(objects)} dinos, parsing them... (and retrieving inventories)")
83
83
  for key, obj in objects.items():
84
- dino = None
85
- if "Dinos/" in obj.blueprint and "_Character_" in obj.blueprint:
86
- is_tamed = obj.get_property_value("TamedTimeStamp") is not None
87
- is_baby = obj.get_property_value("bIsBaby", False)
88
84
 
89
- if obj.uuid in self.parsed_dinos:
90
- if is_tamed and include_tamed:
85
+ try:
86
+ dino = None
87
+ if "Dinos/" in obj.blueprint and "_Character_" in obj.blueprint:
88
+ is_tamed = obj.get_property_value("TamedTimeStamp") is not None
89
+ is_baby = obj.get_property_value("bIsBaby", False)
90
+
91
+ if obj.uuid in self.parsed_dinos:
92
+ if is_tamed and include_tamed:
93
+ if is_baby and include_babies:
94
+ dino = self.parsed_dinos[obj.uuid]
95
+ else:
96
+ dino = self.parsed_dinos[obj.uuid]
97
+ elif not is_tamed and include_wild:
98
+ if is_baby and include_babies:
99
+ dino = self.parsed_dinos[obj.uuid]
100
+ else:
101
+ dino = self.parsed_dinos[obj.uuid]
102
+ elif is_tamed and include_tamed:
91
103
  if is_baby and include_babies:
92
- dino = self.parsed_dinos[obj.uuid]
104
+ dino = TamedBaby(obj.uuid, save=self.save)
93
105
  else:
94
- dino = self.parsed_dinos[obj.uuid]
95
- elif not is_tamed and include_wild:
106
+ dino = TamedDino(obj.uuid, save=self.save)
107
+ self.parsed_dinos[obj.uuid] = dino
108
+ elif include_wild and not is_tamed:
96
109
  if is_baby and include_babies:
97
- dino = self.parsed_dinos[obj.uuid]
110
+ dino = Baby(obj.uuid, save=self.save)
98
111
  else:
99
- dino = self.parsed_dinos[obj.uuid]
100
- elif is_tamed and include_tamed:
101
- if is_baby and include_babies:
102
- dino = TamedBaby(obj.uuid, save=self.save)
103
- else:
104
- dino = TamedDino(obj.uuid, save=self.save)
105
- self.parsed_dinos[obj.uuid] = dino
106
- elif include_wild and not is_tamed:
107
- if is_baby and include_babies:
108
- dino = Baby(obj.uuid, save=self.save)
109
- else:
110
- dino = Dino(obj.uuid, save=self.save)
111
- self.parsed_dinos[obj.uuid] = dino
112
-
113
- elif "PrimalItem_WeaponEmptyCryopod_C" in obj.blueprint and include_cryos and include_tamed:
114
- if not obj.get_property_value("bIsEngram", default=False):
115
- if obj.uuid in self.parsed_cryopods:
116
- is_baby = self.parsed_cryopods[obj.uuid].dino is not None and isinstance(self.parsed_cryopods[obj.uuid].dino, TamedBaby)
117
- if is_baby and include_babies:
118
- dino = self.parsed_cryopods[obj.uuid].dino
112
+ dino = Dino(obj.uuid, save=self.save)
113
+ self.parsed_dinos[obj.uuid] = dino
114
+
115
+ elif "PrimalItem_WeaponEmptyCryopod_C" in obj.blueprint and include_cryos and include_tamed:
116
+ if not obj.get_property_value("bIsEngram", default=False):
117
+ if obj.uuid in self.parsed_cryopods:
118
+ is_baby = self.parsed_cryopods[obj.uuid].dino is not None and isinstance(self.parsed_cryopods[obj.uuid].dino, TamedBaby)
119
+ if is_baby and include_babies:
120
+ dino = self.parsed_cryopods[obj.uuid].dino
121
+ else:
122
+ dino = self.parsed_cryopods[obj.uuid].dino
119
123
  else:
120
- dino = self.parsed_cryopods[obj.uuid].dino
121
- else:
122
- try:
123
- cryopod = Cryopod(obj.uuid, save=self.save)
124
- self.parsed_cryopods[obj.uuid] = cryopod
125
- if cryopod.dino is not None:
126
- dino = cryopod.dino
127
- dino.is_cryopodded = True
128
- except Exception as e:
129
- if "Unsupported embedded data version" in str(e):
130
- ArkSaveLogger.warning_log(f"Skipping cryopod {obj.uuid} due to unsupported embedded data version (pre Unreal 5.5)")
131
- continue
132
- ArkSaveLogger.set_log_level(ArkSaveLogger.LogTypes.PARSER, True)
133
- parser = ArkBinaryParser(self.save.get_game_obj_binary(obj.uuid), self.save.save_context)
134
- cryopod = Cryopod(obj.uuid, parser)
135
- ArkSaveLogger.set_log_level(ArkSaveLogger.LogTypes.PARSER, False)
136
- ArkSaveLogger.error_log(f"Error parsing cryopod {obj.uuid}: {e}")
137
-
138
- if ArkSaveLogger._allow_invalid_objects:
139
- continue
140
- raise e
141
-
142
- if dino is not None:
143
- dinos[key] = dino
124
+ try:
125
+ cryopod = Cryopod(obj.uuid, save=self.save)
126
+ self.parsed_cryopods[obj.uuid] = cryopod
127
+ if cryopod.dino is not None:
128
+ dino = cryopod.dino
129
+ dino.is_cryopodded = True
130
+ except Exception as e:
131
+ if "Unsupported embedded data version" in str(e):
132
+ ArkSaveLogger.warning_log(f"Skipping cryopod {obj.uuid} due to unsupported embedded data version (pre Unreal 5.5)")
133
+ continue
134
+ ArkSaveLogger.set_log_level(ArkSaveLogger.LogTypes.PARSER, True)
135
+ cryopod = Cryopod(obj.uuid, save=self.save)
136
+ ArkSaveLogger.set_log_level(ArkSaveLogger.LogTypes.PARSER, False)
137
+ ArkSaveLogger.error_log(f"Error parsing cryopod {obj.uuid}: {e}")
138
+ raise e
139
+
140
+ if dino is not None:
141
+ dinos[key] = dino
142
+ except Exception as e:
143
+ if ArkSaveLogger._allow_invalid_objects:
144
+ ArkSaveLogger.error_log(f"Failed to parse dino {obj.uuid}: {e}")
145
+ else:
146
+ raise e
144
147
 
145
148
  ArkSaveLogger.api_log(f"Parsed {len(dinos)} dinos")
146
149
 
@@ -391,15 +394,15 @@ class DinoApi:
391
394
  return np.array(heatmap)
392
395
 
393
396
 
394
- def get_best_dino_for_stat(self, classes: List[str] = None, stat: ArkStat = None, only_tamed: bool = False, only_untamed: bool = False, base_stat: bool = False, mutated_stat=False) -> (Dino, int, ArkStat):
397
+ def get_best_dino_for_stat(self, classes: List[str] = None, stat: ArkStat = None, only_tamed: bool = False, only_untamed: bool = False, base_stat: bool = False, mutated_stat=False, level_upper_bound=None) -> (Dino, int, ArkStat):
395
398
  if only_tamed and only_untamed:
396
399
  raise ValueError("Cannot specify both only_tamed and only_untamed")
397
400
 
398
401
  if mutated_stat and base_stat:
399
402
  raise ValueError("Cannot specify both base_stat and base_mutated_stat")
400
403
 
401
- if classes is not None:
402
- dinos = self.get_all_filtered(class_names=classes, include_cryopodded=True)
404
+ if classes is not None or level_upper_bound is not None:
405
+ dinos = self.get_all_filtered(class_names=classes, include_cryopodded=True, level_upper_bound=level_upper_bound)
403
406
  else:
404
407
  dinos = self.get_all()
405
408
 
@@ -450,8 +453,8 @@ class DinoApi:
450
453
  if file_path.name.endswith(".bin") or file_path.name.startswith("loc_"):
451
454
  out.append(ImportFile(str(file_path)))
452
455
  return out
453
-
454
- def import_dino(self, path: Path, location: ActorTransform = None) -> Dino:
456
+
457
+ def import_dino(self, path: Path, location: ActorTransform = None) -> Dino | TamedDino:
455
458
  uuid_translation_map = {}
456
459
 
457
460
  def replace_uuids(uuid_map: Dict[UUID, UUID], bytes_: bytes):
@@ -494,6 +497,7 @@ class DinoApi:
494
497
  ArkSaveLogger.api_log(f"Added inventory item {item.uuid} to DB")
495
498
 
496
499
  # Get inventory and add to DB
500
+ inventory = None
497
501
  for file in files:
498
502
  if file.type == "inv":
499
503
  new_uuid = uuid_translation_map[file.uuid]
@@ -515,8 +519,6 @@ class DinoApi:
515
519
  self.save.add_obj_to_db(new_uuid, parser.byte_buffer)
516
520
  stats = DinoStats(uuid=new_uuid, save=self.save)
517
521
  stats.reidentify(new_uuid)
518
- stats.binary.replace_boolean(stats.object.find_property("bServerFirstInitialized"), False)
519
- self.save.modify_game_obj(stats.object.uuid, stats.binary.byte_buffer)
520
522
  ArkSaveLogger.api_log(f"Added dino stats {stats.uuid} to DB")
521
523
 
522
524
  # Get AI controller and add to DB
@@ -546,16 +548,16 @@ class DinoApi:
546
548
  if location is not None:
547
549
  dino.set_location(location)
548
550
 
549
- dino.binary.replace_boolean(dino.object.find_property("bServerInitializedDino"), False)
550
- # dino.binary.replace_boolean(dino.object.find_property("bSavedWhenStasised"), False)
551
- # dino.binary.replace_u32(dino.object.find_property("TamingTeamID"), 1466169314)
552
- # dino.binary.replace_u32(dino.object.find_property("TargetingTeam"), 1466169314)
553
- # dino.binary.replace_u32(dino.object.find_property("OwningPlayerID"), 290175622)
554
- dino.update_binary()
555
-
556
551
  ArkSaveLogger.api_log(f"Replacing name \"{dino.stats.object.names[1]}\" with \"{dino.object.names[0]}\"")
557
552
  dino.stats.replace_name_at_index_with(1, dino.object.names[0])
558
553
  dino.stats.update_binary()
554
+
555
+ if inventory is not None:
556
+ ArkSaveLogger.api_log(f"Replacing inventory name \"{inventory.object.names[1]}\" with \"{dino.object.names[0]}\"")
557
+ inventory.replace_name_at_index_with(1, dino.object.names[0])
558
+ inventory.update_binary()
559
+ dino: TamedDino = TamedDino(uuid=new_uuid, save=self.save)
560
+ return dino
559
561
 
560
562
  return dino
561
563
 
@@ -201,7 +201,7 @@ class EquipmentApi(GeneralApi):
201
201
  elif random_value > range_max*1.5:
202
202
  random_value = int(range_max*1.5)
203
203
 
204
- ArkSaveLogger.api_log(f"Setting {stat} to {random_value} for {equipment.class_name} {"(used normal distribution)" if normal_distribution else "(used uniform distribution)"}")
204
+ ArkSaveLogger.api_log(f"Setting {stat} to {random_value} for {equipment.class_name} {'(used normal distribution)' if normal_distribution else '(used uniform distribution)'}")
205
205
  equipment.set_stat(stat, equipment.get_actual_value(stat, random_value))
206
206
 
207
207
  equipment.auto_rate()
@@ -1,6 +1,7 @@
1
1
  from typing import Dict
2
2
  from uuid import UUID
3
3
 
4
+ from arkparse.logging.ark_save_logger import ArkSaveLogger
4
5
  from arkparse.object_model.ark_game_object import ArkGameObject
5
6
  from arkparse.parsing import ArkBinaryParser
6
7
  from arkparse.saves.asa_save import AsaSave
@@ -39,11 +40,17 @@ class GeneralApi:
39
40
  if valid_filter and not valid_filter(obj):
40
41
  continue
41
42
 
42
- if key in self.parsed_objects:
43
- parsed[key] = self.parsed_objects[key]
44
- else:
45
- parsed[key] = constructor(obj.uuid, self.save)
46
- self.parsed_objects[key] = parsed[key]
43
+ try:
44
+ if key in self.parsed_objects:
45
+ parsed[key] = self.parsed_objects[key]
46
+ else:
47
+ parsed[key] = constructor(obj.uuid, self.save)
48
+ self.parsed_objects[key] = parsed[key]
49
+ except Exception as e:
50
+ if ArkSaveLogger._allow_invalid_objects:
51
+ ArkSaveLogger.error_log(f"Failed to parse object {obj.uuid}: {e}")
52
+ else:
53
+ raise e
47
54
 
48
55
  return parsed
49
56
 
@@ -279,6 +279,81 @@ class JsonApi:
279
279
 
280
280
  ArkSaveLogger.api_log("Player pawns successfully exported.")
281
281
 
282
+ def export_players(self, player_api: PlayerApi = None, export_folder_path: str = Path.cwd() / "json_exports"):
283
+ ArkSaveLogger.api_log("Exporting players...")
284
+
285
+ # Get player API if not provided.
286
+ if player_api is None:
287
+ player_api = PlayerApi(self.save, self.ignore_error)
288
+
289
+ # Format players into JSON.
290
+ all_players = []
291
+ for player in player_api.players:
292
+ player_json_obj = player.to_json_obj()
293
+ found: bool = False
294
+ for p in player_api.pawns.values():
295
+ uniqueid = p.get_property_value("PlatformProfileID", None)
296
+ if uniqueid is not None and hasattr(uniqueid, "value") and player.unique_id == uniqueid.value:
297
+ found = True
298
+ break
299
+ player_json_obj["FoundOnMap"] = found
300
+ all_players.append(player_json_obj)
301
+
302
+ # Create json exports folder if it does not exist.
303
+ path_obj = Path(export_folder_path)
304
+ if not (path_obj.exists() and path_obj.is_dir()):
305
+ path_obj.mkdir(parents=True, exist_ok=True)
306
+
307
+ # Write JSON.
308
+ with open(path_obj / "players.json", "w") as text_file:
309
+ text_file.write(json.dumps(all_players, default=lambda o: o.to_json_obj() if hasattr(o, 'to_json_obj') else None, indent=4, cls=DefaultJsonEncoder))
310
+
311
+ ArkSaveLogger.api_log("Players successfully exported.")
312
+
313
+ def export_tribes(self, player_api: PlayerApi = None, export_folder_path: str = Path.cwd() / "json_exports", include_players_data: bool = False):
314
+ ArkSaveLogger.api_log("Exporting tribes...")
315
+
316
+ # Get player API if not provided.
317
+ if player_api is None:
318
+ player_api = PlayerApi(self.save, self.ignore_error)
319
+
320
+ # Format tribes into JSON.
321
+ all_tribes = []
322
+ for tribe in player_api.tribes:
323
+ # Grab the tribe json object
324
+ tribe_json_obj = tribe.to_json_obj()
325
+ # Grab tribe members as json objects if they exists
326
+ tribe_members = []
327
+ for p in player_api.tribe_to_player_map[tribe.tribe_id]:
328
+ if include_players_data:
329
+ player_json_obj = p.to_json_obj()
330
+ else:
331
+ player_json_obj = { "PlayerCharacterName": p.char_name, "PlayerDataID": p.id_ }
332
+ player_json_obj["IsActive"] = True
333
+ tribe_members.append(player_json_obj)
334
+ for idx, p_id in enumerate(tribe.member_ids):
335
+ is_active = False
336
+ for pl in player_api.tribe_to_player_map[tribe.tribe_id]:
337
+ if pl.id_ == p_id:
338
+ is_active = True
339
+ if not is_active:
340
+ tribe_members.append({ "PlayerCharacterName": tribe.members[idx], "PlayerDataID": p_id, "IsActive": False })
341
+ tribe_json_obj["TribeMembers"] = tribe_members
342
+ # Add to the tribes array
343
+ all_tribes.append(tribe_json_obj)
344
+
345
+ # Create json exports folder if it does not exist.
346
+ path_obj = Path(export_folder_path)
347
+ if not (path_obj.exists() and path_obj.is_dir()):
348
+ path_obj.mkdir(parents=True, exist_ok=True)
349
+
350
+ # Write JSON.
351
+ with open(path_obj / "tribes.json", "w") as text_file:
352
+ text_file.write(
353
+ json.dumps(all_tribes, default=lambda o: o.to_json_obj() if hasattr(o, 'to_json_obj') else None, indent=4, cls=DefaultJsonEncoder))
354
+
355
+ ArkSaveLogger.api_log("Tribes successfully exported.")
356
+
282
357
  def export_dinos(self, dino_api: DinoApi = None, export_folder_path: str = Path.cwd() / "json_exports"):
283
358
  ArkSaveLogger.api_log("Exporting dinos...")
284
359
 
@@ -313,11 +388,11 @@ class JsonApi:
313
388
  structure_api = StructureApi(self.save)
314
389
 
315
390
  # Get structures.
316
- structures: list[Structure | StructureWithInventory] = structure_api.get_all_fast()
391
+ structures: dict[UUID, Structure | StructureWithInventory] = structure_api.get_all()
317
392
 
318
393
  # Format dinos into JSON.
319
394
  all_structures = []
320
- for structure in structures:
395
+ for structure in structures.values():
321
396
  all_structures.append(structure.to_json_obj())
322
397
 
323
398
  # Create json exports folder if it does not exist.
@@ -340,27 +415,33 @@ class JsonApi:
340
415
  with self.save.connection as conn:
341
416
  cursor = conn.execute(query)
342
417
  for row in cursor:
343
- obj_uuid = self.save.byte_array_to_uuid(row[0])
344
- byte_buffer = ArkBinaryParser(row[1], self.save.save_context)
345
- class_name = byte_buffer.read_name()
346
-
347
- if "/PrimalItemArmor_" not in class_name and \
348
- "/PrimalItem_" not in class_name and \
349
- "/PrimalItemAmmo_" not in class_name and \
350
- "/PrimalItemC4Ammo" not in class_name and \
351
- "/PrimalItemResource_" not in class_name and \
352
- "/DroppedItemGeneric_" not in class_name and \
353
- "/PrimalItemConsumable_" not in class_name:
354
- continue
355
-
356
- obj = self.save.parse_as_predefined_object(obj_uuid, class_name, byte_buffer)
357
- if obj is not None:
358
- is_engram = False
359
- if obj.has_property("bIsEngram"):
360
- is_engram = obj.get_property_value("bIsEngram", False)
361
- if is_engram and not include_engrams:
418
+ try:
419
+ obj_uuid = self.save.byte_array_to_uuid(row[0])
420
+ byte_buffer = ArkBinaryParser(row[1], self.save.save_context)
421
+ class_name = byte_buffer.read_name()
422
+
423
+ if "/PrimalItemArmor_" not in class_name and \
424
+ "/PrimalItem_" not in class_name and \
425
+ "/PrimalItemAmmo_" not in class_name and \
426
+ "/PrimalItemC4Ammo" not in class_name and \
427
+ "/PrimalItemResource_" not in class_name and \
428
+ "/DroppedItemGeneric_" not in class_name and \
429
+ "/PrimalItemConsumable_" not in class_name:
362
430
  continue
363
- all_items.append(JsonApi.primal_item_to_json_obj(obj))
431
+
432
+ obj = self.save.parse_as_predefined_object(obj_uuid, class_name, byte_buffer)
433
+ if obj is not None:
434
+ is_engram = False
435
+ if obj.has_property("bIsEngram"):
436
+ is_engram = obj.get_property_value("bIsEngram", False)
437
+ if is_engram and not include_engrams:
438
+ continue
439
+ all_items.append(JsonApi.primal_item_to_json_obj(obj))
440
+ except Exception as e:
441
+ if ArkSaveLogger._allow_invalid_objects:
442
+ ArkSaveLogger.error_log(f"Failed to parse item {UUID(row[0])}: {e}")
443
+ else:
444
+ raise e
364
445
 
365
446
  # Create json exports folder if it does not exist.
366
447
  path_obj = Path(export_folder_path)
@@ -373,12 +454,29 @@ class JsonApi:
373
454
 
374
455
  ArkSaveLogger.api_log("Items successfully exported.")
375
456
 
457
+ def export_save_file_info(self, export_folder_path: str = Path.cwd() / "json_exports"):
458
+ ArkSaveLogger.api_log("Exporting save file info...")
459
+
460
+ save_info = { "MapName": self.save.save_context.map_name, "GameTime": self.save.save_context.game_time }
461
+
462
+ # Create json exports folder if it does not exist.
463
+ path_obj = Path(export_folder_path)
464
+ if not (path_obj.exists() and path_obj.is_dir()):
465
+ path_obj.mkdir(parents=True, exist_ok=True)
466
+
467
+ # Write JSON.
468
+ with open(path_obj / "save_info.json", "w") as text_file:
469
+ text_file.write(json.dumps(save_info, default=lambda o: o.to_json_obj() if hasattr(o, 'to_json_obj') else None, indent=4, cls=DefaultJsonEncoder))
470
+
471
+ ArkSaveLogger.api_log("Save file info successfully exported.")
472
+
376
473
  def export_all(self,
377
474
  equipment_api: EquipmentApi = None,
378
475
  player_api: PlayerApi = None,
379
476
  dino_api: DinoApi = None,
380
477
  structure_api: StructureApi = None,
381
478
  export_folder_path: str = Path.cwd() / "json_exports"):
479
+ self.export_save_file_info(export_folder_path=export_folder_path)
382
480
  self.export_armors(equipment_api=equipment_api, export_folder_path=export_folder_path)
383
481
  self.export_weapons(equipment_api=equipment_api, export_folder_path=export_folder_path)
384
482
  self.export_shields(equipment_api=equipment_api, export_folder_path=export_folder_path)
@@ -387,3 +485,5 @@ class JsonApi:
387
485
  self.export_structures(structure_api=structure_api, export_folder_path=export_folder_path)
388
486
  self.export_dinos(dino_api=dino_api, export_folder_path=export_folder_path)
389
487
  self.export_player_pawns(player_api=player_api, export_folder_path=export_folder_path)
488
+ self.export_players(player_api=player_api, export_folder_path=export_folder_path)
489
+ self.export_tribes(player_api=player_api, export_folder_path=export_folder_path)
@@ -125,7 +125,7 @@ class PlayerApi:
125
125
  OBJECT = 0
126
126
  DINO = 1
127
127
 
128
- def __init__(self, save: AsaSave, ignore_error: bool = False):
128
+ def __init__(self, save: AsaSave, ignore_error: bool = False, no_pawns: bool = False):
129
129
  self.players: List[ArkPlayer] = []
130
130
  self.tribes: List[ArkTribe] = []
131
131
  self.tribe_to_player_map: Dict[int, List[ArkPlayer]] = {}
@@ -141,7 +141,7 @@ class PlayerApi:
141
141
  ArkSaveLogger.api_log("Profile data not found in save, checking database")
142
142
  self.from_store = False
143
143
 
144
- if self.save is not None:
144
+ if self.save is not None and not no_pawns:
145
145
  ArkSaveLogger.api_log(f"Retrieving player pawns")
146
146
  self.__init_pawns()
147
147
 
@@ -326,6 +326,12 @@ class PlayerApi:
326
326
  dict[p.id_] = p.stats.experience
327
327
 
328
328
  return self.__calc_stat(xp, stat_type) if not as_dict else dict
329
+
330
+ def get_tribe(self, tribe_id: int):
331
+ for t in self.tribes:
332
+ if t.tribe_id == tribe_id:
333
+ return t
334
+ return None
329
335
 
330
336
  def get_player_with(self, stat: int, stat_type: int = StatType.HIGHEST):
331
337
  istat = self.__get_stat(stat)