2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2026-07-02 22:08:11 +00:00

feat: filter assets in modman (#860)

This commit is contained in:
Jan
2026-06-27 12:06:25 +02:00
committed by GitHub
parent a287947976
commit 256b5a5699
7 changed files with 545 additions and 111 deletions
@@ -0,0 +1,9 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640">
<!--!Font Awesome Free v7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.-->
<path
fill="currentColor"
d="M480 272C480 317.9 465.1 360.3 440 394.7L566.6 521.4C579.1 533.9 579.1 554.2 566.6 566.7C554.1 579.2 533.8 579.2 521.3 566.7L394.7 440C360.3 465.1 317.9 480 272 480C157.1 480 64 386.9 64 272C64 157.1 157.1 64 272 64C386.9 64 480 157.1 480 272zM272 416C351.5 416 416 351.5 416 272C416 192.5 351.5 128 272 128C192.5 128 128 192.5 128 272C128 351.5 192.5 416 272 416z"
/>
</svg>
</template>
+281
View File
@@ -0,0 +1,281 @@
import type { GameId } from "@/native/ZoneBinds.ts";
import type { CommonAssetType } from "@/native/AssetBinds.ts";
const ASSET_TYPES_BY_GAME: Record<GameId, CommonAssetType[]> = {
iw3: [
"xmodel_pieces",
"phys_preset",
"xanim",
"xmodel",
"material",
"technique_set",
"image",
"sound",
"sound_curve",
"loaded_sound",
"clip_map",
"clip_map",
"com_world",
"game_world_sp",
"game_world_mp",
"map_ents",
"gfx_world",
"light_def",
"ui_map",
"font",
"menu_list",
"menu",
"localize_entry",
"weapon",
"sound_driver_globals",
"fx",
"impact_fx",
"ai_type",
"mp_type",
"character",
"xmodel_alias",
"raw_file",
"string_table",
],
iw4: [
"phys_preset",
"phys_coll_map",
"xanim",
"xmodel_surfs",
"xmodel",
"material",
"pixel_shader",
"vertex_shader",
"vertex_decl",
"technique_set",
"image",
"sound",
"sound_curve",
"loaded_sound",
"clip_map",
"clip_map",
"com_world",
"game_world_sp",
"game_world_mp",
"map_ents",
"fx_world",
"gfx_world",
"light_def",
"ui_map",
"font",
"menu_list",
"menu",
"localize_entry",
"weapon",
"sound_driver_globals",
"fx",
"impact_fx",
"ai_type",
"mp_type",
"character",
"xmodel_alias",
"raw_file",
"string_table",
"leaderboard",
"structured_data_def",
"tracer",
"vehicle",
"addon_map_ents",
],
iw5: [
"phys_preset",
"phys_coll_map",
"xanim",
"xmodel_surfs",
"xmodel",
"material",
"pixel_shader",
"vertex_shader",
"vertex_decl",
"technique_set",
"image",
"sound",
"sound_curve",
"loaded_sound",
"clip_map",
"com_world",
"glass_world",
"path_data",
"vehicle_track",
"map_ents",
"fx_world",
"gfx_world",
"light_def",
"ui_map",
"font",
"menu_list",
"menu",
"localize_entry",
"attachment",
"weapon",
"sound_driver_globals",
"fx",
"impact_fx",
"surface_fx",
"ai_type",
"mp_type",
"character",
"xmodel_alias",
"raw_file",
"script",
"string_table",
"leaderboard",
"structured_data_def",
"tracer",
"vehicle",
"addon_map_ents",
],
t4: [
"xmodel_pieces",
"phys_preset",
"phys_constraints",
"destructible_def",
"xanim",
"xmodel",
"material",
"technique_set",
"image",
"sound",
"loaded_sound",
"clip_map",
"clip_map",
"com_world",
"game_world_sp",
"game_world_mp",
"map_ents",
"gfx_world",
"light_def",
"ui_map",
"font",
"menu_list",
"menu",
"localize_entry",
"weapon",
"sound_driver_globals",
"fx",
"impact_fx",
"ai_type",
"mp_type",
"character",
"xmodel_alias",
"raw_file",
"string_table",
"pack_index",
],
t5: [
"xmodel_pieces",
"phys_preset",
"phys_constraints",
"destructible_def",
"xanim",
"xmodel",
"material",
"technique_set",
"image",
"sound",
"sound_patch",
"clip_map",
"clip_map",
"com_world",
"game_world_sp",
"game_world_mp",
"map_ents",
"gfx_world",
"light_def",
"ui_map",
"font",
"menu_list",
"menu",
"localize_entry",
"weapon",
"weapon_def",
"weapon_variant",
"sound_driver_globals",
"fx",
"impact_fx",
"ai_type",
"mp_type",
"mp_body",
"mp_head",
"character",
"xmodel_alias",
"raw_file",
"string_table",
"pack_index",
"xglobals",
"ddl",
"glasses",
"emblem_set",
],
t6: [
"xmodel_pieces",
"phys_preset",
"phys_constraints",
"destructible_def",
"xanim",
"xmodel",
"material",
"technique_set",
"image",
"sound",
"sound_patch",
"clip_map",
"clip_map",
"com_world",
"game_world_sp",
"game_world_mp",
"map_ents",
"gfx_world",
"light_def",
"ui_map",
"font",
"font_icon",
"menu_list",
"menu",
"localize_entry",
"weapon",
"weapon_def",
"weapon_variant",
"weapon_full",
"attachment",
"attachment_unique",
"weapon_camo",
"sound_driver_globals",
"fx",
"impact_fx",
"ai_type",
"mp_type",
"mp_body",
"mp_head",
"character",
"xmodel_alias",
"raw_file",
"string_table",
"leaderboard",
"xglobals",
"ddl",
"glasses",
"emblem_set",
"script",
"key_value_pairs",
"vehicle",
"memory_block",
"addon_map_ents",
"tracer",
"skinned_verts",
"qdb",
"slug",
"footstep_table",
"footstep_fx_table",
"zbarrier",
],
};
export function getAssetTypesByGame(game: GameId): CommonAssetType[] {
return ASSET_TYPES_BY_GAME[game] ?? [];
}
-1
View File
@@ -14,7 +14,6 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color: colors.$color-text;
@@ -35,6 +35,7 @@ watch(
<InspectZoneAssets
v-model:selected-asset="selectedAsset"
:assets="assetsOfZone.assets"
:zone-name="zoneName"
class="inspect-area-assets"
/>
</template>
@@ -0,0 +1,109 @@
<script setup lang="ts">
import { computed } from "vue";
import IftaLabel from "primevue/iftalabel";
import InputText from "primevue/inputtext";
import MultiSelect from "primevue/multiselect";
import IconField from "primevue/iconfield";
import InputIcon from "primevue/inputicon";
import IconMagnifyingGlass from "@/components/icons/IconMagnifyingGlass.vue";
import { useZoneStore } from "@/stores/ZoneStore.ts";
import { getAssetTypesByGame } from "@/meta/GameAssetTypes.ts";
import { localizeAssetType } from "@/i18n/i18n.ts";
export interface AssetListFilterData {
searchText?: string;
assetTypeFilter?: string[];
}
const modelValue = defineModel<AssetListFilterData>({ required: true });
const props = defineProps<{
zoneName: string;
}>();
const zoneStore = useZoneStore();
const zone = computed(() => {
const result = zoneStore.getLoadedZoneByName(props.zoneName);
if (!result) throw new Error("Could not find zone");
return result;
});
const gameId = computed(() => zone.value.game);
const availableAssetTypes = computed(() =>
getAssetTypesByGame(gameId.value).map((assetType) => ({
value: assetType,
name: localizeAssetType(assetType),
})),
);
const searchText = computed<string>({
get: () => modelValue.value.searchText ?? "",
set: (value) => {
if (value) {
modelValue.value = { ...modelValue.value, searchText: value };
} else {
modelValue.value = { ...modelValue.value, searchText: undefined };
}
},
});
const assetTypeFilter = computed<string[]>({
get: () => modelValue.value.assetTypeFilter ?? [],
set: (value) => {
if (value.length > 0) {
modelValue.value = { ...modelValue.value, assetTypeFilter: value };
} else {
modelValue.value = { ...modelValue.value, assetTypeFilter: undefined };
}
},
});
</script>
<template>
<div class="filters">
<IftaLabel>
<IconField>
<InputIcon>
<IconMagnifyingGlass class="search-icon" />
</InputIcon>
<InputText v-model="searchText" fluid inputId="alf_search" />
<InputIcon>
<IconMagnifyingGlass class="search-icon" />
</InputIcon>
</IconField>
<label for="alf_search">Asset name</label>
</IftaLabel>
<IftaLabel>
<MultiSelect
v-model="assetTypeFilter"
fluid
inputId="alf_asset_types"
:options="availableAssetTypes"
show-clear
option-label="name"
option-value="value"
:max-selected-labels="2"
filter
variant="filled"
/>
<label for="alf_asset_types">Asset types</label>
</IftaLabel>
</div>
</template>
<style scoped lang="scss">
@use "sass:math";
$FILTER_GAP: 0.5rem;
$FILTER_COL_COUNT: 2;
$FILTER_PAD_MIDDLE: ($FILTER_COL_COUNT - 1) * $FILTER_GAP;
$FILTER_PAD_EDGE: math.div($FILTER_PAD_MIDDLE, 2);
.filters {
display: grid;
grid-template-columns: calc(60% - #{$FILTER_PAD_EDGE}) calc(40% - #{$FILTER_PAD_EDGE});
grid-gap: 0.5rem;
padding-bottom: 0.5rem;
}
.search-icon {
height: 1em;
}
</style>
@@ -2,31 +2,63 @@
import Listbox from "primevue/listbox";
import type { AssetDto } from "@/native/AssetBinds.ts";
import AssetListOption from "@/view/inspect_details/components/AssetListOption.vue";
import { computed, ref } from "vue";
import AssetListFilter, {
type AssetListFilterData,
} from "@/view/inspect_details/components/AssetListFilter.vue";
const selectedAsset = defineModel<AssetDto | undefined>("selectedAsset", { required: true });
defineProps<{
const props = defineProps<{
assets: AssetDto[];
zoneName: string;
}>();
const filter = ref<AssetListFilterData>({});
const filteredAssets = computed(() => {
let result = props.assets;
const nameFilter = filter.value.searchText;
const assetTypesToFilter = filter.value.assetTypeFilter;
if (assetTypesToFilter) {
result = result.filter((asset: AssetDto) => assetTypesToFilter.includes(asset.type));
}
if (nameFilter) {
result = result.filter((asset: AssetDto) => asset.name.indexOf(nameFilter) >= 0);
}
return result;
});
</script>
<template>
<Listbox
v-model="selectedAsset"
class="asset-list"
:options="assets"
option-label="name"
scroll-height="100%"
:virtual-scroller-options="{ itemSize: 24 }"
>
<template #option="{ option }: { option: AssetDto }">
<AssetListOption :asset="option" />
</template>
</Listbox>
<div class="zone-assets">
<AssetListFilter v-model="filter" :zone-name="zoneName" />
<Listbox
v-model="selectedAsset"
class="asset-list"
:options="filteredAssets"
option-label="name"
scroll-height="100%"
:virtual-scroller-options="{ itemSize: 24 }"
>
<template #option="{ option }: { option: AssetDto }">
<AssetListOption :asset="option" />
</template>
</Listbox>
</div>
</template>
<style scoped lang="scss">
.zone-assets {
display: flex;
flex-direction: column;
}
.asset-list {
--p-listbox-option-padding: 0;
flex-grow: 1;
}
:deep(.p-virtualscroller) {