mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-06-25 17:10:43 +00:00
wip
This commit is contained in:
parent
86c4f3072f
commit
ab32ab6746
16 changed files with 367 additions and 23 deletions
|
|
@ -3569,6 +3569,8 @@ _miWorld:
|
|||
crushed_description: "描画が継続できなくなりました。デバイスのリソース不足の可能性が考えられます。"
|
||||
antialiasing: "アンチエイリアス"
|
||||
avatar: "アバター"
|
||||
advancedCustomize: "高度なアレンジ"
|
||||
attachAccessory: "アクセサリーを追加"
|
||||
|
||||
_avatars:
|
||||
_default:
|
||||
|
|
@ -3576,6 +3578,13 @@ _miWorld:
|
|||
eyes: "目"
|
||||
mouth: "口"
|
||||
|
||||
_avatarAccessories:
|
||||
mug: "マグカップ"
|
||||
_mug:
|
||||
bodyMat: "コップの素材"
|
||||
liquidMat: "液体の素材"
|
||||
mikan: "みかん"
|
||||
|
||||
_miRoom:
|
||||
snapToGrid: "グリッドにスナップ"
|
||||
gridScale: "グリッドサイズ"
|
||||
|
|
@ -3779,6 +3788,9 @@ _miRoom:
|
|||
_monstera:
|
||||
potMat: "鉢の素材"
|
||||
mug: "マグカップ"
|
||||
_mug:
|
||||
bodyMat: "コップの素材"
|
||||
liquidMat: "液体の素材"
|
||||
newtonsCradle: "ニュートンクレードル"
|
||||
_newtonsCradle:
|
||||
frameMat: "フレームの素材"
|
||||
|
|
|
|||
|
|
@ -153,13 +153,6 @@ export class PlayerContainer {
|
|||
|
||||
this.registerMeshes(this.modelRoot.getChildMeshes());
|
||||
|
||||
// debug
|
||||
this.profile.worldAvatar.accessories = [{
|
||||
id: 'a',
|
||||
type: 'mikan',
|
||||
options: {},
|
||||
}];
|
||||
|
||||
this.accessoryContainers = await Promise.all(this.profile.worldAvatar.accessories.map(ac => this.loadAccessory({
|
||||
type: ac.type,
|
||||
id: ac.id,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { mug_schema } from 'misskey-world/src/avatars/accessories/mug.schema.js'
|
|||
import { defineAccessory } from '../accessory.js';
|
||||
|
||||
export const mug = defineAccessory(mug_schema, {
|
||||
createInstance: ({ scene, root, sr }) => {
|
||||
createInstance: ({ options, scene, root, sr, model }) => {
|
||||
const emitter = new BABYLON.TransformNode('emitter', scene);
|
||||
emitter.parent = root;
|
||||
emitter.position = new BABYLON.Vector3(0, cm(5), 0);
|
||||
|
|
@ -35,7 +35,33 @@ export const mug = defineAccessory(mug_schema, {
|
|||
ps.start();
|
||||
sr.fixParticleSystem(ps);
|
||||
|
||||
const bodyMaterial = model.findMaterial('__X_MUG__');
|
||||
|
||||
const applyBodyMat = () => {
|
||||
bodyMaterial.albedoColor = new BABYLON.Color3(options.bodyMat.color[0], options.bodyMat.color[1], options.bodyMat.color[2]);
|
||||
bodyMaterial.roughness = options.bodyMat.roughness;
|
||||
bodyMaterial.metallic = options.bodyMat.metallic;
|
||||
};
|
||||
|
||||
applyBodyMat();
|
||||
|
||||
const liquidMaterial = model.findMaterial('__X_LIQUID__');
|
||||
|
||||
const applyLiquidMat = () => {
|
||||
liquidMaterial.albedoColor = new BABYLON.Color3(options.liquidMat.color[0], options.liquidMat.color[1], options.liquidMat.color[2]);
|
||||
liquidMaterial.roughness = options.liquidMat.roughness;
|
||||
liquidMaterial.metallic = options.liquidMat.metallic;
|
||||
};
|
||||
|
||||
applyLiquidMat();
|
||||
|
||||
return {
|
||||
onOptionsUpdated: ([k, v]) => {
|
||||
switch (k) {
|
||||
case 'bodyMat': applyBodyMat(); break;
|
||||
case 'liquidMat': applyLiquidMat(); break;
|
||||
}
|
||||
},
|
||||
dispose: () => {
|
||||
ps.stop();
|
||||
emitter.dispose();
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import * as BABYLON from '@babylonjs/core';
|
||||
import { cm, WORLD_SCALE } from 'misskey-world/src/utility.js';
|
||||
import { GRAPHICS_QUALITY } from '../utility.js';
|
||||
import { findMaterial, SYSTEM_HEYA_MESH_NAMES } from './utility.js';
|
||||
import { findMaterial, GRAPHICS_QUALITY } from '../utility.js';
|
||||
import { SYSTEM_HEYA_MESH_NAMES } from './utility.js';
|
||||
import type { RoomEngine } from './engine.js';
|
||||
import type { SimpleEnvOptions, JapaneseEnvOptions, MuseumEnvOptions } from 'misskey-world/src/room/type.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -4,18 +4,41 @@
|
|||
*/
|
||||
|
||||
import * as BABYLON from '@babylonjs/core';
|
||||
import { defineObject } from '../object.js';
|
||||
import { cm } from 'misskey-world/src/utility.js';
|
||||
import { yuge } from '../utility.js';
|
||||
import { mug_schema } from 'misskey-world/src/room/objects/mug.schema.js';
|
||||
import { defineObject } from '../object.js';
|
||||
import { yuge } from '../utility.js';
|
||||
|
||||
export const mug = defineObject(mug_schema, {
|
||||
createInstance: ({ scene, root, sr }) => {
|
||||
let yugeDispose: (() => void) | null = null;
|
||||
createInstance: ({ options, scene, root, sr, model }) => {
|
||||
const yugeDispose = yuge(scene, root, new BABYLON.Vector3(0, cm(5), 0), sr);
|
||||
|
||||
const bodyMaterial = model.findMaterial('__X_MUG__');
|
||||
|
||||
const applyBodyMat = () => {
|
||||
bodyMaterial.albedoColor = new BABYLON.Color3(options.bodyMat.color[0], options.bodyMat.color[1], options.bodyMat.color[2]);
|
||||
bodyMaterial.roughness = options.bodyMat.roughness;
|
||||
bodyMaterial.metallic = options.bodyMat.metallic;
|
||||
};
|
||||
|
||||
applyBodyMat();
|
||||
|
||||
const liquidMaterial = model.findMaterial('__X_LIQUID__');
|
||||
|
||||
const applyLiquidMat = () => {
|
||||
liquidMaterial.albedoColor = new BABYLON.Color3(options.liquidMat.color[0], options.liquidMat.color[1], options.liquidMat.color[2]);
|
||||
liquidMaterial.roughness = options.liquidMat.roughness;
|
||||
liquidMaterial.metallic = options.liquidMat.metallic;
|
||||
};
|
||||
|
||||
applyLiquidMat();
|
||||
|
||||
return {
|
||||
onInited: () => {
|
||||
yugeDispose = yuge(scene, root, new BABYLON.Vector3(0, cm(5), 0), sr);
|
||||
onOptionsUpdated: ([k, v]) => {
|
||||
switch (k) {
|
||||
case 'bodyMat': applyBodyMat(); break;
|
||||
case 'liquidMat': applyLiquidMat(); break;
|
||||
}
|
||||
},
|
||||
interactions: {},
|
||||
dispose: () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div :class="$style.root">
|
||||
<div class="_gaps_s">
|
||||
<MkFolder v-for="[k, s] in Object.entries(schema.options.schema)" :key="k">
|
||||
<template #label>{{ AVATAR_ACCESSORY_UI_DEFS[schema.id].options[k].label }}</template>
|
||||
<template #suffix>
|
||||
<span v-if="s.type === 'color'" :style="{ color: getHex(options[k]) }">●</span>
|
||||
<span v-else-if="s.type === 'material'" :style="{ color: getHex(options[k].color) }">●</span>
|
||||
</template>
|
||||
|
||||
<div v-if="s.type === 'color'">
|
||||
<!-- debounce or throttleしないとカラーピッカー上で高速でなぞったときになぜか無限ループになる。ワーカーとの間でラグがあるため、少し前の値がまたmodelValueとしてフィードバックされてしまうためだと思われる -->
|
||||
<MkInput :modelValue="getHex(options[k])" type="color" :throttle="300" @update:modelValue="v => { const c = getRgb(v); if (c != null) emit('update', k, c); }"></MkInput>
|
||||
</div>
|
||||
<div v-else-if="s.type === 'material'" class="_gaps_s">
|
||||
<!-- debounce or throttleしないとカラーピッカー上で高速でなぞったときになぜか無限ループになる。ワーカーとの間でラグがあるため、少し前の値がまたmodelValueとしてフィードバックされてしまうためだと思われる -->
|
||||
<MkInput :modelValue="getHex(options[k].color)" type="color" :throttle="300" @update:modelValue="v => { const c = getRgb(v); if (c != null) updateMaterialColor(k, c); }">
|
||||
<template #label>{{ i18n.ts.color }}</template>
|
||||
</MkInput>
|
||||
<template v-if="advancedCustomize">
|
||||
<MkRange :continuousUpdate="true" :min="0" :max="1" :step="0.1" :modelValue="options[k].metallic" @update:modelValue="v => updateMaterialMetallic(k, v)">
|
||||
<template #label>{{ i18n.ts._miRoom.material_metallic }}</template>
|
||||
</MkRange>
|
||||
<MkRange :continuousUpdate="true" :min="0" :max="1" :step="0.05" :modelValue="options[k].roughness" @update:modelValue="v => updateMaterialRoughness(k, v)">
|
||||
<template #label>{{ i18n.ts._miRoom.material_roughness }}</template>
|
||||
</MkRange>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="s.type === 'light'" class="_gaps_s">
|
||||
<!-- debounce or throttleしないとカラーピッカー上で高速でなぞったときになぜか無限ループになる。ワーカーとの間でラグがあるため、少し前の値がまたmodelValueとしてフィードバックされてしまうためだと思われる -->
|
||||
<MkInput :modelValue="getHex(options[k].color)" type="color" :throttle="300" @update:modelValue="v => { const c = getRgb(v); if (c != null) updateLightColor(k, c); }">
|
||||
<template #label>{{ i18n.ts.color }}</template>
|
||||
</MkInput>
|
||||
<MkRange :continuousUpdate="true" :min="0" :max="1" :step="0.1" :modelValue="options[k].brightness" @update:modelValue="v => updateLightBrightness(k, v)">
|
||||
<template #label>{{ i18n.ts._miRoom.light_brightness }}</template>
|
||||
</MkRange>
|
||||
</div>
|
||||
<div v-else-if="s.type === 'boolean'">
|
||||
<MkSwitch :modelValue="options[k]" @update:modelValue="v => emit('update', k, v)"></MkSwitch>
|
||||
</div>
|
||||
<div v-else-if="s.type === 'enum'">
|
||||
<MkSelect :items="s.enum.map(e => ({ label: AVATAR_ACCESSORY_UI_DEFS[schema.id].options[k].enum[e.value].label, value: e.value }))" :modelValue="options[k]" @update:modelValue="v => emit('update', k, v)"></MkSelect>
|
||||
</div>
|
||||
<div v-else-if="s.type === 'string'">
|
||||
<MkInput type="text" :modelValue="options[k]" @update:modelValue="v => emit('update', k, v)"></MkInput>
|
||||
</div>
|
||||
<div v-else-if="s.type === 'range'">
|
||||
<MkRange :continuousUpdate="true" :min="s.min" :max="s.max" :step="s.step" :modelValue="options[k]" @update:modelValue="v => emit('update', k, v)"></MkRange>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkSwitch v-model="advancedCustomize">{{ i18n.ts._miWorld.advancedCustomize }}</MkSwitch>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { getHex, getRgb } from 'misskey-world/src/utility.js';
|
||||
import type { AccessorySchemaDef } from 'misskey-world/src/avatars/accessory.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import * as os from '@/os.js';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkRange from '@/components/MkRange.vue';
|
||||
import MkRadios from '@/components/MkRadios.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import { prefer } from '@/preferences.js';
|
||||
import { AVATAR_ACCESSORY_UI_DEFS } from '@/world/avatars/accessory-ui-defs.js';
|
||||
|
||||
const props = defineProps<{
|
||||
schema: AccessorySchemaDef<any>;
|
||||
options: Record<string, unknown>;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update', k: string, v: any): void;
|
||||
}>();
|
||||
|
||||
const advancedCustomize = ref(false);
|
||||
|
||||
function updateMaterialColor(k: string, color: { r: number; g: number; b: number }) {
|
||||
emit('update', k, { ...props.options[k], color });
|
||||
}
|
||||
|
||||
function updateMaterialMetallic(k: string, metallic: number) {
|
||||
emit('update', k, { ...props.options[k], metallic });
|
||||
}
|
||||
|
||||
function updateMaterialRoughness(k: string, roughness: number) {
|
||||
emit('update', k, { ...props.options[k], roughness });
|
||||
}
|
||||
|
||||
function updateLightColor(k: string, color: { r: number; g: number; b: number }) {
|
||||
emit('update', k, { ...props.options[k], color });
|
||||
}
|
||||
|
||||
function updateLightBrightness(k: string, brightness: number) {
|
||||
emit('update', k, { ...props.options[k], brightness });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
}
|
||||
</style>
|
||||
|
|
@ -108,6 +108,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</MkInput>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<hr>
|
||||
|
||||
<MkFolder v-for="a in avatar.accessories" :key="a.id">
|
||||
<template #label>{{ AVATAR_ACCESSORY_UI_DEFS[a.type].name }}</template>
|
||||
<XAccessory :schema="getAccessorySchemaDef(a.type)" :options="a.options" @update="(k, v) => { a.options[k] = v; updateAvatarOption(); }"/>
|
||||
</MkFolder>
|
||||
|
||||
<MkButton primary rounded @click="addAccessory">Add accessory</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
|
@ -121,7 +130,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
import { ref, useTemplateRef, watch, onMounted, onUnmounted, reactive, nextTick, shallowRef, computed, triggerRef, markRaw } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { getHex, getRgb } from 'misskey-world/src/utility.js';
|
||||
import { ACCESSORY_SCHEMA_DEFS, getAccessorySchemaDef } from 'misskey-world/src/avatars/accessory-schema-defs.js';
|
||||
import MkFolder from './MkFolder.vue';
|
||||
import XAccessory from './MkWorldAvatarEditDialog.accessory.vue';
|
||||
import type { Ref } from 'vue';
|
||||
import type { WorldAvatar } from 'misskey-world/src/types.js';
|
||||
import type { AvatarPreviewEngineControllerOptions } from '@/world/avatarPreviewEngineController.js';
|
||||
|
|
@ -137,6 +148,8 @@ import { store } from '@/store.js';
|
|||
import MkInput from '@/components/MkInput.vue';
|
||||
import { withTimeout } from '@/utility/promise-timeout.js';
|
||||
import { ensureSignin } from '@/i.js';
|
||||
import { AVATAR_ACCESSORY_UI_DEFS } from '@/world/avatars/accessory-ui-defs.js';
|
||||
import { genId } from '@/utility/id.js';
|
||||
|
||||
const $i = ensureSignin();
|
||||
|
||||
|
|
@ -217,6 +230,22 @@ function updateAvatarOption() {
|
|||
controller.updateAvatar(avatar.value);
|
||||
}
|
||||
|
||||
async function addAccessory() {
|
||||
const { canceled, result: type } = await os.select({
|
||||
title: 'select',
|
||||
items: Object.entries(ACCESSORY_SCHEMA_DEFS).map(([k, v]) => ({ label: AVATAR_ACCESSORY_UI_DEFS[k].name, value: k })),
|
||||
});
|
||||
if (canceled || type == null) return;
|
||||
|
||||
avatar.value!.accessories.push({
|
||||
id: genId(),
|
||||
type,
|
||||
options: deepClone(ACCESSORY_SCHEMA_DEFS[type].options.default),
|
||||
});
|
||||
|
||||
updateAvatarOption();
|
||||
}
|
||||
|
||||
function ok() {
|
||||
emit('ok', {
|
||||
avatar: deepClone(avatar.value!),
|
||||
|
|
|
|||
13
packages/frontend/src/world/avatars/accessories/mikan.ui.ts
Normal file
13
packages/frontend/src/world/avatars/accessories/mikan.ui.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { defineAccessoryUi } from '../defineAccessoryUi.js';
|
||||
import type { mikan_schema } from 'misskey-world/src/avatars/accessories/mikan.schema.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
export const mikan_ui = defineAccessoryUi<typeof mikan_schema>({
|
||||
name: i18n.ts._miWorld._avatarAccessories.mikan,
|
||||
options: {},
|
||||
});
|
||||
20
packages/frontend/src/world/avatars/accessories/mug.ui.ts
Normal file
20
packages/frontend/src/world/avatars/accessories/mug.ui.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { defineAccessoryUi } from '../defineAccessoryUi.js';
|
||||
import type { mug_schema } from 'misskey-world/src/avatars/accessories/mug.schema.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
export const mug_ui = defineAccessoryUi<typeof mug_schema>({
|
||||
name: i18n.ts._miWorld._avatarAccessories.mug,
|
||||
options: {
|
||||
bodyMat: {
|
||||
label: i18n.ts._miWorld._avatarAccessories._mug.bodyMat,
|
||||
},
|
||||
liquidMat: {
|
||||
label: i18n.ts._miWorld._avatarAccessories._mug.liquidMat,
|
||||
},
|
||||
},
|
||||
});
|
||||
21
packages/frontend/src/world/avatars/accessory-ui-defs.ts
Normal file
21
packages/frontend/src/world/avatars/accessory-ui-defs.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { mug_ui } from './accessories/mug.ui.js';
|
||||
import { mikan_ui } from './accessories/mikan.ui.js';
|
||||
import type { AccessoryUiDef } from './defineAccessoryUi.js';
|
||||
|
||||
export const AVATAR_ACCESSORY_UI_DEFS = {
|
||||
mug: mug_ui,
|
||||
mikan: mikan_ui,
|
||||
} as Record<string, AccessoryUiDef>;
|
||||
|
||||
export function getAccessoryUiDef(type: string): AccessoryUiDef {
|
||||
const def = AVATAR_ACCESSORY_UI_DEFS[type as keyof typeof AVATAR_ACCESSORY_UI_DEFS];
|
||||
if (def == null) {
|
||||
throw new Error(`Unrecognized accessory type: ${type}`);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
28
packages/frontend/src/world/avatars/defineAccessoryUi.ts
Normal file
28
packages/frontend/src/world/avatars/defineAccessoryUi.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { AccessorySchemaDef, AvatarAccessoryOptionsSchema, BooleanOptionSchema, ColorOptionSchema, EnumOptionSchema, LightOptionSchema, MaterialOptionSchema, NumberOptionSchema, RangeOptionSchema, StringOptionSchema } from 'misskey-world/src/avatars/accessory.js';
|
||||
|
||||
type GetOptionsSchemaUiDef<T extends AvatarAccessoryOptionsSchema> = {
|
||||
[K in keyof T]:
|
||||
T[K] extends NumberOptionSchema ? { label: string; } :
|
||||
T[K] extends BooleanOptionSchema ? { label: string; } :
|
||||
T[K] extends StringOptionSchema ? { label: string; } :
|
||||
T[K] extends ColorOptionSchema ? { label: string; } :
|
||||
T[K] extends MaterialOptionSchema ? { label: string; } :
|
||||
T[K] extends LightOptionSchema ? { label: string; } :
|
||||
T[K] extends EnumOptionSchema ? { label: string; enum: Record<T[K]['enum'][number]['value'], { label: string; }>; } :
|
||||
T[K] extends RangeOptionSchema ? { label: string; } :
|
||||
never;
|
||||
};
|
||||
|
||||
export type AccessoryUiDef<Schema extends AccessorySchemaDef = AccessorySchemaDef> = {
|
||||
name: string;
|
||||
options: GetOptionsSchemaUiDef<Schema['options']['schema']>;
|
||||
};
|
||||
|
||||
export function defineAccessoryUi<const Schema extends AccessoryUiDef<any>>(def: AccessoryUiDef<Schema>): AccessoryUiDef<Schema> {
|
||||
return def;
|
||||
}
|
||||
|
|
@ -9,5 +9,12 @@ import { i18n } from '@/i18n.js';
|
|||
|
||||
export const mug_ui = defineObjectUi<typeof mug_schema>({
|
||||
name: i18n.ts._miRoom._objects.mug,
|
||||
options: {},
|
||||
options: {
|
||||
bodyMat: {
|
||||
label: i18n.ts._miRoom._objects._mug.bodyMat,
|
||||
},
|
||||
liquidMat: {
|
||||
label: i18n.ts._miRoom._objects._mug.liquidMat,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13319,6 +13319,14 @@ export interface Locale extends ILocale {
|
|||
* アバター
|
||||
*/
|
||||
"avatar": string;
|
||||
/**
|
||||
* 高度なアレンジ
|
||||
*/
|
||||
"advancedCustomize": string;
|
||||
/**
|
||||
* アクセサリーを追加
|
||||
*/
|
||||
"attachAccessory": string;
|
||||
"_avatars": {
|
||||
"_default": {
|
||||
/**
|
||||
|
|
@ -13335,6 +13343,26 @@ export interface Locale extends ILocale {
|
|||
"mouth": string;
|
||||
};
|
||||
};
|
||||
"_avatarAccessories": {
|
||||
/**
|
||||
* マグカップ
|
||||
*/
|
||||
"mug": string;
|
||||
"_mug": {
|
||||
/**
|
||||
* コップの素材
|
||||
*/
|
||||
"bodyMat": string;
|
||||
/**
|
||||
* 液体の素材
|
||||
*/
|
||||
"liquidMat": string;
|
||||
};
|
||||
/**
|
||||
* みかん
|
||||
*/
|
||||
"mikan": string;
|
||||
};
|
||||
};
|
||||
"_miRoom": {
|
||||
/**
|
||||
|
|
@ -14071,6 +14099,16 @@ export interface Locale extends ILocale {
|
|||
* マグカップ
|
||||
*/
|
||||
"mug": string;
|
||||
"_mug": {
|
||||
/**
|
||||
* コップの素材
|
||||
*/
|
||||
"bodyMat": string;
|
||||
/**
|
||||
* 液体の素材
|
||||
*/
|
||||
"liquidMat": string;
|
||||
};
|
||||
/**
|
||||
* ニュートンクレードル
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,7 +7,17 @@ import { defineAccessorySchema } from '../accessory.js';
|
|||
export const mug_schema = defineAccessorySchema({
|
||||
id: 'mug',
|
||||
options: {
|
||||
schema: {},
|
||||
default: {},
|
||||
schema: {
|
||||
bodyMat: {
|
||||
type: 'material',
|
||||
},
|
||||
liquidMat: {
|
||||
type: 'material',
|
||||
},
|
||||
},
|
||||
default: {
|
||||
bodyMat: { color: [0.8, 0.8, 0.8], roughness: 0, metallic: 0 },
|
||||
liquidMat: { color: [0.033, 0.013, 0.005], roughness: 0, metallic: 0 },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ import { mug_schema } from './accessories/mug.schema.js';
|
|||
import { mikan_schema } from './accessories/mikan.schema.js';
|
||||
import type { AccessorySchemaDef } from './accessory.js';
|
||||
|
||||
export const OBJECT_SCHEMA_DEFS = {
|
||||
export const ACCESSORY_SCHEMA_DEFS = {
|
||||
mug: mug_schema,
|
||||
mikan: mikan_schema,
|
||||
} as Record<string, AccessorySchemaDef<any>>;
|
||||
|
||||
export function getAccessorySchemaDef(type: string): AccessorySchemaDef {
|
||||
const def = OBJECT_SCHEMA_DEFS[type as keyof typeof OBJECT_SCHEMA_DEFS];
|
||||
const def = ACCESSORY_SCHEMA_DEFS[type as keyof typeof ACCESSORY_SCHEMA_DEFS];
|
||||
if (def == null) {
|
||||
throw new Error(`Unrecognized accessory type: ${type}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,18 @@ import { defineObjectSchema } from '../object.js';
|
|||
export const mug_schema = defineObjectSchema({
|
||||
id: 'mug',
|
||||
options: {
|
||||
schema: {},
|
||||
default: {},
|
||||
schema: {
|
||||
bodyMat: {
|
||||
type: 'material',
|
||||
},
|
||||
liquidMat: {
|
||||
type: 'material',
|
||||
},
|
||||
},
|
||||
default: {
|
||||
bodyMat: { color: [0.8, 0.8, 0.8], roughness: 0, metallic: 0 },
|
||||
liquidMat: { color: [0.033, 0.013, 0.005], roughness: 0, metallic: 0 },
|
||||
},
|
||||
},
|
||||
placement: 'top',
|
||||
hasCollisions: false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue