From 10efcb465ca76dc6e1b201fd81047e01eeb80e7a Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Thu, 3 Oct 2024 23:09:17 +0800 Subject: [PATCH 01/10] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E5=9B=BD?= =?UTF-8?q?=E9=99=85=E5=8C=96=20&=20=E4=BC=98=E5=8C=96=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ScreenLoading/style.ts | 1 - src/features/AgentViewer/style.ts | 6 +++--- src/libs/emoteController/motionController.ts | 5 +++-- src/locales/default/role.ts | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/ScreenLoading/style.ts b/src/components/ScreenLoading/style.ts index e8095f44..bf797f56 100644 --- a/src/components/ScreenLoading/style.ts +++ b/src/components/ScreenLoading/style.ts @@ -5,7 +5,6 @@ export const useStyles = createStyles(({ css }) => ({ position: relative; width: 100%; height: 100%; - background-color: #000; #loading-screen { width: 150px; diff --git a/src/features/AgentViewer/style.ts b/src/features/AgentViewer/style.ts index a6b3816c..8aa78e0c 100644 --- a/src/features/AgentViewer/style.ts +++ b/src/features/AgentViewer/style.ts @@ -10,6 +10,9 @@ export const useStyles = createStyles(({ css, token }) => ({ width: 100%; height: 100%; min-height: 0; + + background-color: ${rgba(token.colorBgLayout, 0.2)}; + backdrop-filter: saturate(180%) blur(2px); `, toolbar: css` position: absolute; @@ -26,9 +29,6 @@ export const useStyles = createStyles(({ css, token }) => ({ max-width: 100%; height: 100%; max-height: 100%; - - background-color: ${rgba(token.colorBgLayout, 0.2)}; - backdrop-filter: saturate(180%) blur(8px); `, canvas: css` display: block; diff --git a/src/libs/emoteController/motionController.ts b/src/libs/emoteController/motionController.ts index 84a3d089..5214886e 100644 --- a/src/libs/emoteController/motionController.ts +++ b/src/libs/emoteController/motionController.ts @@ -25,8 +25,9 @@ export class MotionController { * @param motion */ public playMotion(motion: MotionPresetName, loop: boolean) { + console.log('motion', motion); const { type, url } = this.getMotionInfo(motion); - this.motionManager.loadMotionUrl(type, url, loop); + if (type && url) this.motionManager.loadMotionUrl(type, url, loop); } public playMotionUrl(fileType: MotionFileType, url: string, loop: boolean) { @@ -34,7 +35,7 @@ export class MotionController { } public getMotionInfo(motion: MotionPresetName) { - return motionPresetMap[motion]; + return motionPresetMap[motion] || motionPresetMap.idle; } public stopMotion() { diff --git a/src/locales/default/role.ts b/src/locales/default/role.ts index 857476bf..31e4991e 100644 --- a/src/locales/default/role.ts +++ b/src/locales/default/role.ts @@ -59,6 +59,7 @@ export default { leg: '腿部', chest: '胸部', belly: '腹部', + buttocks: '臀部', }, expression: { natural: '自然', From 778075ebd0a2c5ccd33a0b62f4a988229c1ae183 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 4 Oct 2024 09:46:39 +0800 Subject: [PATCH 02/10] :sparkles: feat: page top loading --- package.json | 1 + src/app/layout.tsx | 6 +++++- src/components/NProgress/index.tsx | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/components/NProgress/index.tsx diff --git a/package.json b/package.json index e3f2190f..20c54aec 100644 --- a/package.json +++ b/package.json @@ -104,6 +104,7 @@ "nanoid": "^5.0.7", "next": "^14.2.13", "next-pwa": "^5.6.0", + "nextjs-toploader": "^3.6.15", "numeral": "^2.0.6", "openai": "^4.63.0", "pino": "^9.4.0", diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8cd5742d..c20a23d8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,7 @@ import { Analytics } from '@vercel/analytics/react'; import { PropsWithChildren } from 'react'; +import NProgress from '@/components/NProgress'; import Layout from '@/layout'; import StyleRegistry from './StyleRegistry'; @@ -12,7 +13,10 @@ const RootLayout = ({ children }: PropsWithChildren) => { - {children} + + + {children} + diff --git a/src/components/NProgress/index.tsx b/src/components/NProgress/index.tsx new file mode 100644 index 00000000..3820eefc --- /dev/null +++ b/src/components/NProgress/index.tsx @@ -0,0 +1,12 @@ +'use client'; + +import { useTheme } from 'antd-style'; +import NextTopLoader from 'nextjs-toploader'; +import { memo } from 'react'; + +const NProgress = memo(() => { + const theme = useTheme(); + return ; +}); + +export default NProgress; From 6cc02e9d062b7e61b828fe379e950a8b226f375d Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 4 Oct 2024 10:14:42 +0800 Subject: [PATCH 03/10] =?UTF-8?q?:sparkles:=20feat:=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=94=B7=E6=80=A7=E8=A7=92=E8=89=B2=E7=9A=84=20motion=20?= =?UTF-8?q?=E9=A2=84=E8=AE=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/touch.tsx | 16 ++++++++++++++++ src/libs/emoteController/motionPresetMap.ts | 12 ++++++++++++ src/locales/default/role.ts | 9 +++++++++ 3 files changed, 37 insertions(+) diff --git a/src/constants/touch.tsx b/src/constants/touch.tsx index 99f63fc7..cb2fe371 100644 --- a/src/constants/touch.tsx +++ b/src/constants/touch.tsx @@ -150,76 +150,92 @@ export const DEFAULT_TOUCH_ACTION_CONFIG_MALE: TouchActionConfig = { { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.headAction.neutralA', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.headAction.neutralB', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.headAction.neutralC', + motion: MotionPresetName.MaleHappy, }, ], [TouchAreaEnum.Arm]: [ { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.armAction.neutralA', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.armAction.neutralB', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.armAction.neutralC', + motion: MotionPresetName.MaleHappy, }, ], [TouchAreaEnum.Leg]: [ { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.legAction.neutralA', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.legAction.neutralB', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.Angry, text: 'touch.maleAction.legAction.angryA', + motion: MotionPresetName.MaleAngry, }, ], [TouchAreaEnum.Chest]: [ { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.chestAction.neutralA', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.BlinkLeft, text: 'touch.maleAction.chestAction.blinkLeftA', + motion: MotionPresetName.MaleHappy, }, ], [TouchAreaEnum.Belly]: [ { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.bellyAction.neutralA', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.Happy, text: 'touch.maleAction.bellyAction.happyA', + motion: MotionPresetName.MaleHappy, }, { expression: VRMExpressionPresetName.Neutral, text: 'touch.maleAction.bellyAction.neutralB', + motion: MotionPresetName.MaleHappy, }, ], [TouchAreaEnum.Buttocks]: [ { expression: VRMExpressionPresetName.Surprised, text: 'touch.maleAction.buttocksAction.surprisedA', + motion: MotionPresetName.MaleAngry, }, { expression: VRMExpressionPresetName.Angry, text: 'touch.maleAction.buttocksAction.angryA', + motion: MotionPresetName.MaleAngry, }, ], }; diff --git a/src/libs/emoteController/motionPresetMap.ts b/src/libs/emoteController/motionPresetMap.ts index 60b003cb..69d48f4c 100644 --- a/src/libs/emoteController/motionPresetMap.ts +++ b/src/libs/emoteController/motionPresetMap.ts @@ -16,6 +16,8 @@ export enum MotionPresetName { FemaleCoverChest = 'female_cover_chest', FemaleStand = 'female_stand', FemaleStandMix = 'female_stand_mix', + MaleHappy = 'male_happy', + MaleAngry = 'male_angry', } export const motionPresetMap: Record< @@ -36,11 +38,21 @@ export const motionPresetMap: Record< type: MotionFileType.FBX, name: 'Female/Happy', }, + male_happy: { + url: 'https://r2.vidol.chat/animations/c9ccf37e-b96c-11e4-a802-0aaa78deedf9.fbx', + type: MotionFileType.FBX, + name: 'Male/Happy', + }, female_angry: { url: 'https://r2.vidol.chat/animations/c9c98b02-b96c-11e4-a802-0aaa78deedf9.fbx', type: MotionFileType.FBX, name: 'Female/Angry', }, + male_angry: { + url: 'https://r2.vidol.chat/animations/c9c916ce-b96c-11e4-a802-0aaa78deedf9.fbx', + type: MotionFileType.FBX, + name: 'Male/Angry', + }, female_greeting: { url: 'https://r2.vidol.chat/animations/c9c7996a-b96c-11e4-a802-0aaa78deedf9.fbx', type: MotionFileType.FBX, diff --git a/src/locales/default/role.ts b/src/locales/default/role.ts index 31e4991e..904dcf12 100644 --- a/src/locales/default/role.ts +++ b/src/locales/default/role.ts @@ -119,6 +119,11 @@ export default { relaxedA: '醒醒,我们之间没有结果的!', relaxedB: '讨厌!我可要生气啦!', }, + buttocksAction: { + surprisedA: '啊!你在摸哪里?!', + angryA: '你这个变态!离我远点!', + embarrassedA: '呜...不要这样...', + }, }, maleAction: { headAction: { @@ -145,6 +150,10 @@ export default { happyA: '别挠痒痒,小心我笑出腹肌', neutralB: '看到我这团腹肌了吗?它们只是藏得比较深罢了', }, + buttocksAction: { + surprisedA: '嘿!注意你的手!', + angryA: '再碰我就揍你了!', + }, }, }, info: { From e8c76efbbc1edfbdaa41c361b423f5c698576988 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 4 Oct 2024 10:55:42 +0800 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=A4=B4?= =?UTF-8?q?=E9=83=A8=E8=A7=A6=E6=91=B8=E7=82=B9=E5=87=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/vrmViewer/viewer.ts | 103 ++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/src/libs/vrmViewer/viewer.ts b/src/libs/vrmViewer/viewer.ts index c0da95fa..f8e2c1f6 100644 --- a/src/libs/vrmViewer/viewer.ts +++ b/src/libs/vrmViewer/viewer.ts @@ -1,7 +1,17 @@ import { VRMHumanBoneName } from '@pixiv/three-vrm'; import { Parser } from 'mmd-parser'; import * as THREE from 'three'; -import { Audio, GridHelper, Mesh, MeshLambertMaterial, PlaneGeometry } from 'three'; +import { + Audio, + Box3, + BoxGeometry, + GridHelper, + Mesh, + MeshBasicMaterial, + MeshLambertMaterial, + PlaneGeometry, + Vector3, +} from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; import { MotionFileType } from '@/libs/emoteController/type'; @@ -29,6 +39,8 @@ export class Viewer { private _boundHandleClick: (event: MouseEvent) => void; private _onBodyTouch?: (area: TouchAreaEnum) => void; private _isDancing: boolean = false; + private _headHitbox?: THREE.Mesh; + private _headHitboxSize: Vector3 = new Vector3(0.2, 0.25, 0.2); constructor() { this.isReady = false; @@ -127,6 +139,10 @@ export class Viewer { this.resetCamera(); }); + if (this.model?.vrm) { + this.createHeadHitbox(); + } + // 重新设置事件监听器 if (this._canvas) { this._canvas.addEventListener('click', this._boundHandleClick, false); @@ -135,6 +151,12 @@ export class Viewer { public unloadVRM(): void { if (this.model?.vrm) { + if (this._headHitbox) { + this._headHitbox.parent?.remove(this._headHitbox); + this._headHitbox.geometry.dispose(); + (this._headHitbox.material as MeshBasicMaterial).dispose(); + this._headHitbox = undefined; + } this._scene.remove(this.model.vrm.scene); this.model?.unLoadVrm(); } @@ -287,6 +309,10 @@ export class Viewer { this._cameraHelper.update(); } + if (this.model?.vrm && this._headHitbox) { + this.updateHeadHitbox(); + } + if (this._renderer && this._camera) { this._renderer.render(this._scene, this._camera); } @@ -307,25 +333,38 @@ export class Viewer { } private handleClick = (event: MouseEvent) => { - if (this._isDancing) return; // 如果正在跳舞,不处理点击事件 + if (this._isDancing) return; const intersects = this.handleRaycasterIntersection(event); if (!intersects || intersects.length === 0) return; + // 检查是否点击了头部 hitbox + const headHitboxIntersect = intersects.find( + (intersect) => intersect.object === this._headHitbox, + ); + if (headHitboxIntersect) { + this.handleBodyPartClick(VRMHumanBoneName.Head); + return; + } + const intersectedPoint = intersects[0].point; const closestBone = this.findClosestBone(intersectedPoint); + console.log('closestBone', closestBone, intersects); + if (closestBone) { this.handleBodyPartClick(closestBone); } }; - private handleMouseMove(event: MouseEvent) { - const intersects = this.handleRaycasterIntersection(event); - - if (this._canvas) { - this._canvas.style.cursor = intersects && intersects.length > 0 ? 'pointer' : 'default'; - } + private getHeadBones(): VRMHumanBoneName[] { + return [ + VRMHumanBoneName.Head, + VRMHumanBoneName.Neck, + VRMHumanBoneName.LeftEye, + VRMHumanBoneName.RightEye, + VRMHumanBoneName.Jaw, + ]; } private findClosestBone(point: THREE.Vector3): VRMHumanBoneName | null { @@ -335,8 +374,7 @@ export class Viewer { let closestWeightedDistance = Infinity; const mainBones: VRMHumanBoneName[] = [ - VRMHumanBoneName.Head, - VRMHumanBoneName.Neck, + ...this.getHeadBones(), VRMHumanBoneName.Chest, VRMHumanBoneName.Spine, VRMHumanBoneName.Hips, @@ -357,6 +395,10 @@ export class Viewer { const getBoneWeight = (boneName: VRMHumanBoneName): number => { switch (boneName) { case VRMHumanBoneName.Head: + case VRMHumanBoneName.LeftEye: + case VRMHumanBoneName.RightEye: + case VRMHumanBoneName.Jaw: + return 2; // 增加头部相关骨骼的权重 case VRMHumanBoneName.Chest: case VRMHumanBoneName.Spine: case VRMHumanBoneName.Hips: @@ -399,11 +441,12 @@ export class Viewer { } private mapBoneNameToTouchArea(boneName: VRMHumanBoneName): TouchAreaEnum | null { - switch (boneName) { - case VRMHumanBoneName.Head: - case VRMHumanBoneName.Neck: - return TouchAreaEnum.Head; + const headBones = this.getHeadBones(); + if (headBones.includes(boneName)) { + return TouchAreaEnum.Head; + } + switch (boneName) { case VRMHumanBoneName.LeftUpperArm: case VRMHumanBoneName.LeftLowerArm: case VRMHumanBoneName.LeftHand: @@ -433,4 +476,36 @@ export class Viewer { return null; } } + + private createHeadHitbox() { + if (!this.model?.vrm) return; + + const headBone = this.model.vrm.humanoid.getNormalizedBoneNode('head'); + if (!headBone) return; + + const geometry = new BoxGeometry( + this._headHitboxSize.x, + this._headHitboxSize.y, + this._headHitboxSize.z, + ); + const material = new MeshBasicMaterial({ visible: false }); + this._headHitbox = new Mesh(geometry, material); + + headBone.add(this._headHitbox); + + // 调整 hitbox 的位置,使其位于头部中心 + this._headHitbox.position.set(0, this._headHitboxSize.y / 2, 0); + } + + private updateHeadHitbox() { + if (!this.model?.vrm || !this._headHitbox) return; + + const headBone = this.model.vrm.humanoid.getNormalizedBoneNode('head'); + if (!headBone) return; + + // 更新 hitbox 的缩放以匹配模型的缩放 + const scale = new Vector3(); + headBone.getWorldScale(scale); + this._headHitbox.scale.copy(scale); + } } From e55cffe92dcbcb7bff0f1ea697c1ff067c70e80c Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 4 Oct 2024 11:06:20 +0800 Subject: [PATCH 05/10] =?UTF-8?q?fix:=20=E8=B0=83=E6=95=B4=E5=A4=B4?= =?UTF-8?q?=E9=83=A8=E5=8C=BA=E5=9F=9F=E7=82=B9=E5=87=BB=E7=9B=92=E5=AD=90?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/vrmViewer/viewer.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libs/vrmViewer/viewer.ts b/src/libs/vrmViewer/viewer.ts index f8e2c1f6..3a0a3c33 100644 --- a/src/libs/vrmViewer/viewer.ts +++ b/src/libs/vrmViewer/viewer.ts @@ -488,13 +488,18 @@ export class Viewer { this._headHitboxSize.y, this._headHitboxSize.z, ); - const material = new MeshBasicMaterial({ visible: false }); + const material = new MeshBasicMaterial({ + color: 0x00ff00, // 绿色 + transparent: true, + opacity: 0.5, + visible: true, + }); this._headHitbox = new Mesh(geometry, material); headBone.add(this._headHitbox); - // 调整 hitbox 的位置,使其位于头部中心 - this._headHitbox.position.set(0, this._headHitboxSize.y / 2, 0); + // 调整 hitbox 的位置,使其位于头部中心偏下的位置 + this._headHitbox.position.set(0, this._headHitboxSize.y * 0.3, 0); } private updateHeadHitbox() { From cc7745dea5d31dd7685f8bbf030e5fedc57f1845 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 4 Oct 2024 11:09:38 +0800 Subject: [PATCH 06/10] chore: visible false --- src/libs/vrmViewer/viewer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/vrmViewer/viewer.ts b/src/libs/vrmViewer/viewer.ts index 3a0a3c33..018419d5 100644 --- a/src/libs/vrmViewer/viewer.ts +++ b/src/libs/vrmViewer/viewer.ts @@ -492,7 +492,7 @@ export class Viewer { color: 0x00ff00, // 绿色 transparent: true, opacity: 0.5, - visible: true, + visible: false, }); this._headHitbox = new Mesh(geometry, material); From 7614c4ad2803eecab383f866c1f3891178a0d886 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 4 Oct 2024 23:28:41 +0800 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/bg-BG/common.json | 1 + locales/bg-BG/role.json | 10 +++ locales/de-DE/common.json | 1 + locales/de-DE/role.json | 10 +++ locales/en-US/common.json | 1 + locales/en-US/role.json | 10 +++ locales/es-ES/common.json | 1 + locales/es-ES/role.json | 10 +++ locales/fr-FR/common.json | 1 + locales/fr-FR/role.json | 10 +++ locales/it-IT/common.json | 1 + locales/it-IT/role.json | 10 +++ locales/ja-JP/common.json | 1 + locales/ja-JP/role.json | 10 +++ locales/ko-KR/common.json | 1 + locales/ko-KR/role.json | 10 +++ locales/nl-NL/common.json | 1 + locales/nl-NL/role.json | 10 +++ locales/pl-PL/common.json | 1 + locales/pl-PL/role.json | 10 +++ locales/pt-BR/common.json | 1 + locales/pt-BR/role.json | 10 +++ locales/ru-RU/common.json | 1 + locales/ru-RU/role.json | 10 +++ locales/tr-TR/common.json | 1 + locales/tr-TR/role.json | 10 +++ locales/vi-VN/common.json | 1 + locales/vi-VN/role.json | 10 +++ locales/zh-CN/common.json | 8 +- locales/zh-CN/role.json | 12 ++- locales/zh-TW/common.json | 1 + locales/zh-TW/role.json | 10 +++ src/layout/Header/Actions/Discord.tsx | 2 +- src/layout/Header/Actions/Github.tsx | 2 +- src/layout/Header/Actions/Support.tsx | 76 +++++++++++++++++++ .../{ThemeMode/index.tsx => ThemeMode.tsx} | 0 src/layout/Header/index.tsx | 2 + src/locales/default/common.ts | 8 +- 38 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 src/layout/Header/Actions/Support.tsx rename src/layout/Header/Actions/{ThemeMode/index.tsx => ThemeMode.tsx} (100%) diff --git a/locales/bg-BG/common.json b/locales/bg-BG/common.json index b2569c56..bfde7bd1 100644 --- a/locales/bg-BG/common.json +++ b/locales/bg-BG/common.json @@ -1,6 +1,7 @@ { "cancel": "Отмяна", "close": "Затвори", + "community": "Обществена подкрепа", "confirm": "Потвърдете", "confirmDel": "Сигурни ли сте, че искате да изтриете?", "defaultAssistant": "По подразбиране асистент", diff --git a/locales/bg-BG/role.json b/locales/bg-BG/role.json index 7fcb8947..84d7ee54 100644 --- a/locales/bg-BG/role.json +++ b/locales/bg-BG/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Ръка", "belly": "Корем", + "buttocks": "задник", "chest": "Гърди", "head": "Глава", "leg": "Крак" @@ -143,6 +144,11 @@ "relaxedB": "Дразнещо! Ще се ядосам!", "surprisedA": "Случайно ли ме докосна?" }, + "buttocksAction": { + "angryA": "Ти си луд! Далеч от мен!", + "embarrassedA": "Ох... не прави така...", + "surprisedA": "Ох! Къде ме пипаш?!" + }, "chestAction": { "angryA": "Не можеш да ме тормозиш така! Бързо махни ръката си!", "angryB": "Има ли тук перверзник, който постоянно ме гали?", @@ -179,6 +185,10 @@ "neutralA": "Коремните ми мускули са просто дълбоко скрита сила", "neutralB": "Виждаш ли тези коремни мускули? Те просто са скрити дълбоко." }, + "buttocksAction": { + "angryA": "Ако ме пипнеш пак, ще те ударя!", + "surprisedA": "Хей! Внимавай с ръцете си!" + }, "chestAction": { "blinkLeftA": "Ела, опри се на мускулите ми!", "neutralA": "Това са просто мускулите, които съм изградил с ежедневни тренировки, няма нищо изненадващо." diff --git a/locales/de-DE/common.json b/locales/de-DE/common.json index 6fe36d92..6502dbee 100644 --- a/locales/de-DE/common.json +++ b/locales/de-DE/common.json @@ -1,6 +1,7 @@ { "cancel": "Abbrechen", "close": "Schließen", + "community": "Gemeinschaftsunterstützung", "confirm": "Bestätigen", "confirmDel": "Möchten Sie wirklich löschen?", "defaultAssistant": "Standardassistent", diff --git a/locales/de-DE/role.json b/locales/de-DE/role.json index db60c623..50943e39 100644 --- a/locales/de-DE/role.json +++ b/locales/de-DE/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Arm", "belly": "Bauch", + "buttocks": "Gesäß", "chest": "Brust", "head": "Kopf", "leg": "Bein" @@ -143,6 +144,11 @@ "relaxedB": "Das nervt! Ich werde wütend!", "surprisedA": "Das war doch ein Versehen..." }, + "buttocksAction": { + "angryA": "Du Perverse! Halt Abstand von mir!", + "embarrassedA": "Ähm... mach das nicht...", + "surprisedA": "Ah! Wo fasst du hin?!" + }, "chestAction": { "angryA": "Du kannst mich nicht so ärgern! Nimm deine Hand weg!", "angryB": "Hallo? Hier ist ein Pervertierter, der mich ständig anfasst!", @@ -179,6 +185,10 @@ "neutralA": "Meine Bauchmuskeln sind nur tief versteckte innere Kraft.", "neutralB": "Siehst du meine Bauchmuskeln? Sie sind nur etwas tiefer versteckt." }, + "buttocksAction": { + "angryA": "Wenn du mich nochmal anfasst, kriegst du Ärger!", + "surprisedA": "Hey! Pass auf, wo du hinfasst!" + }, "chestAction": { "blinkLeftA": "Komm, lehn dich an meine Brustmuskeln!", "neutralA": "Das sind nur die Brustmuskeln, die ich im Alltag trainiert habe, da gibt es nichts, worüber man überrascht sein sollte." diff --git a/locales/en-US/common.json b/locales/en-US/common.json index 6455188d..d6415dd2 100644 --- a/locales/en-US/common.json +++ b/locales/en-US/common.json @@ -1,6 +1,7 @@ { "cancel": "Cancel", "close": "Close", + "community": "Community Support", "confirm": "Confirm", "confirmDel": "Are you sure you want to delete?", "defaultAssistant": "Default Assistant", diff --git a/locales/en-US/role.json b/locales/en-US/role.json index 64df7d74..cf49603b 100644 --- a/locales/en-US/role.json +++ b/locales/en-US/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Arm", "belly": "Belly", + "buttocks": "buttocks", "chest": "Chest", "head": "Head", "leg": "Leg" @@ -143,6 +144,11 @@ "relaxedB": "Stop it! I'm going to get angry!", "surprisedA": "That was an accidental touch, right...?" }, + "buttocksAction": { + "angryA": "You pervert! Stay away from me!", + "embarrassedA": "Ugh... don't do that...", + "surprisedA": "Ah! Where are you touching?!" + }, "chestAction": { "angryA": "You can't bully me like this! Take your hand away!", "angryB": "Is this a prank? There's a creep touching me!", @@ -179,6 +185,10 @@ "neutralA": "My abs are just hidden strength from training.", "neutralB": "Do you see my abs? They're just hiding a bit deeper." }, + "buttocksAction": { + "angryA": "If you touch me again, I'll hit you!", + "surprisedA": "Hey! Watch your hands!" + }, "chestAction": { "blinkLeftA": "Come on, lean on my chest!", "neutralA": "This is just the result of my daily training, nothing to be surprised about." diff --git a/locales/es-ES/common.json b/locales/es-ES/common.json index 350045ab..89916936 100644 --- a/locales/es-ES/common.json +++ b/locales/es-ES/common.json @@ -1,6 +1,7 @@ { "cancel": "Cancelar", "close": "Cerrar", + "community": "Soporte comunitario", "confirm": "Confirmar", "confirmDel": "¿Estás seguro de que deseas eliminar?", "defaultAssistant": "Asistente predeterminado", diff --git a/locales/es-ES/role.json b/locales/es-ES/role.json index 18c5e588..e82eb63e 100644 --- a/locales/es-ES/role.json +++ b/locales/es-ES/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Brazo", "belly": "Vientre", + "buttocks": "nalgas", "chest": "Pecho", "head": "Cabeza", "leg": "Pierna" @@ -143,6 +144,11 @@ "relaxedB": "¡Qué molesto! ¡Voy a enojarme!", "surprisedA": "¿Fue un accidente...?" }, + "buttocksAction": { + "angryA": "¡Eres un pervertido! ¡Aléjate de mí!", + "embarrassedA": "Uh... no hagas eso...", + "surprisedA": "¡Ah! ¿Dónde estás tocando?!" + }, "chestAction": { "angryA": "¡No puedes acosarme así! ¡Saca tu mano!", "angryB": "¿Es un 010? ¡Hay un pervertido tocándome!", @@ -179,6 +185,10 @@ "neutralA": "Mis abdominales son solo un poder oculto que he cultivado", "neutralB": "¿Ves mis abdominales? Solo están un poco más ocultos." }, + "buttocksAction": { + "angryA": "¡Si me tocas de nuevo, te golpearé!", + "surprisedA": "¡Hey! ¡Cuidado con tu mano!" + }, "chestAction": { "blinkLeftA": "¡Vamos, apóyate en mi pecho!", "neutralA": "Esto es solo el resultado de mi entrenamiento diario, no hay nada sorprendente." diff --git a/locales/fr-FR/common.json b/locales/fr-FR/common.json index c442c369..59d7d086 100644 --- a/locales/fr-FR/common.json +++ b/locales/fr-FR/common.json @@ -1,6 +1,7 @@ { "cancel": "Annuler", "close": "Fermer", + "community": "Soutien communautaire", "confirm": "Confirmer", "confirmDel": "Êtes-vous sûr de vouloir supprimer ?", "defaultAssistant": "Assistant par défaut", diff --git a/locales/fr-FR/role.json b/locales/fr-FR/role.json index 93cf9420..1d872c91 100644 --- a/locales/fr-FR/role.json +++ b/locales/fr-FR/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Bras", "belly": "Ventre", + "buttocks": "fesses", "chest": "Poitrine", "head": "Tête", "leg": "Jambe" @@ -143,6 +144,11 @@ "relaxedB": "C'est agaçant ! Je vais me fâcher !", "surprisedA": "C'était un accident, non ?" }, + "buttocksAction": { + "angryA": "Tu es un pervers! Éloigne-toi de moi!", + "embarrassedA": "Euh... ne fais pas ça...", + "surprisedA": "Ah! Tu touches où?!" + }, "chestAction": { "angryA": "Tu ne peux pas me harceler comme ça ! Retire ta main !", "angryB": "Est-ce que c'est un pervers qui me touche ici ?", @@ -179,6 +185,10 @@ "neutralA": "Mes abdominaux cachent juste une force intérieure bien entraînée.", "neutralB": "Tu vois mes abdominaux ? Ils sont juste bien cachés." }, + "buttocksAction": { + "angryA": "Si tu me touches encore, je te frappe!", + "surprisedA": "Hé! Fais attention à tes mains!" + }, "chestAction": { "blinkLeftA": "Allez, repose-toi sur mes muscles !", "neutralA": "Ce ne sont que les muscles que j'ai développés, rien d'étonnant." diff --git a/locales/it-IT/common.json b/locales/it-IT/common.json index 709a66ae..c6c05509 100644 --- a/locales/it-IT/common.json +++ b/locales/it-IT/common.json @@ -1,6 +1,7 @@ { "cancel": "Annulla", "close": "Chiudi", + "community": "Supporto della comunità", "confirm": "Conferma", "confirmDel": "Sei sicuro di voler eliminare?", "defaultAssistant": "Assistente predefinito", diff --git a/locales/it-IT/role.json b/locales/it-IT/role.json index bb3517f1..559730e3 100644 --- a/locales/it-IT/role.json +++ b/locales/it-IT/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Braccio", "belly": "Pancia", + "buttocks": "natiche", "chest": "Petto", "head": "Testa", "leg": "Gamba" @@ -143,6 +144,11 @@ "relaxedB": "Che fastidio! Sto per arrabbiarmi!", "surprisedA": "È stato un contatto accidentale, vero..." }, + "buttocksAction": { + "angryA": "Sei un pervertito! Stai lontano da me!", + "embarrassedA": "Uff... non fare così...", + "surprisedA": "Ah! Dove stai toccando?!" + }, "chestAction": { "angryA": "Non puoi bullizzarmi così! Togliti la mano!", "angryB": "C'è un pervertito che continua a toccarmi!", @@ -179,6 +185,10 @@ "neutralA": "I miei addominali sono solo il risultato di un allenamento profondo", "neutralB": "Vedi i miei addominali? Sono solo ben nascosti!" }, + "buttocksAction": { + "angryA": "Se mi tocchi di nuovo, ti picchio!", + "surprisedA": "Ehi! Fai attenzione con le mani!" + }, "chestAction": { "blinkLeftA": "Dai, appoggiati ai miei muscoli!", "neutralA": "Questi sono solo i muscoli che ho sviluppato, non c'è nulla di cui sorprendersi." diff --git a/locales/ja-JP/common.json b/locales/ja-JP/common.json index fb7541f7..050e6847 100644 --- a/locales/ja-JP/common.json +++ b/locales/ja-JP/common.json @@ -1,6 +1,7 @@ { "cancel": "キャンセル", "close": "閉じる", + "community": "コミュニティサポート", "confirm": "確認", "confirmDel": "本当に削除してもよろしいですか?", "defaultAssistant": "デフォルトアシスタント", diff --git a/locales/ja-JP/role.json b/locales/ja-JP/role.json index 11d52a38..f35d6aae 100644 --- a/locales/ja-JP/role.json +++ b/locales/ja-JP/role.json @@ -114,6 +114,7 @@ "area": { "arm": "腕", "belly": "腹部", + "buttocks": "お尻", "chest": "胸部", "head": "頭部", "leg": "脚" @@ -143,6 +144,11 @@ "relaxedB": "嫌だ!怒るよ!", "surprisedA": "偶然触れたのかな…" }, + "buttocksAction": { + "angryA": "あなたは変態ね! 離れて!", + "embarrassedA": "うぅ...そんなことしないで...", + "surprisedA": "あっ! どこを触ってるの?!" + }, "chestAction": { "angryA": "そんな風に私をいじめないで!手を離して!", "angryB": "やや零?ここに変態がいる!", @@ -179,6 +185,10 @@ "neutralA": "私の腹筋はただ隠された内力を修行しているだけ", "neutralB": "私のこの腹筋見える?ただ深く隠れているだけだよ" }, + "buttocksAction": { + "angryA": "もう一度触ったら殴るぞ!", + "surprisedA": "おい! 手に気をつけろ!" + }, "chestAction": { "blinkLeftA": "さあ、兄の胸筋に寄りかかって!", "neutralA": "これは私の日常的な修行の成果の胸筋、驚くことはない。" diff --git a/locales/ko-KR/common.json b/locales/ko-KR/common.json index 7776d0b6..87aa3218 100644 --- a/locales/ko-KR/common.json +++ b/locales/ko-KR/common.json @@ -1,6 +1,7 @@ { "cancel": "취소", "close": "닫기", + "community": "커뮤니티 지원", "confirm": "확인", "confirmDel": "정말 삭제하시겠습니까?", "defaultAssistant": "기본 도우미", diff --git a/locales/ko-KR/role.json b/locales/ko-KR/role.json index 0529d85f..9e23f7d2 100644 --- a/locales/ko-KR/role.json +++ b/locales/ko-KR/role.json @@ -114,6 +114,7 @@ "area": { "arm": "팔", "belly": "배", + "buttocks": "엉덩이", "chest": "가슴", "head": "머리", "leg": "다리" @@ -143,6 +144,11 @@ "relaxedB": "싫어! 나 화낼 거야!", "surprisedA": "우연히 닿은 거겠지..." }, + "buttocksAction": { + "angryA": "너 미친 거 아니야! 나한테서 떨어져!", + "embarrassedA": "으... 그렇게 하지 마...", + "surprisedA": "아! 어디 만지고 있는 거야?!" + }, "chestAction": { "angryA": "이렇게 나를 괴롭히면 안 돼! 손을 치워!", "angryB": "여기 변태가 나를 만지고 있어!", @@ -179,6 +185,10 @@ "neutralA": "내 복근은 단련된 내력을 숨기고 있을 뿐이야", "neutralB": "내 복근 보이니? 그건 단지 깊이 숨겨져 있을 뿐이야" }, + "buttocksAction": { + "angryA": "다시 만지면 너한테 맞을 거야!", + "surprisedA": "이봐! 손 조심해!" + }, "chestAction": { "blinkLeftA": "자, 형의 가슴에 기대!", "neutralA": "이건 내가 평소에 단련한 가슴근육일 뿐이야, 놀랄 필요 없어." diff --git a/locales/nl-NL/common.json b/locales/nl-NL/common.json index af1919f9..3e2805e4 100644 --- a/locales/nl-NL/common.json +++ b/locales/nl-NL/common.json @@ -1,6 +1,7 @@ { "cancel": "Annuleren", "close": "Sluiten", + "community": "Gemeenschapssteun", "confirm": "Bevestigen", "confirmDel": "Weet je zeker dat je dit wilt verwijderen?", "defaultAssistant": "Standaardassistent", diff --git a/locales/nl-NL/role.json b/locales/nl-NL/role.json index 9d06b5f2..760de27e 100644 --- a/locales/nl-NL/role.json +++ b/locales/nl-NL/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Arm", "belly": "Buik", + "buttocks": "billen", "chest": "Borst", "head": "Hoofd", "leg": "Been" @@ -143,6 +144,11 @@ "relaxedB": "Vervelend! Ik ga boos worden!", "surprisedA": "Dat was per ongeluk, toch..." }, + "buttocksAction": { + "angryA": "Wat een pervert! Blijf van me af!", + "embarrassedA": "O... doe dat niet...", + "surprisedA": "Ah! Waar ben je aan het voelen?!" + }, "chestAction": { "angryA": "Je kunt me niet zo pesten! Haal je hand weg!", "angryB": "Is dit 010? Er is een pervert die me aanraakt!", @@ -179,6 +185,10 @@ "neutralA": "Mijn buikspieren zijn gewoon verborgen kracht die ik heb getraind.", "neutralB": "Zie je deze buikspieren? Ze zijn gewoon goed verborgen." }, + "buttocksAction": { + "angryA": "Als je me nog een keer aanraakt, krijg je klappen!", + "surprisedA": "Hé! Let op je handen!" + }, "chestAction": { "blinkLeftA": "Kom, leun op mijn borstspieren!", "neutralA": "Dit zijn gewoon de borstspieren die ik dagelijks train, er is niets om verbaasd over te zijn." diff --git a/locales/pl-PL/common.json b/locales/pl-PL/common.json index 5d90b3bf..d40aac74 100644 --- a/locales/pl-PL/common.json +++ b/locales/pl-PL/common.json @@ -1,6 +1,7 @@ { "cancel": "Anuluj", "close": "Zamknij", + "community": "Wsparcie społeczności", "confirm": "Potwierdź", "confirmDel": "Czy na pewno chcesz usunąć?", "defaultAssistant": "Domyślny asystent", diff --git a/locales/pl-PL/role.json b/locales/pl-PL/role.json index ee41ae27..76f2bc5b 100644 --- a/locales/pl-PL/role.json +++ b/locales/pl-PL/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Ramię", "belly": "Brzuch", + "buttocks": "pośladki", "chest": "Klatka piersiowa", "head": "Głowa", "leg": "Noga" @@ -143,6 +144,11 @@ "relaxedB": "Nienawidzę! Zaczynam się złościć!", "surprisedA": "To było przypadkowe, prawda?" }, + "buttocksAction": { + "angryA": "Ty zboczeńcu! Trzymaj się z daleka!", + "embarrassedA": "E... nie rób tak...", + "surprisedA": "A! Gdzie ty dotykasz?!" + }, "chestAction": { "angryA": "Nie możesz mnie tak traktować! Szybko zabierz rękę!", "angryB": "Czy to 010? Tutaj jest zboczeniec, który ciągle mnie głaszcze!", @@ -179,6 +185,10 @@ "neutralA": "Moje mięśnie brzucha to tylko ukryta siła.", "neutralB": "Widzisz te mięśnie brzucha? One są tylko głęboko ukryte." }, + "buttocksAction": { + "angryA": "Jeszcze raz mnie dotkniesz, to cię uderzę!", + "surprisedA": "Hej! Uważaj na ręce!" + }, "chestAction": { "blinkLeftA": "Chodź, oprzyj się na mojej klatce piersiowej!", "neutralA": "To tylko klatka piersiowa, którą osiągnąłem dzięki codziennym treningom, nie ma się czym dziwić." diff --git a/locales/pt-BR/common.json b/locales/pt-BR/common.json index 2c623309..09fb9468 100644 --- a/locales/pt-BR/common.json +++ b/locales/pt-BR/common.json @@ -1,6 +1,7 @@ { "cancel": "Cancelar", "close": "Fechar", + "community": "Suporte da Comunidade", "confirm": "Confirmar", "confirmDel": "Você tem certeza de que deseja excluir?", "defaultAssistant": "Assistente Padrão", diff --git a/locales/pt-BR/role.json b/locales/pt-BR/role.json index 8025e532..9eee1d2d 100644 --- a/locales/pt-BR/role.json +++ b/locales/pt-BR/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Braço", "belly": "Barriga", + "buttocks": "nádegas", "chest": "Peito", "head": "Cabeça", "leg": "Perna" @@ -143,6 +144,11 @@ "relaxedB": "Que chato! Estou começando a ficar brava!", "surprisedA": "Foi sem querer, certo..." }, + "buttocksAction": { + "angryA": "Você é um pervertido! Fique longe de mim!", + "embarrassedA": "Ugh... não faça isso...", + "surprisedA": "Ah! Onde você está tocando?!" + }, "chestAction": { "angryA": "Não pode me tratar assim! Tire a mão!", "angryB": "Alô? Tem um pervertido me tocando!", @@ -179,6 +185,10 @@ "neutralA": "Meu abdômen é apenas um poder oculto em treinamento", "neutralB": "Viu meu abdômen? Ele só está escondido mais fundo." }, + "buttocksAction": { + "angryA": "Se você me tocar de novo, eu vou te bater!", + "surprisedA": "Ei! Cuidado com suas mãos!" + }, "chestAction": { "blinkLeftA": "Vem, encosta no meu peito!", "neutralA": "Isso é apenas o resultado do meu treinamento diário, não há nada de surpreendente." diff --git a/locales/ru-RU/common.json b/locales/ru-RU/common.json index b55e6c00..2cf38067 100644 --- a/locales/ru-RU/common.json +++ b/locales/ru-RU/common.json @@ -1,6 +1,7 @@ { "cancel": "Отмена", "close": "Закрыть", + "community": "Поддержка сообщества", "confirm": "Подтвердить", "confirmDel": "Вы уверены, что хотите удалить?", "defaultAssistant": "Стандартный помощник", diff --git a/locales/ru-RU/role.json b/locales/ru-RU/role.json index d12a4e99..6d683ae6 100644 --- a/locales/ru-RU/role.json +++ b/locales/ru-RU/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Рука", "belly": "Живот", + "buttocks": "ягодицы", "chest": "Грудь", "head": "Голова", "leg": "Нога" @@ -143,6 +144,11 @@ "relaxedB": "Ненавижу! Я начинаю злиться!", "surprisedA": "Это было случайное прикосновение..." }, + "buttocksAction": { + "angryA": "Ты извращенец! Уйди от меня!", + "embarrassedA": "Ух... не делай так...", + "surprisedA": "А! Ты где трогаешь?!" + }, "chestAction": { "angryA": "Не смей меня так дразнить! Убери руку!", "angryB": "Алло, здесь какой-то извращенец, который меня трогает!", @@ -179,6 +185,10 @@ "neutralA": "Мои кубики пресса просто скрывают свою силу", "neutralB": "Видишь мои кубики? Они просто глубоко спрятаны" }, + "buttocksAction": { + "angryA": "Если еще раз тронешь, я тебя ударю!", + "surprisedA": "Эй! Следи за руками!" + }, "chestAction": { "blinkLeftA": "Давай, прижмись к моим мышцам!", "neutralA": "Это всего лишь результат моих ежедневных тренировок, нечего удивляться." diff --git a/locales/tr-TR/common.json b/locales/tr-TR/common.json index a617e72c..0ee32a80 100644 --- a/locales/tr-TR/common.json +++ b/locales/tr-TR/common.json @@ -1,6 +1,7 @@ { "cancel": "İptal", "close": "Kapat", + "community": "Topluluk Desteği", "confirm": "Onayla", "confirmDel": "Silmek istediğinize emin misiniz?", "defaultAssistant": "Varsayılan Asistan", diff --git a/locales/tr-TR/role.json b/locales/tr-TR/role.json index 10781f6e..f24cd396 100644 --- a/locales/tr-TR/role.json +++ b/locales/tr-TR/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Kol", "belly": "Karın", + "buttocks": "kalça", "chest": "Göğüs", "head": "Baş", "leg": "Bacak" @@ -143,6 +144,11 @@ "relaxedB": "Sinirleniyorum! Gerçekten kızacağım!", "surprisedA": "Bu kazara oldu değil mi..." }, + "buttocksAction": { + "angryA": "Sen bir sapıksın! Benden uzak dur!", + "embarrassedA": "Of... böyle yapma...", + "surprisedA": "Ah! Nereye dokunuyorsun?!" + }, "chestAction": { "angryA": "Beni böyle zorlayamazsın! Hemen elini çek!", "angryB": "Yoo, burada bir sapık var beni okşuyor!", @@ -179,6 +185,10 @@ "neutralA": "Karın kaslarım sadece derin bir güç saklıyor", "neutralB": "Bu karın kaslarımı gördün mü? Sadece derinlerde saklanıyorlar" }, + "buttocksAction": { + "angryA": "Bir daha dokunursan seni döverim!", + "surprisedA": "Hey! Elini dikkat et!" + }, "chestAction": { "blinkLeftA": "Gel, kardeşin göğüs kasına yaslan!", "neutralA": "Bu sadece günlük antrenmanımın sonucu olan göğüs kası, şaşıracak bir şey yok." diff --git a/locales/vi-VN/common.json b/locales/vi-VN/common.json index a63e72d0..4d41e863 100644 --- a/locales/vi-VN/common.json +++ b/locales/vi-VN/common.json @@ -1,6 +1,7 @@ { "cancel": "Hủy", "close": "Đóng", + "community": "Hỗ trợ cộng đồng", "confirm": "Xác nhận", "confirmDel": "Bạn có chắc chắn muốn xóa không?", "defaultAssistant": "Trợ lý mặc định", diff --git a/locales/vi-VN/role.json b/locales/vi-VN/role.json index ce1f1927..1eb5c3f1 100644 --- a/locales/vi-VN/role.json +++ b/locales/vi-VN/role.json @@ -114,6 +114,7 @@ "area": { "arm": "Cánh tay", "belly": "Bụng", + "buttocks": "mông", "chest": "Ngực", "head": "Đầu", "leg": "Chân" @@ -143,6 +144,11 @@ "relaxedB": "Ghét quá! Tôi sắp tức giận rồi!", "surprisedA": "Chắc là vô tình chạm phải thôi..." }, + "buttocksAction": { + "angryA": "Bạn thật biến thái! Tránh xa tôi ra!", + "embarrassedA": "Ôi... đừng như vậy...", + "surprisedA": "À! Bạn đang sờ đâu vậy?!" + }, "chestAction": { "angryA": "Không được bắt nạt tôi như vậy! Mau rút tay ra!", "angryB": "Có phải là 010 không? Có một kẻ biến thái đang sờ tôi!", @@ -179,6 +185,10 @@ "neutralA": "Bụng của tôi chỉ là sức mạnh ẩn giấu từ việc luyện tập.", "neutralB": "Bạn có thấy bụng của tôi không? Chúng chỉ ẩn sâu hơn thôi." }, + "buttocksAction": { + "angryA": "Nếu còn chạm vào tôi nữa, tôi sẽ đánh bạn!", + "surprisedA": "Này! Chú ý tay của bạn!" + }, "chestAction": { "blinkLeftA": "Đến đây, cơ ngực của tôi cho bạn dựa!", "neutralA": "Đây chỉ là cơ ngực tôi đạt được từ việc luyện tập hàng ngày, không có gì đáng ngạc nhiên." diff --git a/locales/zh-CN/common.json b/locales/zh-CN/common.json index 156b4c91..f0f85039 100644 --- a/locales/zh-CN/common.json +++ b/locales/zh-CN/common.json @@ -1,6 +1,7 @@ { "cancel": "取消", "close": "关闭", + "community": "社区支持", "confirm": "确定", "confirmDel": "确定删除吗?", "defaultAssistant": "默认助手", @@ -31,7 +32,12 @@ "undo": "取消订阅", "success": "已取消订阅" }, - "support": "社区支持", + "support": { + "title": "支持我们", + "tooltip": "请我喝杯咖啡", + "wechatQRCode": "微信赞赏", + "kofiSupport": "通过 Ko-fi 支持我们" + }, "theme": { "auto": "跟随系统", "light": "亮色模式", diff --git a/locales/zh-CN/role.json b/locales/zh-CN/role.json index 84ff0d68..46b9e24c 100644 --- a/locales/zh-CN/role.json +++ b/locales/zh-CN/role.json @@ -125,7 +125,8 @@ "arm": "手臂", "leg": "腿部", "chest": "胸部", - "belly": "腹部" + "belly": "腹部", + "buttocks": "臀部" }, "expression": { "natural": "自然", @@ -184,6 +185,11 @@ "angryA": "干嘛动我呀,小心我咬你哦!", "relaxedA": "醒醒,我们之间没有结果的!", "relaxedB": "讨厌!我可要生气啦!" + }, + "buttocksAction": { + "surprisedA": "啊!你在摸哪里?!", + "angryA": "你这个变态!离我远点!", + "embarrassedA": "呜...不要这样..." } }, "maleAction": { @@ -210,6 +216,10 @@ "neutralA": "我的腹肌只是再修炼深藏不露的内力", "happyA": "别挠痒痒,小心我笑出腹肌", "neutralB": "看到我这团腹肌了吗?它们只是藏得比较深罢了" + }, + "buttocksAction": { + "surprisedA": "嘿!注意你的手!", + "angryA": "再碰我就揍你了!" } } }, diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index f02d6e0a..e20e6089 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -1,6 +1,7 @@ { "cancel": "取消", "close": "關閉", + "community": "社區支持", "confirm": "確定", "confirmDel": "確定要刪除嗎?", "defaultAssistant": "預設助手", diff --git a/locales/zh-TW/role.json b/locales/zh-TW/role.json index 4b7ab37d..ed7183fe 100644 --- a/locales/zh-TW/role.json +++ b/locales/zh-TW/role.json @@ -114,6 +114,7 @@ "area": { "arm": "手臂", "belly": "腹部", + "buttocks": "臀部", "chest": "胸部", "head": "頭部", "leg": "腿部" @@ -143,6 +144,11 @@ "relaxedB": "討厭!我可要生氣啦!", "surprisedA": "是不小心碰到的吧..." }, + "buttocksAction": { + "angryA": "你這個變態!離我遠點!", + "embarrassedA": "呜...不要這樣...", + "surprisedA": "啊!你在摸哪裡?!" + }, "chestAction": { "angryA": "不可以這樣欺負我啦!快把手拿開!", "angryB": "幺幺零嗎?這裡有個變態一直在摸我!", @@ -179,6 +185,10 @@ "neutralA": "我的腹肌只是再修煉深藏不露的內力", "neutralB": "看到我這團腹肌了嗎?它們只是藏得比較深罷了" }, + "buttocksAction": { + "angryA": "再碰我就揍你了!", + "surprisedA": "嘿!注意你的手!" + }, "chestAction": { "blinkLeftA": "來,哥的胸肌給你靠!", "neutralA": "這不過是我日常修煉成就的胸肌,沒什麼好驚訝的。" diff --git a/src/layout/Header/Actions/Discord.tsx b/src/layout/Header/Actions/Discord.tsx index 9049c26c..2c784c61 100644 --- a/src/layout/Header/Actions/Discord.tsx +++ b/src/layout/Header/Actions/Discord.tsx @@ -28,7 +28,7 @@ export default () => { className={styles.icon} icon={SiDiscord} key="discord" - title={t('support')} + title={t('community')} onClick={() => window.open('https://discord.gg/AYFPHvv2jT', '_blank')} style={{ border: `1px solid ${theme.colorFillSecondary}` }} /> diff --git a/src/layout/Header/Actions/Github.tsx b/src/layout/Header/Actions/Github.tsx index 996847d5..4c7699db 100644 --- a/src/layout/Header/Actions/Github.tsx +++ b/src/layout/Header/Actions/Github.tsx @@ -27,7 +27,7 @@ export default () => { className={styles.icon} icon={SiGithub} key="github" - title={'✨ GitHub'} + title={'✨ Star'} onClick={() => window.open('https://github.com/lobehub/lobe-vidol', '_blank')} style={{ border: `1px solid ${theme.colorFillSecondary}` }} /> diff --git a/src/layout/Header/Actions/Support.tsx b/src/layout/Header/Actions/Support.tsx new file mode 100644 index 00000000..d11324a6 --- /dev/null +++ b/src/layout/Header/Actions/Support.tsx @@ -0,0 +1,76 @@ +/* eslint-disable @next/next/no-img-element */ +import { ActionIcon } from '@lobehub/ui'; +import { Popover } from 'antd'; +import { createStyles, useTheme } from 'antd-style'; +import { Coffee } from 'lucide-react'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +const useStyles = createStyles(({ token, css }) => ({ + supportContent: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '10px', + }, + icon: css` + svg { + fill: ${token.colorTextDescription}; + } + + &:hover { + svg { + fill: ${token.colorText}; + } + } + `, + qrCode: { + width: '200px', + height: '200px', + }, + kofiLink: { + 'color': token.colorPrimary, + 'textDecoration': 'none', + '&:hover': { + textDecoration: 'underline', + }, + }, +})); + +const Support: React.FC = () => { + const { styles } = useStyles(); + const { t } = useTranslation('common'); + const theme = useTheme(); + + const content = ( + + ); + + return ( + + + + ); +}; + +export default Support; diff --git a/src/layout/Header/Actions/ThemeMode/index.tsx b/src/layout/Header/Actions/ThemeMode.tsx similarity index 100% rename from src/layout/Header/Actions/ThemeMode/index.tsx rename to src/layout/Header/Actions/ThemeMode.tsx diff --git a/src/layout/Header/index.tsx b/src/layout/Header/index.tsx index d0e12749..97e999e8 100644 --- a/src/layout/Header/index.tsx +++ b/src/layout/Header/index.tsx @@ -11,6 +11,7 @@ import { HeaderNavKey } from '@/layout/type'; import Discord from './Actions/Discord'; import Github from './Actions/Github'; +import Support from './Actions/Support'; import ThemeMode from './Actions/ThemeMode'; interface Props { @@ -35,6 +36,7 @@ const Header = (props: Props) => { , , // , + , ]} logo={ diff --git a/src/locales/default/common.ts b/src/locales/default/common.ts index 94cd2ffb..92cfac6b 100644 --- a/src/locales/default/common.ts +++ b/src/locales/default/common.ts @@ -12,7 +12,13 @@ export default { dark: '暗黑模式', }, close: '关闭', - support: '社区支持', + support: { + title: '支持我们', + tooltip: '请我喝杯咖啡', + wechatQRCode: '微信赞赏', + kofiSupport: '通过 Ko-fi 支持我们', + }, + community: '社区支持', random: '随机', delete: '删除', submit: '提交', From d2d3fed4c437a48262eca0f1340a68c455ffc230 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Fri, 4 Oct 2024 23:46:06 +0800 Subject: [PATCH 08/10] feat: add ko-fi script --- src/layout/Header/Actions/Support.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/layout/Header/Actions/Support.tsx b/src/layout/Header/Actions/Support.tsx index d11324a6..6a55d2ba 100644 --- a/src/layout/Header/Actions/Support.tsx +++ b/src/layout/Header/Actions/Support.tsx @@ -49,13 +49,13 @@ const Support: React.FC = () => { src="https://r2.vidol.chat/common/wechat.jpg" alt={t('support.wechatQRCode')} /> - - ♥️ {`${t('support.kofiSupport')}`} + + Buy Me a Coffee at ko-fi.com ); From 5a4c3b5d8710d4484d079b81c17ae9413c756252 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Sat, 5 Oct 2024 09:56:09 +0800 Subject: [PATCH 09/10] feat: add background image loading --- src/app/chat/Background/index.tsx | 8 +-- .../chat/ChatInfo/BackGroundList/index.tsx | 35 ++++++++--- .../chat/ChatInfo/DanceList/Item/index.tsx | 18 +++--- src/hooks/useLoadBackground.tsx | 63 +++++++++++++++++++ .../{useLoadDance.tsx => useLoadSrc.tsx} | 6 +- src/libs/vrmViewer/viewer.ts | 4 +- src/store/global/index.ts | 11 ++-- src/utils/file.ts | 9 ++- 8 files changed, 117 insertions(+), 37 deletions(-) create mode 100644 src/hooks/useLoadBackground.tsx rename src/hooks/{useLoadDance.tsx => useLoadSrc.tsx} (89%) diff --git a/src/app/chat/Background/index.tsx b/src/app/chat/Background/index.tsx index 3e2d124c..f25e098a 100644 --- a/src/app/chat/Background/index.tsx +++ b/src/app/chat/Background/index.tsx @@ -1,4 +1,3 @@ -import { TRANSPARENT_ID, backgroundOptions } from '@/constants/background'; import { useGlobalStore } from '@/store/global'; import { useSettingStore } from '@/store/setting'; @@ -6,18 +5,17 @@ import { useStyles } from './style'; const Background = () => { const { styles } = useStyles(); - const backgroundId = useGlobalStore((s) => s.backgroundId); + const backgroundUrl = useGlobalStore((s) => s.backgroundUrl); const backgroundEffect = useSettingStore((s) => s.config.backgroundEffect); - const background = backgroundOptions.find((item) => item.id === backgroundId); - if (background && backgroundId !== TRANSPARENT_ID) { + if (backgroundUrl) { return (
({ @@ -26,13 +28,26 @@ interface BackgroundListProps { } const BackgroundList = ({ className, style }: BackgroundListProps) => { - const [backgroundId, setBackgroundId] = useGlobalStore((s) => [ - s.backgroundId, - s.setBackgroundId, - ]); + const [setBackgroundUrl] = useGlobalStore((s) => [s.setBackgroundUrl]); + const [backgroundId, setBackgroundId] = useState(undefined); const { t } = useTranslation('chat'); - const { styles } = useStyles(); + const { downloading, fetchBackgroundUrl, getBackgroundThumbById } = useLoadBackground(); + + const handleItemClick = async (id: string) => { + setBackgroundId(id); + if (id === TRANSPARENT_ID) { + setBackgroundUrl(undefined); + return; + } + try { + const url = await fetchBackgroundUrl(id); + setBackgroundUrl(url); + } catch { + message.error('背景图片下载失败'); + } + }; + return (
{ /> ({ - avatar: `https://r2.vidol.chat/backgrounds/${encodeURIComponent(option.thumbnail)}`, + avatar: `${getBackgroundThumbById(option.id)}`, id: option.id, name: option.name, url: option.url, + spin: downloading && backgroundId === option.id, }))} - onClick={(id) => setBackgroundId(id)} - isActivated={(id) => id === backgroundId} + onClick={handleItemClick} /> ); diff --git a/src/app/chat/ChatInfo/DanceList/Item/index.tsx b/src/app/chat/ChatInfo/DanceList/Item/index.tsx index 9f0d8d6f..aaf10970 100644 --- a/src/app/chat/ChatInfo/DanceList/Item/index.tsx +++ b/src/app/chat/ChatInfo/DanceList/Item/index.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import ListItem from '@/components/ListItem'; import { useLoadAudio } from '@/hooks/useLoadAudio'; -import { useLoadDance } from '@/hooks/useLoadDance'; +import { useLoadSrc } from '@/hooks/useLoadSrc'; import { useDanceStore } from '@/store/dance'; import { useGlobalStore } from '@/store/global'; import { Dance } from '@/types/dance'; @@ -42,7 +42,7 @@ const DanceItem = (props: DanceItemProps) => { const { t } = useTranslation('dance'); const { downloading: audioDownloading, percent: audioPercent, fetchAudioUrl } = useLoadAudio(); - const { downloading: danceDownloading, percent: dancePercent, fetchDanceUrl } = useLoadDance(); + const { downloading: srcDownloading, percent: srcPercent, fetchSrcUrl } = useLoadSrc(); const viewer = useGlobalStore((s) => s.viewer); const handlePlayPause = async () => { @@ -53,10 +53,10 @@ const DanceItem = (props: DanceItemProps) => { setCurrentPlayId(danceItem.danceId); setIsPlaying(true); const audioPromise = fetchAudioUrl(danceItem.danceId, danceItem.audio); - const dancePromise = fetchDanceUrl(danceItem.danceId, danceItem.src); - const [danceUrl, audioUrl] = await Promise.all([dancePromise, audioPromise]); - if (danceUrl && audioUrl) - viewer?.dance(danceUrl, audioUrl, () => { + const srcPromise = fetchSrcUrl(danceItem.danceId, danceItem.src); + const [srcUrl, audioUrl] = await Promise.all([srcPromise, audioPromise]); + if (srcUrl && audioUrl) + viewer?.dance(srcUrl, audioUrl, () => { setIsPlaying(false); }); } @@ -65,14 +65,14 @@ const DanceItem = (props: DanceItemProps) => { return ( ) : null, diff --git a/src/hooks/useLoadBackground.tsx b/src/hooks/useLoadBackground.tsx new file mode 100644 index 00000000..b89258e1 --- /dev/null +++ b/src/hooks/useLoadBackground.tsx @@ -0,0 +1,63 @@ +import { message } from 'antd'; +import { useState } from 'react'; + +import { backgroundOptions } from '@/constants/background'; +import { fetchWithProgress } from '@/utils/fetch'; +import { getBackgroundPathById } from '@/utils/file'; +import { cacheStorage } from '@/utils/storage'; + +const getBackgroundUrlById = (backgroundId: string) => { + const background = backgroundOptions.find((item) => item.id === backgroundId); + return background + ? `https://r2.vidol.chat/backgrounds/${encodeURIComponent(background.url)}` + : undefined; +}; + +const getBackgroundThumbById = (backgroundId: string) => { + const background = backgroundOptions.find((item) => item.id === backgroundId); + return background + ? `https://r2.vidol.chat/backgrounds/${encodeURIComponent(background.thumbnail)}` + : undefined; +}; + +export const useLoadBackground = () => { + const [downloading, setDownloading] = useState(false); + const [percent, setPercent] = useState(0); + + const fetchBackgroundUrl = async (backgroundId: string) => { + const backgroundUrl = getBackgroundUrlById(backgroundId); + if (!backgroundUrl) { + message.info('未找到对应的背景图片'); + return; + } + const localBackgroundPath = getBackgroundPathById(backgroundId); + let backgroundBlob = await cacheStorage.getItem(localBackgroundPath); + + try { + if (!backgroundBlob) { + setDownloading(true); + setPercent(0); + + backgroundBlob = await fetchWithProgress(backgroundUrl, { + onProgress: (loaded, total) => { + setPercent((loaded / total) * 100); + }, + }); + await cacheStorage.setItem(localBackgroundPath, backgroundBlob); + } + } finally { + setDownloading(false); + } + if (!backgroundBlob) return undefined; + + return URL.createObjectURL(backgroundBlob); + }; + + return { + downloading, + percent, + fetchBackgroundUrl, + getBackgroundUrlById, + getBackgroundThumbById, + }; +}; diff --git a/src/hooks/useLoadDance.tsx b/src/hooks/useLoadSrc.tsx similarity index 89% rename from src/hooks/useLoadDance.tsx rename to src/hooks/useLoadSrc.tsx index 2b3e508f..ec1ec1bf 100644 --- a/src/hooks/useLoadDance.tsx +++ b/src/hooks/useLoadSrc.tsx @@ -5,11 +5,11 @@ import { fetchWithProgress } from '@/utils/fetch'; import { getDancePathByDanceId } from '@/utils/file'; import { cacheStorage } from '@/utils/storage'; -export const useLoadDance = () => { +export const useLoadSrc = () => { const [downloading, setDownloading] = useState(false); const [percent, setPercent] = useState(0); - const fetchDanceUrl = async (danceId: string, src: string) => { + const fetchSrcUrl = async (danceId: string, src: string) => { const localDancePath = getDancePathByDanceId(danceId); let danceBlob = await cacheStorage.getItem(localDancePath); @@ -43,6 +43,6 @@ export const useLoadDance = () => { return { downloading, percent, - fetchDanceUrl, + fetchSrcUrl: fetchSrcUrl, }; }; diff --git a/src/libs/vrmViewer/viewer.ts b/src/libs/vrmViewer/viewer.ts index 018419d5..b8c33bd5 100644 --- a/src/libs/vrmViewer/viewer.ts +++ b/src/libs/vrmViewer/viewer.ts @@ -77,7 +77,7 @@ export class Viewer { /** * 播放舞蹈,以音乐文件的播放作为结束标志。 */ - public async dance(danceUrl: string, audioUrl: string, onEnd?: () => void) { + public async dance(srcUrl: string, audioUrl: string, onEnd?: () => void) { if (!this._sound || !this.model) { console.error('Audio Object or Model Object Not Existed'); return null; @@ -96,7 +96,7 @@ export class Viewer { this._sound.setVolume(0.5); this._sound.play(); - this.model?.playMotionUrl(MotionFileType.VMD, danceUrl, false); + this.model?.playMotionUrl(MotionFileType.VMD, srcUrl, false); } public resetToIdle() { diff --git a/src/store/global/index.ts b/src/store/global/index.ts index 05618767..8ef42e88 100644 --- a/src/store/global/index.ts +++ b/src/store/global/index.ts @@ -2,15 +2,14 @@ import type { ThemeMode } from 'antd-style'; import { shallow } from 'zustand/shallow'; import { createWithEqualityFn } from 'zustand/traditional'; -import { TRANSPARENT_ID } from '@/constants/background'; import { Viewer } from '@/libs/vrmViewer/viewer'; const viewer = new Viewer(); export interface GlobalStore { - backgroundId: string; + backgroundUrl: string | undefined; isPlaying: boolean; - setBackgroundId: (id: string) => void; + setBackgroundUrl: (url: string | undefined) => void; setChatDialog: (show: boolean) => void; setChatSidebar: (show: boolean) => void; setIsPlaying: (isPlaying: boolean) => void; @@ -37,7 +36,7 @@ const initialState = { showSessionList: true, showChatDialog: true, showRoleList: true, - backgroundId: TRANSPARENT_ID, + backgroundUrl: undefined, }; export const useGlobalStore = createWithEqualityFn()( (set) => ({ @@ -45,8 +44,8 @@ export const useGlobalStore = createWithEqualityFn()( setIsPlaying: (isPlaying: boolean) => { set({ isPlaying: isPlaying }); }, - setBackgroundId: (id: string) => { - set({ backgroundId: id }); + setBackgroundUrl: (url: string | undefined) => { + set({ backgroundUrl: url }); }, setThemeMode: (themeMode) => { set({ themeMode }); diff --git a/src/utils/file.ts b/src/utils/file.ts index f1bae7eb..4295c0f6 100644 --- a/src/utils/file.ts +++ b/src/utils/file.ts @@ -1,6 +1,7 @@ const MODEL_SCHEMA = 'model'; const AUDIO_SCHEMA = 'audio'; -const Dance_SCHEMA = 'dance'; +const DANCE_SCHEMA = 'dance'; +const BACKGROUND_SCHEMA = 'background'; const MOTION_SCHEMA = 'motion'; export const getModelPathByAgentId = (agentId: string) => { @@ -20,5 +21,9 @@ export const getMotionPathByMotionId = (motionId: string) => { }; export const getDancePathByDanceId = (danceId: string) => { - return `${Dance_SCHEMA}://${danceId}`; + return `${DANCE_SCHEMA}://${danceId}`; +}; + +export const getBackgroundPathById = (backgroundId: string) => { + return `${BACKGROUND_SCHEMA}://${backgroundId}`; }; From f2e1272023b59b175b9bbd7133858486b6c23d35 Mon Sep 17 00:00:00 2001 From: rdmclin2 Date: Sat, 5 Oct 2024 10:18:01 +0800 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E6=97=B6=E7=9A=84=E6=80=A7=E8=83=BD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/chat/ChatHeader/index.tsx | 3 ++- src/app/chat/ViewerMode/index.tsx | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app/chat/ChatHeader/index.tsx b/src/app/chat/ChatHeader/index.tsx index 58ba292b..c9f31ea2 100644 --- a/src/app/chat/ChatHeader/index.tsx +++ b/src/app/chat/ChatHeader/index.tsx @@ -2,6 +2,7 @@ import { Skeleton, Space } from 'antd'; import classNames from 'classnames'; +import { isEqual } from 'lodash-es'; import React, { Suspense } from 'react'; import { Flexbox } from 'react-layout-kit'; @@ -22,7 +23,7 @@ interface Props { export default (props: Props) => { const { className, style } = props; const { styles } = useStyles(); - const [currentAgent] = useSessionStore((s) => [sessionSelectors.currentAgent(s)]); + const [currentAgent] = useSessionStore((s) => [sessionSelectors.currentAgent(s)], isEqual); return ( { const { styles } = useStyles(); - const [currentAgent, interactive] = useSessionStore((s) => [ - sessionSelectors.currentAgent(s), - s.interactive, - ]); + const [currentAgent, interactive] = useSessionStore( + (s) => [sessionSelectors.currentAgent(s), s.interactive], + isEqual, + ); return (