def is_item_base_class(item: PrimalItem) -> bool: item_name = item.get('DescriptiveNameBase', 0, None) icon_texture = item.get('ItemIcon', 0, None) icon_material = item.get('ItemIconMaterialParent', 0, None) if not item_name or (not icon_texture and not icon_material): return True return False
def _get_pretty_item_type(item: PrimalItem) -> str: itemType = item.get('MyItemType', 0, None) value = itemType.get_enum_value_name() if value == 'MiscConsumable': consumableType = item.get('MyConsumableType', 0, None) return value + '/' + consumableType.get_enum_value_name() elif value == 'Equipment': equipmentType = item.get('MyEquipmentType', 0, None) return value + '/' + equipmentType.get_enum_value_name() return value
def convert_egg_values(item: PrimalItem) -> Dict[str, Any]: v = dict() dino_class = item.get('EggDinoClassToSpawn', 0, None) if dino_class: v['dinoClass'] = dino_class v['temperature'] = (item.EggMinTemperature[0], item.EggMaxTemperature[0]) hud_mic_ref = item.get('ItemIconMaterialParent', 0, None) if hud_mic_ref: hud_mic = item.get_source().asset.loader.load_related(hud_mic_ref) hud_mic = hud_mic.default_export hud_colors = gather_hud_color_data(hud_mic) if hud_colors: v['hudColorisation'] = hud_colors return v
def convert_egg_values(item: PrimalItem) -> Optional[EggData]: dino_class = item.get('EggDinoClassToSpawn', 0, None) if not dino_class or not dino_class.value or not dino_class.value.value: return None return EggData( dinoClass=dino_class.value.value.format_for_json(), temperature=MinMaxRange(min=item.EggMinTemperature[0], max=item.EggMaxTemperature[0]), )
def convert_egg_values(item: PrimalItem) -> Dict[str, Any]: v = dict() dino_class = item.get('EggDinoClassToSpawn', 0, None) if dino_class: v['dinoClass'] = dino_class v['temperature'] = (item.EggMinTemperature[0], item.EggMaxTemperature[0]) hud_mic_ref = item.get('ItemIconMaterialParent', 0, None) if hud_mic_ref and hud_mic_ref.value and hud_mic_ref.value.value: try: hud_mic = item.get_source().asset.loader.load_related(hud_mic_ref) hud_mic = hud_mic.default_export hud_colors = gather_hud_color_data(hud_mic) if hud_colors: v['hudColorisation'] = hud_colors except AssetLoadException: logger.warning(f'Failure while gathering color data from {hud_mic_ref.value.value.fullname}', exc_info=True) return v
def convert_crafting_values(item: PrimalItem) -> Dict[str, Any]: if item.bCraftDontActuallyGiveItem[0]: product_count: Union[int, IntProperty] = 0 else: if item.CraftingGivesItemQuantityOverride[0].value >= 1: product_count = item.CraftingGiveItemCount[0] else: product_count = item.ItemQuantity[0] v: Dict[str, Any] = dict(crafting=dict( xp=item.BaseCraftingXP[0], bpCraftTime=item.BlueprintTimeToCraft[0], minLevelReq=item.CraftingMinLevelRequirement[0], productCount=product_count, skillQualityMult=(item.CraftingSkillQualityMultiplierMin[0], item.CraftingSkillQualityMultiplierMax[0]), )) recipe = item.get('BaseCraftingResourceRequirements', 0, None) if recipe and recipe.values: v['crafting']['recipe'] = [ v for v in (convert_recipe_entry(entry.as_dict()) for entry in recipe.values) if v ] if item.bAllowRepair[0]: v['repair'] = dict( xp=item.BaseRepairingXP[0], time=item.TimeForFullRepair[0], resourceMult=item.RepairResourceRequirementMultiplier[0], ) if item.bOverrideRepairingRequirements[0]: recipe = item.get('OverrideRepairingRequirements', 0, None) if recipe and recipe.values: v['repair']['recipe'] = [ v for v in (convert_recipe_entry(entry.as_dict()) for entry in recipe.values) if v ] return v
def get_item_name(item: PrimalItem) -> Optional[str]: item_name = item.get('DescriptiveNameBase', fallback=None) if not item_name: return None out = str(item_name) # The game adds the Skin suffix to the item's name if bIsItemSkin is true. This only happens when the name is # not overridden by any other dynamic feature, like scripts or the rarity system (that preseeds quality and # other stats), and it's probably safer for us to export the CDO name in these cases. if item.bIsItemSkin[0] and not item.bUseBPGetItemName[0] and not item.bUseItemStats[0]: out += ' Skin' return out
def extract_core(self, _: Path): '''Perform sanity tests on core items.''' # Count species by prefix (/Game/<part> or /Game/Mods/<id>) counter: Counter = Counter() for clsname in find_sub_classes(PrimalItem.get_ue_type()): if not clsname.startswith('/Game'): continue parts = clsname.split('/') if clsname.startswith('/Game/Mods/'): modid = self.manager.loader.get_mod_id(clsname) assert modid parts[3] = modid parts = parts[:4] else: parts = parts[:3] counter.update(['/'.join(parts)]) # Check counts against configured limits overrides = get_overrides() reports = list() for path, min_count in overrides.sanity_checks.min_items.items(): count = counter.get(path, 0) logger.debug('%s = %d (need %d)', path, count, min_count) if count < min_count: reports.append((path, count, min_count)) # If anything failed, report and turn off Git push if not reports: return None # Disable git push to hopefully avoid damaging commits self.manager.config.git.SkipPush = False # Put together a report message header = "Items count sanity check failed! (git push disabled):\n" lines: List[str] = [] for path, count, min_count in reports: lines.append(f" {path} expected {min_count}, found {count}\n") # Log it logger.error(''.join([header] + lines)) # Send it to Discord send_to_discord(log=lines, header='Purlovia failed the items sanity check:')
def get_ue_type(self) -> str: return PrimalItem.get_ue_type()
def gather_item_stat(item: PrimalItem, index: Stat) -> Dict[str, Any]: leaf_export = item.get_source() ark_index = index.value return gather_inherited_struct_fields(leaf_export, 'ItemStatInfos', DEFAULTS, ark_index)
def convert_crafting_values( item: PrimalItem, has_durability: bool = False ) -> Tuple[Optional[CraftingData], Optional[RepairData]]: recipe = item.get('BaseCraftingResourceRequirements', 0, None) if not recipe: return (None, None) # Crafted item number if item.bCraftDontActuallyGiveItem[0]: product_count: Union[int, IntProperty] = 0 elif item.CraftingGiveItemCount[0] >= 1: product_count = item.CraftingGiveItemCount[0] elif item.CraftingGivesItemQuantityOverride[0] >= 1: product_count = item.CraftingGivesItemQuantityOverride[0] else: product_count = item.ItemQuantity[0] crafting = CraftingData( xp=item.BaseCraftingXP[0], time=item.BlueprintTimeToCraft[0], levelReq=item.CraftingMinLevelRequirement[0], productCount=int(product_count), skillQualityMult=MinMaxRange( min=item.CraftingSkillQualityMultiplierMin[0], max=item.CraftingSkillQualityMultiplierMax[0]), recipe=list(convert_recipe_entries(recipe.values)), ) # Do not export crafting info if recipe consists only of nulls. if not crafting.recipe: return (None, None) # Durability repair info repair = None if item.bAllowRepair[0] and has_durability: repair = RepairData( xp=item.BaseRepairingXP[0], time=item.TimeForFullRepair[0], recipe=[], ) if item.bOverrideRepairingRequirements[0]: recipe = item.get('OverrideRepairingRequirements', 0, None) if not recipe or not recipe.values: # Override to no ingredients, skip repair. repair = None else: # Convert the repair requirements list. repair.recipe = list(convert_recipe_entries(recipe.values)) # Do not export repair info if the overrides lead to no valid ingredients if not repair.recipe: repair = None else: # Copy crafting ingredients and scale their quantities by the repair multiplier. qty_mult = item.RepairResourceRequirementMultiplier[0] if qty_mult != 1.0: for ingredient in crafting.recipe: ingredient_copy = ingredient.copy() ingredient_copy.qty = clean_float(ingredient_copy.qty * qty_mult) repair.recipe.append(ingredient_copy) return (crafting, repair)
def collect_data(asset: UAsset) -> Tuple[str, Any]: if args.default and args.export is not None: print("Cannot specify an export with --default", file=sys.stderr) sys.exit(1) if args.ovi: if not args.export and not args.default: # Grab the default export since we need a starting point for the proxy. args.default = True export = find_export(asset) assert export assert export.fullname if not inherits_from(export, PrimalItem.get_ue_type()): print(f"Export {export.name} is not a subclass of PrimalItem.", file=sys.stderr) sys.exit(1) proxy: UEProxyStructure = gather_properties(export) item = cast(PrimalItem, proxy) if 'ItemIconMaterialParent' not in item: print("Item does not use an icon shader", file=sys.stderr) sys.exit(1) name = get_item_name(item) or export.name data: Dict[str, Any] = dict( Format='2.0', Name=name, BlueprintPath=export.fullname, ) assert asset.loader mat_instance = asset.loader.load_related(item.ItemIconMaterialParent[0]).default_export assert mat_instance mat_properties = mat_instance.properties.as_dict() # Convert all parameters from the material instance. parameters = dict() if 'ScalarParameterValues' in mat_properties: for param in mat_properties['ScalarParameterValues'][0].values: param_info = param.as_dict() param_name = sanitise_output(param_info['ParameterName']) parameters[param_name] = param_info['ParameterValue'] if 'VectorParameterValues' in mat_properties: for param in mat_properties['VectorParameterValues'][0].values: param_info = param.as_dict() param_name = sanitise_output(param_info['ParameterName']) parameters[param_name] = param_info['ParameterValue'].values[0] if 'TextureParameterValues' in mat_properties: for param in mat_properties['TextureParameterValues'][0].values: param_info = param.as_dict() param_name = sanitise_output(param_info['ParameterName']) parameters[param_name] = param_info['ParameterValue'] if parameters: data['2DMaterial'] = dict(Parent=mat_properties['Parent'][0], **parameters) else: # Export only the parent material as the instance has no parameters. data['2DMaterial'] = mat_properties['Parent'][0] data = sanitise_output(data) filename = create_filename(export.fullname) elif args.default or args.export: export = find_export(asset) assert export assert export.fullname data = sanitise_output(export.properties) filename = create_filename(export.fullname) else: # Full asset extraction data = sanitise_output(asset) assert asset.assetname filename = create_filename(asset.assetname) return (filename, data)