consumable icons

This commit is contained in:
tresabhi committed 2026-05-27 20:54:48 -05:00
1 parent f58d06b3c3
commit 0cb4aadec3
6 files changed
+96 -19

No files matched your search

+3 -1
View File
@@ -2,6 +2,8 @@
"words": [
"gltf",
"amogos",
"Tankopedia"
"Tankopedia",
"uasset",
"oodle"
]
}
+12 -5
View File
@@ -11,6 +11,10 @@ using CUE4Parse.UE4.Objects.UObject;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.JavaScript.NodeApi;
using SkiaSharp;
using CUE4Parse.UE4.AssetRegistry.Objects;
using CUE4Parse.UE4.Readers;
using CUE4Parse.UE4.AssetRegistry.Readers;
using CUE4Parse.UE4.AssetRegistry;
namespace BlitzKit.Game.JSExport;
@@ -24,7 +28,7 @@ public partial class GameInterface
public GameInterface(string directory, string map, string temp)
{
provider = new(directory, map);
provider = new(directory, map, temp);
files = [.. provider.Files.Keys];
@@ -38,10 +42,6 @@ public partial class GameInterface
.Where(nation => !nation.EndsWith(".uasset") && nation != "TankStub")
.Distinct(),
];
string oodlePath = $"{temp}/oodle.so";
OodleHelper.DownloadOodleDll(ref oodlePath!);
OodleHelper.Initialize(oodlePath);
}
public HashSet<string> Files => files;
@@ -123,6 +123,13 @@ public partial class GameInterface
return [];
}
public byte[] ConsumableIcon(string tag)
{
var pda = provider.Discovered<UPrimaryDataAsset>(tag);
var icon = pda.Get<FSoftObjectPath>("Icon");
return provider.Image(icon);
}
private static readonly string equipmentPDAPrefix =
"Blitz/Content/TanksStuff/Equipment/PDA_Equipment";
private static readonly string equipmentPDASuffix = ".uasset";
+43 -2
View File
@@ -10,12 +10,21 @@ using CUE4Parse.UE4.Objects.UObject;
using CUE4Parse.UE4.Versions;
using CUE4Parse.Utils;
using SkiaSharp;
using CUE4Parse.UE4.Readers;
using CUE4Parse.UE4.AssetRegistry;
using System.Text.RegularExpressions;
using CUE4Parse.UE4.Assets.Exports;
namespace BlitzKit.Game.Models;
public class BlitzFileProvider : DefaultFileProvider
public partial class BlitzFileProvider : DefaultFileProvider
{
public BlitzFileProvider(string directory, string map)
[GeneratedRegex(@"\(GameplayTags=\(\(TagName=""(.+)""\)\)\)")]
private static partial Regex GameplayTagsRegex();
private readonly Dictionary<string, string> discovered = [];
public BlitzFileProvider(string directory, string map, string temp)
: base(
directory: directory,
searchOption: SearchOption.AllDirectories,
@@ -27,6 +36,38 @@ public class BlitzFileProvider : DefaultFileProvider
Initialize();
Mount();
string oodlePath = $"{temp}/oodle.so";
OodleHelper.DownloadOodleDll(ref oodlePath!);
OodleHelper.Initialize(oodlePath);
if (TryGetGameFile("Blitz/AssetRegistry.bin", out var file))
{
var bytes = file.Read();
var archive = new FByteArchive(file.Path, bytes, Versions);
var state = new FAssetRegistryState(archive);
foreach (var asset in state.PreallocatedAssetDataBuffers)
{
if (asset.TagsAndValues.TryGetValue("DiscoveryTags", out var tags))
{
var match = GameplayTagsRegex().Match(tags);
if (!match.Success)
{
continue;
}
var tag = match.Groups[1].Value;
discovered[tag] = asset.ObjectPath;
}
}
}
}
public T Discovered<T>(string path) where T : UObject
{
return LoadPackageObject<T>(discovered[path]);
}
public UPrimaryDataAsset PDA(string path)
@@ -0,0 +1,13 @@
import type { BlitzStaticAssetsDiscoveryComponent } from "@protos/blitz_static_assets_discovery_component";
export function assertDiscoveryTag(discovery: BlitzStaticAssetsDiscoveryComponent) {
if (
discovery.blocked_tags !== undefined ||
discovery.required_tags === undefined ||
discovery.required_tags.tags.length !== 1
) {
throw new TypeError("Unexpected discovery tag format");
}
return discovery.required_tags.tags[0].value;
}
@@ -0,0 +1,23 @@
import type { APIContext } from "astro";
import { api } from "../../../api/dynamic";
import { mixStaticPaths } from "../../../astro/mixStaticPaths";
import { game } from "../../../game/game";
import { getStaticPaths as _getStaticPaths } from "../_index";
import { assertDiscoveryTag } from "../../../game/assertDiscoveryTag";
export const getStaticPaths = mixStaticPaths(_getStaticPaths, () => {
return api.metadata
.group("ConsumableEntity")
.map(({ id }) => ({ params: { id } }));
});
export function GET({ params }: APIContext<never, { id: string }>) {
const item = api.metadata.item(params.id);
const assetsDiscovery = item.BlitzStaticAssetsDiscovery();
const tag = assertDiscoveryTag(assetsDiscovery);
const icon = game!.consumableIcon(tag);
const array = new Uint8Array(icon);
return new Response(array);
}
@@ -4,6 +4,7 @@ import { notFoundResponse } from "../../../api/responses";
import { mixStaticPaths } from "../../../astro/mixStaticPaths";
import { game } from "../../../game/game";
import { getStaticPaths as _getStaticPaths } from "../_index";
import { assetsDiscovery } from "../../../game/assetsDiscovery";
export const getStaticPaths = mixStaticPaths(_getStaticPaths, () => {
return api.metadata
@@ -17,17 +18,7 @@ const HACK_nameMap: Record<string, string> = {
export function GET({ params }: APIContext<never, { id: string }>) {
const item = api.metadata.item(params.id);
const assetDiscovery = item.BlitzStaticAssetsDiscovery();
const names = assetDiscovery.required_tags!.tags.map(
(tag) => tag.value.split(".").at(-1)!,
);
for (const name of names) {
if (name in HACK_nameMap) {
names.push(HACK_nameMap[name]);
}
}
const names = assetsDiscovery(item, HACK_nameMap);
const icon = game!.equipmentIcon(names);
if (icon.length === 0) {