mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-11-17 18:52:06 +00:00
feat: show zone and asset statistics in modman
This commit is contained in:
@@ -1,26 +1,139 @@
|
||||
<script setup lang="ts">
|
||||
import Button from "primevue/button";
|
||||
import Tag from "primevue/tag";
|
||||
import MeterGroup, { type MeterItem } from "primevue/metergroup";
|
||||
import Skeleton from "primevue/skeleton";
|
||||
import { dt } from "@primeuix/themes";
|
||||
import type { ZoneDto } from "@/native/ZoneBinds";
|
||||
import { useZoneStore } from "@/stores/ZoneStore";
|
||||
import { computed } from "vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import type { CommonAssetType, ZoneAssetsDto } from "@/native/AssetBinds";
|
||||
import { webviewBinds } from "@/native";
|
||||
import { getAssetTypeNameCapitalized } from "@/utils/AssetTypeName";
|
||||
|
||||
const zoneStore = useZoneStore();
|
||||
|
||||
const props = defineProps<{
|
||||
selectedZone: string;
|
||||
selectedZone: string | null;
|
||||
}>();
|
||||
|
||||
const assets = ref<ZoneAssetsDto | null>(null);
|
||||
const assetCount = computed(() => assets.value?.assets.length ?? 0);
|
||||
const referenceCount = computed(() => assets.value?.references.length ?? 0);
|
||||
|
||||
const METER_COLORS = [
|
||||
dt("blue.600"),
|
||||
dt("red.600"),
|
||||
dt("yellow.600"),
|
||||
dt("green.600"),
|
||||
dt("purple.600"),
|
||||
dt("orange.600"),
|
||||
dt("teal.600"),
|
||||
dt("lime.600"),
|
||||
dt("pink.600"),
|
||||
];
|
||||
const meterItems = computed<MeterItem[]>(() => {
|
||||
const assetTypeCounts: Partial<Record<CommonAssetType, number>> = {};
|
||||
|
||||
for (const asset of assets.value?.assets ?? []) {
|
||||
if (!assetTypeCounts[asset.type]) {
|
||||
assetTypeCounts[asset.type] = 1;
|
||||
} else {
|
||||
assetTypeCounts[asset.type]!++;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not display asset types with under 3 percent representation
|
||||
const minItemCountForDisplay = Math.floor(assetCount.value * 0.03);
|
||||
|
||||
const assetMeterItems: MeterItem[] = Object.entries(assetTypeCounts)
|
||||
.filter((entry) => entry[1] > minItemCountForDisplay)
|
||||
.sort((e0, e1) => e1[1] - e0[1])
|
||||
.map((entry) => ({
|
||||
label: getAssetTypeNameCapitalized(entry[0] as CommonAssetType),
|
||||
value: Math.round((entry[1] / assetCount.value) * 100),
|
||||
}));
|
||||
|
||||
// Since the PrimeVue component rounds to percent we want to fill up the bar completely
|
||||
const otherCount = 100 - assetMeterItems.reduce((val, entry) => val + entry.value, 0);
|
||||
|
||||
if (otherCount > 0) {
|
||||
assetMeterItems.push({
|
||||
label: "Other",
|
||||
value: otherCount,
|
||||
});
|
||||
}
|
||||
|
||||
return assetMeterItems.map(
|
||||
(item, index) =>
|
||||
({
|
||||
...item,
|
||||
color: METER_COLORS[index % METER_COLORS.length],
|
||||
}) satisfies MeterItem,
|
||||
);
|
||||
});
|
||||
|
||||
const selectedZoneDetails = computed<ZoneDto | null>(
|
||||
() => zoneStore.loadedZones.find((zone) => zone.name === props.selectedZone) ?? null,
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.selectedZone,
|
||||
(newValue) => {
|
||||
assets.value = null;
|
||||
if (!newValue) return;
|
||||
webviewBinds.getAssetsForZone(newValue).then((res) => {
|
||||
if (props.selectedZone === newValue) {
|
||||
assets.value = res;
|
||||
}
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="zone-details">
|
||||
<h2>{{ selectedZone ?? "No zone selected" }}</h2>
|
||||
<Button label="Show assets" :disabled="!selectedZone" />
|
||||
<div v-if="selectedZoneDetails" class="zone-tags">
|
||||
<Tag :value="selectedZoneDetails.game" />
|
||||
<Tag :value="selectedZoneDetails.platform" />
|
||||
</div>
|
||||
<div class="zone-assets">
|
||||
<template v-if="assets">
|
||||
<div>{{ assetCount }} assets</div>
|
||||
<div>{{ referenceCount }} references</div>
|
||||
<MeterGroup class="asset-meter" :value="meterItems" />
|
||||
</template>
|
||||
<template v-else-if="selectedZone">
|
||||
<Skeleton class="count-skeleton" width="10em" />
|
||||
<Skeleton class="count-skeleton" width="10em" />
|
||||
<Skeleton class="count-skeleton" height="0.5lh" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.zone-details {
|
||||
.zone-tags {
|
||||
display: flex;
|
||||
margin-top: 0.5em;
|
||||
column-gap: 0.5em;
|
||||
row-gap: 0.5em;
|
||||
}
|
||||
|
||||
.zone-assets {
|
||||
margin-top: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.asset-meter {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
.count-skeleton {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,7 +17,7 @@ export enum CommonAssetType {
|
||||
LIGHT_DEF = "LIGHT_DEF",
|
||||
UI_MAP = "UI_MAP",
|
||||
FONT = "FONT",
|
||||
MENULIST = "MENULIST",
|
||||
MENU_LIST = "MENU_LIST",
|
||||
MENU = "MENU",
|
||||
LOCALIZE_ENTRY = "LOCALIZE_ENTRY",
|
||||
WEAPON = "WEAPON",
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
export enum GameId {
|
||||
IW3 = "IW3",
|
||||
IW4 = "IW4",
|
||||
IW5 = "IW5",
|
||||
T5 = "T5",
|
||||
T6 = "T6",
|
||||
}
|
||||
|
||||
export enum GamePlatform {
|
||||
PC = "PC",
|
||||
XBOX = "XBOX",
|
||||
PS3 = "PS3",
|
||||
}
|
||||
|
||||
export interface ZoneDto {
|
||||
name: string;
|
||||
filePath: string;
|
||||
game: GameId;
|
||||
platform: GamePlatform;
|
||||
}
|
||||
|
||||
export interface ZoneLoadProgressDto {
|
||||
|
||||
85
src/ModManUi/src/utils/AssetTypeName.ts
Normal file
85
src/ModManUi/src/utils/AssetTypeName.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { CommonAssetType } from "@/native/AssetBinds";
|
||||
|
||||
const LOOKUP_CAPITALIZED: Record<CommonAssetType, string> = {
|
||||
[CommonAssetType.PHYS_PRESET]: "Phys preset",
|
||||
[CommonAssetType.XANIM]: "XAnim",
|
||||
[CommonAssetType.XMODEL]: "XModel",
|
||||
[CommonAssetType.MATERIAL]: "Material",
|
||||
[CommonAssetType.TECHNIQUE_SET]: "Technique set",
|
||||
[CommonAssetType.IMAGE]: "Image",
|
||||
[CommonAssetType.SOUND]: "Sound",
|
||||
[CommonAssetType.SOUND_CURVE]: "Sound curve",
|
||||
[CommonAssetType.LOADED_SOUND]: "Loaded sound",
|
||||
[CommonAssetType.CLIP_MAP]: "Clip map",
|
||||
[CommonAssetType.COM_WORLD]: "Com world",
|
||||
[CommonAssetType.GAME_WORLD_SP]: "Game world SP",
|
||||
[CommonAssetType.GAME_WORLD_MP]: "Game world MP",
|
||||
[CommonAssetType.MAP_ENTS]: "Map ents",
|
||||
[CommonAssetType.GFX_WORLD]: "Gfx world",
|
||||
[CommonAssetType.LIGHT_DEF]: "Light def",
|
||||
[CommonAssetType.UI_MAP]: "UI map",
|
||||
[CommonAssetType.FONT]: "Font",
|
||||
[CommonAssetType.MENU_LIST]: "Menu list",
|
||||
[CommonAssetType.MENU]: "Menu",
|
||||
[CommonAssetType.LOCALIZE_ENTRY]: "Localize entry",
|
||||
[CommonAssetType.WEAPON]: "Weapon",
|
||||
[CommonAssetType.SOUND_DRIVER_GLOBALS]: "Sound driver globals",
|
||||
[CommonAssetType.FX]: "FX",
|
||||
[CommonAssetType.IMPACT_FX]: "Impact FX",
|
||||
[CommonAssetType.AI_TYPE]: "AI type",
|
||||
[CommonAssetType.MP_TYPE]: "MP type",
|
||||
[CommonAssetType.CHARACTER]: "Character",
|
||||
[CommonAssetType.XMODEL_ALIAS]: "XModel alias",
|
||||
[CommonAssetType.RAW_FILE]: "Raw file",
|
||||
[CommonAssetType.STRING_TABLE]: "String table",
|
||||
[CommonAssetType.XMODEL_PIECES]: "XModel pieces",
|
||||
[CommonAssetType.PHYS_COLL_MAP]: "Phys coll map",
|
||||
[CommonAssetType.XMODEL_SURFS]: "XModel surfs",
|
||||
[CommonAssetType.PIXEL_SHADER]: "Pixel shader",
|
||||
[CommonAssetType.VERTEX_SHADER]: "Vertex shader",
|
||||
[CommonAssetType.VERTEX_DECL]: "Vertex decl",
|
||||
[CommonAssetType.FX_WORLD]: "FX world",
|
||||
[CommonAssetType.LEADERBOARD]: "Leaderboard",
|
||||
[CommonAssetType.STRUCTURED_DATA_DEF]: "Structured data def",
|
||||
[CommonAssetType.TRACER]: "Tracer",
|
||||
[CommonAssetType.VEHICLE]: "Vehicle",
|
||||
[CommonAssetType.ADDON_MAP_ENTS]: "Addon map ents",
|
||||
[CommonAssetType.GLASS_WORLD]: "Glass world",
|
||||
[CommonAssetType.PATH_DATA]: "Path data",
|
||||
[CommonAssetType.VEHICLE_TRACK]: "Vehicle track",
|
||||
[CommonAssetType.ATTACHMENT]: "Attachment",
|
||||
[CommonAssetType.SURFACE_FX]: "Surface FX",
|
||||
[CommonAssetType.SCRIPT]: "Script",
|
||||
[CommonAssetType.PHYS_CONSTRAINTS]: "Phys constraints",
|
||||
[CommonAssetType.DESTRUCTIBLE_DEF]: "Destructible def",
|
||||
[CommonAssetType.SOUND_PATCH]: "Sound patch",
|
||||
[CommonAssetType.WEAPON_DEF]: "Weapon def",
|
||||
[CommonAssetType.WEAPON_VARIANT]: "Weapon variant",
|
||||
[CommonAssetType.MP_BODY]: "MP body",
|
||||
[CommonAssetType.MP_HEAD]: "MP head",
|
||||
[CommonAssetType.PACK_INDEX]: "Pack index",
|
||||
[CommonAssetType.XGLOBALS]: "XGlobals",
|
||||
[CommonAssetType.DDL]: "DDL",
|
||||
[CommonAssetType.GLASSES]: "Glasses",
|
||||
[CommonAssetType.EMBLEM_SET]: "Emblem set",
|
||||
[CommonAssetType.FONT_ICON]: "Font icon",
|
||||
[CommonAssetType.WEAPON_FULL]: "Weapon full",
|
||||
[CommonAssetType.ATTACHMENT_UNIQUE]: "Attachment unique",
|
||||
[CommonAssetType.WEAPON_CAMO]: "Weapon camo",
|
||||
[CommonAssetType.KEY_VALUE_PAIRS]: "Key value pairs",
|
||||
[CommonAssetType.MEMORY_BLOCK]: "Memory block",
|
||||
[CommonAssetType.SKINNED_VERTS]: "Skinned verts",
|
||||
[CommonAssetType.QDB]: "Qdb",
|
||||
[CommonAssetType.SLUG]: "Slug",
|
||||
[CommonAssetType.FOOTSTEP_TABLE]: "Footstep table",
|
||||
[CommonAssetType.FOOTSTEP_FX_TABLE]: "Footstep FX table",
|
||||
[CommonAssetType.ZBARRIER]: "ZBarrier",
|
||||
};
|
||||
|
||||
export function getAssetTypeNameCapitalized(assetType: CommonAssetType): string {
|
||||
return LOOKUP_CAPITALIZED[assetType];
|
||||
}
|
||||
|
||||
export function getAssetTypeNameLower(assetType: CommonAssetType): string {
|
||||
return getAssetTypeNameCapitalized(assetType).toLocaleLowerCase();
|
||||
}
|
||||
Reference in New Issue
Block a user