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:
@@ -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>
|
||||
@@ -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] ?? [];
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user