Chat相关表现&逻辑分离,新增ChatController
新增情绪机器人逻辑, 新增情绪切换视频逻辑
This commit is contained in:
Binary file not shown.
@@ -145,18 +145,60 @@ export default class LanguageUtils {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getTextWithParams(
|
public static getTextByLanguage(
|
||||||
key: string,
|
key: string,
|
||||||
params: { [key: string]: any },
|
language: LanguageType = LanguageType.EN,
|
||||||
defaultText: string = ""
|
defaultText: string = ""
|
||||||
): string {
|
): string {
|
||||||
let text = this.getText(key, defaultText);
|
if (!this.isInitialized) {
|
||||||
|
this.init();
|
||||||
for (let paramKey in params) {
|
|
||||||
const regex = new RegExp(`{${paramKey}}`, "g");
|
|
||||||
text = text.replace(regex, params[paramKey].toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
return defaultText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.languageCache.has(key)) {
|
||||||
|
return this.languageCache.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ConfigManager.tables || !ConfigManager.tables.TbLanguage) {
|
||||||
|
console.warn("Language table not loaded yet");
|
||||||
|
return defaultText || key;
|
||||||
|
}
|
||||||
|
|
||||||
|
const languageData = ConfigManager.tables.TbLanguage.get(key);
|
||||||
|
if (!languageData) {
|
||||||
|
console.warn(`Language key not found: ${key}`);
|
||||||
|
return defaultText || key;
|
||||||
|
}
|
||||||
|
|
||||||
|
let text: string = "";
|
||||||
|
switch (language) {
|
||||||
|
case LanguageType.EN:
|
||||||
|
text = languageData.languageEn;
|
||||||
|
break;
|
||||||
|
case LanguageType.CN:
|
||||||
|
text = languageData.languageCn;
|
||||||
|
break;
|
||||||
|
case LanguageType.HI:
|
||||||
|
text = languageData.languageHi;
|
||||||
|
break;
|
||||||
|
case LanguageType.FR:
|
||||||
|
text = languageData.languageFr;
|
||||||
|
break;
|
||||||
|
case LanguageType.DE:
|
||||||
|
text = languageData.languageDe;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text = languageData.languageEn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!text || text.length === 0) {
|
||||||
|
text = languageData.languageEn || defaultText || key;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.languageCache.set(key, text);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export enum InnerMsgCode {
|
|||||||
BgVideo_End, //背景视频播放完毕
|
BgVideo_End, //背景视频播放完毕
|
||||||
|
|
||||||
Chat_DialogRefresh,
|
Chat_DialogRefresh,
|
||||||
|
Chat_EmotionInitialized,
|
||||||
SimplePlayVide,
|
SimplePlayVide,
|
||||||
LanguageChange,
|
LanguageChange,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,20 @@ export class ApiConfig {
|
|||||||
return { ...this.config };
|
return { ...this.config };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取情绪AI配置
|
||||||
|
*/
|
||||||
|
public getEmotionAIConfig(): AIConfig {
|
||||||
|
const config = ConfigManager.tables.TbGlobalConfig;
|
||||||
|
return {
|
||||||
|
apiKey: config.emotionApiKey,
|
||||||
|
model: config.Model,
|
||||||
|
temperature: config.Temperature,
|
||||||
|
maxTokens: config.MaxTokens,
|
||||||
|
timeout: config.Timeout,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新生成参数
|
* 更新生成参数
|
||||||
* @param temperature 温度参数
|
* @param temperature 温度参数
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { RoleConfigLoader } from "./RoleConfigLoader";
|
|||||||
import { ChatHistoryManager } from "../manager/ChatHistoryManager";
|
import { ChatHistoryManager } from "../manager/ChatHistoryManager";
|
||||||
import { ApiConfig } from "./ApiConfigLoader";
|
import { ApiConfig } from "./ApiConfigLoader";
|
||||||
import { ErrorHandler, ErrorType } from "../utils/ErrorHandler";
|
import { ErrorHandler, ErrorType } from "../utils/ErrorHandler";
|
||||||
|
import { EmotionAIService } from "./EmotionAIService";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI聊天服务类
|
* AI聊天服务类
|
||||||
@@ -123,6 +124,8 @@ export class ChatAIService {
|
|||||||
this.currentRoleId = roleId;
|
this.currentRoleId = roleId;
|
||||||
// 预创建聊天实例
|
// 预创建聊天实例
|
||||||
this.createOrGetChat(roleId);
|
this.createOrGetChat(roleId);
|
||||||
|
// 预创建情绪聊天实例
|
||||||
|
EmotionAIService.Instance.ensureEmotionChatExists(roleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,6 +189,14 @@ export class ChatAIService {
|
|||||||
role: "model",
|
role: "model",
|
||||||
parts: [{ text: response.text }],
|
parts: [{ text: response.text }],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 更新情绪状态
|
||||||
|
try {
|
||||||
|
await EmotionAIService.Instance.updateEmotionFromChat(roleId, message, response.text);
|
||||||
|
} catch (emotionError) {
|
||||||
|
console.warn(`Failed to update emotion for role ${roleId}:`, emotionError);
|
||||||
|
// 情绪更新失败不影响聊天功能
|
||||||
|
}
|
||||||
} catch (storageError) {
|
} catch (storageError) {
|
||||||
ErrorHandler.Instance.handleError(
|
ErrorHandler.Instance.handleError(
|
||||||
storageError as Error,
|
storageError as Error,
|
||||||
@@ -224,6 +235,8 @@ export class ChatAIService {
|
|||||||
if (this.chatInstances.has(roleId)) {
|
if (this.chatInstances.has(roleId)) {
|
||||||
this.chatInstances.delete(roleId);
|
this.chatInstances.delete(roleId);
|
||||||
}
|
}
|
||||||
|
// 清除情绪聊天历史
|
||||||
|
EmotionAIService.Instance.clearEmotionHistory(roleId);
|
||||||
// 清除本地存储的历史
|
// 清除本地存储的历史
|
||||||
ChatHistoryManager.Instance.clearHistory(roleId);
|
ChatHistoryManager.Instance.clearHistory(roleId);
|
||||||
console.log(`Cleared chat history for role ${roleId}`);
|
console.log(`Cleared chat history for role ${roleId}`);
|
||||||
@@ -234,6 +247,8 @@ export class ChatAIService {
|
|||||||
*/
|
*/
|
||||||
public clearAllChatHistory(): void {
|
public clearAllChatHistory(): void {
|
||||||
this.chatInstances.clear();
|
this.chatInstances.clear();
|
||||||
|
// 清除所有情绪聊天历史
|
||||||
|
EmotionAIService.Instance.clearAllEmotionHistory();
|
||||||
// 清除本地存储的所有历史
|
// 清除本地存储的所有历史
|
||||||
ChatHistoryManager.Instance.clearAllHistory();
|
ChatHistoryManager.Instance.clearAllHistory();
|
||||||
console.log("Cleared all chat histories");
|
console.log("Cleared all chat histories");
|
||||||
|
|||||||
@@ -0,0 +1,250 @@
|
|||||||
|
import { ChatAIService } from "./ChatAIService";
|
||||||
|
import { EmotionAIService } from "./EmotionAIService";
|
||||||
|
import { DialogManager } from "../manager/DialogManager";
|
||||||
|
import { VideoEmotion } from "../../schema/schema";
|
||||||
|
import ConfigManager from "../manager/ConfigManager";
|
||||||
|
import { ErrorHandler, ErrorType } from "../utils/ErrorHandler";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聊天控制器接口 - 定义Panel和Controller之间的通信协议
|
||||||
|
*/
|
||||||
|
export interface IChatPanelCallback {
|
||||||
|
/**
|
||||||
|
* 消息发送开始回调
|
||||||
|
* @param message 用户发送的消息
|
||||||
|
*/
|
||||||
|
onMessageSent(message: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收到AI回复回调
|
||||||
|
* @param response AI的回复内容
|
||||||
|
*/
|
||||||
|
onMessageReceived(response: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 情绪状态更新回调
|
||||||
|
* @param emotion 更新后的情绪状态
|
||||||
|
*/
|
||||||
|
onEmotionUpdated(emotion: VideoEmotion): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话更新回调
|
||||||
|
*/
|
||||||
|
onDialogUpdated(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误处理回调
|
||||||
|
* @param error 错误信息
|
||||||
|
*/
|
||||||
|
onError(error: Error): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聊天控制器类
|
||||||
|
*
|
||||||
|
* 负责处理聊天相关的所有业务逻辑,包括:
|
||||||
|
* - 角色数据管理
|
||||||
|
* - 消息发送和接收
|
||||||
|
* - 情绪状态管理
|
||||||
|
* - 与AI服务的交互
|
||||||
|
* - 对话历史管理
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const controller = new ChatController();
|
||||||
|
* controller.initialize(10001, panelCallback);
|
||||||
|
* const response = await controller.sendMessage("Hello");
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class ChatController {
|
||||||
|
private roleId: number | null = null;
|
||||||
|
private callback: IChatPanelCallback | null = null;
|
||||||
|
private dialogManager: DialogManager | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化聊天控制器
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @param callback 回调接口实现
|
||||||
|
*/
|
||||||
|
public initialize(roleId: number, callback: IChatPanelCallback): void {
|
||||||
|
this.roleId = roleId;
|
||||||
|
this.callback = callback;
|
||||||
|
this.dialogManager = DialogManager.getInstance();
|
||||||
|
|
||||||
|
// 设置当前聊天的角色ID
|
||||||
|
if (roleId && roleId > 0) {
|
||||||
|
ChatAIService.Instance.setCurrentRole(roleId);
|
||||||
|
console.log(`ChatController initialized with role ${roleId}`);
|
||||||
|
} else {
|
||||||
|
const error = new Error(`Invalid roleId: ${roleId}`);
|
||||||
|
this.handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息给AI并处理回复
|
||||||
|
* @param message 用户消息内容
|
||||||
|
* @returns Promise<string | null> AI的回复,失败时返回null
|
||||||
|
*/
|
||||||
|
public async sendMessage(message: string): Promise<string | null> {
|
||||||
|
if (!this.validateSendMessage(message)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 通知界面消息发送开始
|
||||||
|
this.callback?.onMessageSent(message);
|
||||||
|
|
||||||
|
// 更新对话显示 - 用户消息
|
||||||
|
this.dialogManager?.updateDialog(true, message, true);
|
||||||
|
this.callback?.onDialogUpdated();
|
||||||
|
|
||||||
|
console.log(`Sending message to role ${this.roleId}: ${message}`);
|
||||||
|
|
||||||
|
// 发送消息给AI服务
|
||||||
|
const response = await ChatAIService.Instance.sendMessage(this.roleId!, message);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
// 更新对话显示 - AI回复
|
||||||
|
this.dialogManager?.updateDialog(false, response);
|
||||||
|
this.callback?.onDialogUpdated();
|
||||||
|
|
||||||
|
// 通知界面收到回复
|
||||||
|
this.callback?.onMessageReceived(response);
|
||||||
|
|
||||||
|
// 获取更新后的情绪状态
|
||||||
|
try {
|
||||||
|
const currentEmotion = EmotionAIService.Instance.getCurrentEmotion(this.roleId!);
|
||||||
|
console.log(`Current emotion for role ${this.roleId}: ${VideoEmotion[currentEmotion]}`);
|
||||||
|
|
||||||
|
// 通知界面情绪更新
|
||||||
|
this.callback?.onEmotionUpdated(currentEmotion);
|
||||||
|
} catch (emotionError) {
|
||||||
|
console.warn("Failed to get current emotion:", emotionError);
|
||||||
|
// 情绪获取失败不影响聊天功能
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Response received from role ${this.roleId}: ${response}`);
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
const error = new Error("AI返回了空响应");
|
||||||
|
this.handleError(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ErrorHandler.Instance.handleApiError(error, "ChatController.sendMessage", {
|
||||||
|
roleId: this.roleId,
|
||||||
|
message: message.substring(0, 100) + "..."
|
||||||
|
});
|
||||||
|
this.handleError(error as Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前角色的情绪状态
|
||||||
|
* @returns VideoEmotion 当前情绪状态
|
||||||
|
*/
|
||||||
|
public getCurrentEmotion(): VideoEmotion {
|
||||||
|
if (!this.roleId) {
|
||||||
|
return VideoEmotion.calm_down;
|
||||||
|
}
|
||||||
|
return EmotionAIService.Instance.getCurrentEmotion(this.roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前角色数据
|
||||||
|
* @returns 角色数据对象,失败时返回null
|
||||||
|
*/
|
||||||
|
public getRoleData(): any {
|
||||||
|
if (!this.roleId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const roleData = ConfigManager.tables.TbGirls.get(this.roleId);
|
||||||
|
const roleDetail = ConfigManager.tables.TbGirlsDetail.get(this.roleId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
basic: roleData,
|
||||||
|
detail: roleDetail
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to get role data for ${this.roleId}:`, error);
|
||||||
|
this.handleError(error as Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除当前角色的聊天历史
|
||||||
|
*/
|
||||||
|
public clearChatHistory(): void {
|
||||||
|
if (!this.roleId) {
|
||||||
|
console.warn("Cannot clear history: roleId is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ChatAIService.Instance.clearChatHistory(this.roleId);
|
||||||
|
console.log(`Chat history cleared for role ${this.roleId}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to clear chat history:", error);
|
||||||
|
this.handleError(error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前角色ID
|
||||||
|
* @returns 当前角色ID
|
||||||
|
*/
|
||||||
|
public getCurrentRoleId(): number | null {
|
||||||
|
return this.roleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁控制器,清理资源
|
||||||
|
*/
|
||||||
|
public destroy(): void {
|
||||||
|
this.roleId = null;
|
||||||
|
this.callback = null;
|
||||||
|
this.dialogManager = null;
|
||||||
|
console.log("ChatController destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证发送消息的参数
|
||||||
|
* @param message 消息内容
|
||||||
|
* @returns 验证是否通过
|
||||||
|
*/
|
||||||
|
private validateSendMessage(message: string): boolean {
|
||||||
|
if (!this.roleId || this.roleId <= 0) {
|
||||||
|
const error = new Error("角色ID无效");
|
||||||
|
this.handleError(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message || message.trim() === "") {
|
||||||
|
const error = new Error("消息内容不能为空");
|
||||||
|
this.handleError(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.callback) {
|
||||||
|
const error = new Error("回调接口未设置");
|
||||||
|
this.handleError(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一错误处理
|
||||||
|
* @param error 错误对象
|
||||||
|
*/
|
||||||
|
private handleError(error: Error): void {
|
||||||
|
console.error("ChatController error:", error);
|
||||||
|
this.callback?.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "458edd93-5ff2-4c11-a573-f8985c48cc15",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,418 @@
|
|||||||
|
// 首先加载 polyfills 以确保兼容性
|
||||||
|
import "../utils/polyfills";
|
||||||
|
import { GoogleGenAI } from "@google/genai";
|
||||||
|
import { RoleConfigLoader } from "./RoleConfigLoader";
|
||||||
|
import { ChatHistoryManager } from "../manager/ChatHistoryManager";
|
||||||
|
import { ApiConfig } from "./ApiConfigLoader";
|
||||||
|
import { ErrorHandler, ErrorType } from "../utils/ErrorHandler";
|
||||||
|
import { VideoEmotion } from "../../schema/schema";
|
||||||
|
import ConfigManager from "../manager/ConfigManager";
|
||||||
|
import LanguageUtils, { LanguageType } from "../../Main/Common/LanguageUtils";
|
||||||
|
import Utils from "../../Main/Common/Utils";
|
||||||
|
import { InnerMsgCode } from "../../Main/Config/InnerMsgCode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI情绪分析服务类
|
||||||
|
*
|
||||||
|
* 基于Google Gemini API实现的情绪分析系统,支持:
|
||||||
|
* - 基于聊天历史的情绪状态分析
|
||||||
|
* - 多角色独立的情绪聊天实例管理
|
||||||
|
* - 返回标准的VideoEmotion枚举值
|
||||||
|
* - 自动初始化历史情绪分析
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const emotionService = EmotionAIService.Instance;
|
||||||
|
* const emotion = await emotionService.analyzeEmotionalState(10001);
|
||||||
|
* console.log(VideoEmotion[emotion]); // "calm_down"
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @author AI Chat System
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
export class EmotionAIService {
|
||||||
|
private static _instance: EmotionAIService;
|
||||||
|
private emotionAI: GoogleGenAI;
|
||||||
|
private emotionChatInstances: Map<number, any> = new Map();
|
||||||
|
private currentEmotions: Map<number, VideoEmotion> = new Map();
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
const emotionConfig = ApiConfig.Instance.getEmotionAIConfig();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.emotionAI = new GoogleGenAI({ apiKey: emotionConfig.apiKey });
|
||||||
|
} catch (error) {
|
||||||
|
ErrorHandler.Instance.handleError(
|
||||||
|
error as Error,
|
||||||
|
ErrorType.API_ERROR,
|
||||||
|
{ config: { ...emotionConfig, apiKey: "***" } }, // 隐藏API密钥
|
||||||
|
true
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取EmotionAIService的单例实例
|
||||||
|
*
|
||||||
|
* @returns {EmotionAIService} 情绪分析服务实例
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
public static get Instance(): EmotionAIService {
|
||||||
|
if (!this._instance) {
|
||||||
|
this._instance = new EmotionAIService();
|
||||||
|
}
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建或获取指定角色的情绪聊天实例
|
||||||
|
* @param roleId 角色ID
|
||||||
|
*/
|
||||||
|
private createOrGetEmotionChat(roleId: number): any {
|
||||||
|
if (!this.emotionChatInstances.has(roleId)) {
|
||||||
|
const systemInstruction = RoleConfigLoader.getEmotionInstruction(roleId);
|
||||||
|
const config = ApiConfig.Instance.getEmotionAIConfig();
|
||||||
|
|
||||||
|
const chat = this.emotionAI.chats.create({
|
||||||
|
model: config.model,
|
||||||
|
config: {
|
||||||
|
temperature: config.temperature,
|
||||||
|
systemInstruction: systemInstruction,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.emotionChatInstances.set(roleId, chat);
|
||||||
|
console.log(`Created new emotion chat instance for role ${roleId}`);
|
||||||
|
|
||||||
|
// 异步分析历史情绪状态
|
||||||
|
this.initializeEmotionAnalysis(roleId);
|
||||||
|
}
|
||||||
|
return this.emotionChatInstances.get(roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化情绪分析(检查是否有历史记录并进行分析)
|
||||||
|
* @param roleId 角色ID
|
||||||
|
*/
|
||||||
|
private async initializeEmotionAnalysis(roleId: number): Promise<void> {
|
||||||
|
try {
|
||||||
|
// 检查是否有聊天历史
|
||||||
|
const messageCount = ChatHistoryManager.Instance.getMessageCount(roleId);
|
||||||
|
|
||||||
|
let emotionalState: VideoEmotion;
|
||||||
|
|
||||||
|
if (messageCount > 0) {
|
||||||
|
console.log(
|
||||||
|
`Analyzing emotional state for role ${roleId} based on ${messageCount} messages`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 分析历史情绪状态
|
||||||
|
emotionalState = await this.analyzeEmotionalState(roleId);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Initial emotional state for role ${roleId}: ${VideoEmotion[emotionalState]}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`No history found for role ${roleId}, using default emotion: calm_down`
|
||||||
|
);
|
||||||
|
emotionalState = VideoEmotion.calm_down;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送情绪初始化完成事件
|
||||||
|
Utils.sendInnerMsg(InnerMsgCode.Chat_EmotionInitialized, {
|
||||||
|
roleId: roleId,
|
||||||
|
emotion: emotionalState
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Emotion initialization completed for role ${roleId}: ${VideoEmotion[emotionalState]}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to initialize emotion analysis for role ${roleId}:`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
|
||||||
|
// 即使发生错误,也发送默认情绪状态
|
||||||
|
Utils.sendInnerMsg(InnerMsgCode.Chat_EmotionInitialized, {
|
||||||
|
roleId: roleId,
|
||||||
|
emotion: VideoEmotion.calm_down
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化聊天历史用于情绪分析
|
||||||
|
* @param messages 聊天消息数组
|
||||||
|
* @returns 格式化后的分析提示
|
||||||
|
*/
|
||||||
|
private formatHistoryForEmotionAnalysis(messages: any[]): string {
|
||||||
|
if (messages.length === 0) {
|
||||||
|
return "没有聊天历史,请返回默认情绪状态:calm_down";
|
||||||
|
}
|
||||||
|
|
||||||
|
let conversationText =
|
||||||
|
"请分析以下对话的情绪状态,返回对应的VideoEmotion枚举值:\n\n";
|
||||||
|
|
||||||
|
messages.forEach((message, index) => {
|
||||||
|
const speaker = message.role === "user" ? "用户" : "AI";
|
||||||
|
const text = message.parts[0]?.text || "";
|
||||||
|
conversationText += `${speaker}: ${text}\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
conversationText +=
|
||||||
|
"\n请返回以下枚举值之一:calm_down, arousal, desire, passion, orgasm";
|
||||||
|
|
||||||
|
return conversationText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向指定角色的情绪AI发送消息并获取分析回复,返回VideoEmotion枚举值
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @param message 用户发送的消息内容
|
||||||
|
* @returns Promise<VideoEmotion> 情绪AI的分析结果,如果发生错误返回默认值calm_down
|
||||||
|
*/
|
||||||
|
public async sendEmotionMessage(
|
||||||
|
roleId: number,
|
||||||
|
message: string
|
||||||
|
): Promise<VideoEmotion> {
|
||||||
|
// 输入验证
|
||||||
|
if (!roleId || roleId <= 0) {
|
||||||
|
ErrorHandler.Instance.handleValidationError(
|
||||||
|
"roleId",
|
||||||
|
"角色ID必须是正整数",
|
||||||
|
roleId
|
||||||
|
);
|
||||||
|
return VideoEmotion.calm_down;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message || message.trim() === "") {
|
||||||
|
ErrorHandler.Instance.handleValidationError(
|
||||||
|
"message",
|
||||||
|
"消息内容不能为空",
|
||||||
|
message
|
||||||
|
);
|
||||||
|
return VideoEmotion.calm_down;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const emotionChat = this.createOrGetEmotionChat(roleId);
|
||||||
|
const response = await emotionChat.sendMessage({
|
||||||
|
message: message.trim(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response && response.text) {
|
||||||
|
console.log(`Emotion analysis from role ${roleId}:`, response.text);
|
||||||
|
return this.parseEmotionResponse(response.text);
|
||||||
|
} else {
|
||||||
|
const warningMsg = `情绪AI返回了空响应 (角色ID: ${roleId})`;
|
||||||
|
ErrorHandler.Instance.handleError(
|
||||||
|
new Error(warningMsg),
|
||||||
|
ErrorType.API_ERROR,
|
||||||
|
{ roleId, message, response },
|
||||||
|
true
|
||||||
|
);
|
||||||
|
return VideoEmotion.calm_down;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ErrorHandler.Instance.handleApiError(error, "sendEmotionMessage", {
|
||||||
|
roleId,
|
||||||
|
message: message.substring(0, 100) + "...",
|
||||||
|
});
|
||||||
|
return VideoEmotion.calm_down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析指定角色的情绪状态(基于最近10条聊天记录)
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @returns Promise<VideoEmotion> 情绪分析结果
|
||||||
|
*/
|
||||||
|
public async analyzeEmotionalState(roleId: number): Promise<VideoEmotion> {
|
||||||
|
try {
|
||||||
|
// 获取最近10条消息
|
||||||
|
const recentMessages = ChatHistoryManager.Instance.getRecentMessages(
|
||||||
|
roleId,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
// 格式化历史记录用于分析
|
||||||
|
const analysisPrompt =
|
||||||
|
this.formatHistoryForEmotionAnalysis(recentMessages);
|
||||||
|
|
||||||
|
// 发送给情绪AI进行分析
|
||||||
|
return await this.sendEmotionMessage(roleId, analysisPrompt);
|
||||||
|
} catch (error) {
|
||||||
|
ErrorHandler.Instance.handleError(
|
||||||
|
error as Error,
|
||||||
|
ErrorType.API_ERROR,
|
||||||
|
{ roleId },
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return VideoEmotion.calm_down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析情绪AI返回的文本为VideoEmotion枚举值
|
||||||
|
* @param responseText AI返回的文本
|
||||||
|
* @returns VideoEmotion枚举值
|
||||||
|
*/
|
||||||
|
private parseEmotionResponse(responseText: string): VideoEmotion {
|
||||||
|
const text = responseText.toLowerCase().trim();
|
||||||
|
|
||||||
|
if (text.includes("arousal")) {
|
||||||
|
return VideoEmotion.arousal;
|
||||||
|
} else if (text.includes("desire")) {
|
||||||
|
return VideoEmotion.desire;
|
||||||
|
} else if (text.includes("passion")) {
|
||||||
|
return VideoEmotion.passion;
|
||||||
|
} else if (text.includes("orgasm")) {
|
||||||
|
return VideoEmotion.orgasm;
|
||||||
|
} else {
|
||||||
|
return VideoEmotion.calm_down; // 默认值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保指定角色的情绪聊天实例已创建
|
||||||
|
* @param roleId 角色ID
|
||||||
|
*/
|
||||||
|
public ensureEmotionChatExists(roleId: number): void {
|
||||||
|
this.createOrGetEmotionChat(roleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除指定角色的情绪聊天历史
|
||||||
|
* @param roleId 角色ID
|
||||||
|
*/
|
||||||
|
public clearEmotionHistory(roleId: number): void {
|
||||||
|
if (this.emotionChatInstances.has(roleId)) {
|
||||||
|
this.emotionChatInstances.delete(roleId);
|
||||||
|
}
|
||||||
|
console.log(`Cleared emotion chat history for role ${roleId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有情绪聊天历史
|
||||||
|
*/
|
||||||
|
public clearAllEmotionHistory(): void {
|
||||||
|
this.emotionChatInstances.clear();
|
||||||
|
console.log("Cleared all emotion chat histories");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前活跃的情绪聊天实例数量
|
||||||
|
*/
|
||||||
|
public getActiveEmotionChatCount(): number {
|
||||||
|
return this.emotionChatInstances.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定角色的当前情绪状态
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @returns VideoEmotion 当前情绪状态
|
||||||
|
*/
|
||||||
|
public getCurrentEmotion(roleId: number): VideoEmotion {
|
||||||
|
return this.currentEmotions.get(roleId) || VideoEmotion.calm_down;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置指定角色的情绪状态
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @param emotion 情绪状态
|
||||||
|
*/
|
||||||
|
private setCurrentEmotion(roleId: number, emotion: VideoEmotion): void {
|
||||||
|
this.currentEmotions.set(roleId, emotion);
|
||||||
|
console.log(`Updated emotion for role ${roleId}: ${VideoEmotion[emotion]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色的英文名
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @returns 角色英文名,如果未找到返回"AI"
|
||||||
|
*/
|
||||||
|
private getRoleEnglishName(roleId: number): string {
|
||||||
|
try {
|
||||||
|
if (ConfigManager.tables && ConfigManager.tables.TbGirls) {
|
||||||
|
const girlData = ConfigManager.tables.TbGirls.get(roleId);
|
||||||
|
if (girlData && girlData.nameKey) {
|
||||||
|
return (
|
||||||
|
LanguageUtils.getTextByLanguage(
|
||||||
|
girlData.nameKey,
|
||||||
|
LanguageType.EN
|
||||||
|
) || "AI"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to get role name for ${roleId}:`, error);
|
||||||
|
}
|
||||||
|
return "AI";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据聊天对话更新情绪状态
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @param userMessage 用户消息
|
||||||
|
* @param aiResponse AI回复
|
||||||
|
* @returns Promise<VideoEmotion> 更新后的情绪状态
|
||||||
|
*/
|
||||||
|
public async updateEmotionFromChat(
|
||||||
|
roleId: number,
|
||||||
|
userMessage: string,
|
||||||
|
aiResponse: string
|
||||||
|
): Promise<VideoEmotion> {
|
||||||
|
try {
|
||||||
|
// 构建情绪分析提示,包含当前情绪上下文
|
||||||
|
const currentEmotion = this.getCurrentEmotion(roleId);
|
||||||
|
const analysisPrompt = this.formatChatForEmotionAnalysis(
|
||||||
|
roleId,
|
||||||
|
userMessage,
|
||||||
|
aiResponse,
|
||||||
|
currentEmotion
|
||||||
|
);
|
||||||
|
|
||||||
|
// 发送给情绪AI进行分析
|
||||||
|
const newEmotion = await this.sendEmotionMessage(roleId, analysisPrompt);
|
||||||
|
|
||||||
|
// 更新并保存情绪状态
|
||||||
|
this.setCurrentEmotion(roleId, newEmotion);
|
||||||
|
|
||||||
|
return newEmotion;
|
||||||
|
} catch (error) {
|
||||||
|
ErrorHandler.Instance.handleError(
|
||||||
|
error as Error,
|
||||||
|
ErrorType.API_ERROR,
|
||||||
|
{ roleId, userMessage: userMessage.substring(0, 50) },
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return this.getCurrentEmotion(roleId); // 返回当前情绪状态
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化聊天对话用于情绪分析
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @param userMessage 用户消息
|
||||||
|
* @param aiResponse AI回复
|
||||||
|
* @param currentEmotion 当前情绪状态
|
||||||
|
* @returns 格式化后的分析提示
|
||||||
|
*/
|
||||||
|
private formatChatForEmotionAnalysis(
|
||||||
|
roleId: number,
|
||||||
|
userMessage: string,
|
||||||
|
aiResponse: string,
|
||||||
|
currentEmotion: VideoEmotion
|
||||||
|
): string {
|
||||||
|
const roleName = this.getRoleEnglishName(roleId);
|
||||||
|
|
||||||
|
let analysisText = `当前情绪状态: ${VideoEmotion[currentEmotion]}\n\n`;
|
||||||
|
analysisText += `请分析以下最新对话的情绪变化,返回对应的VideoEmotion枚举值:\n\n`;
|
||||||
|
analysisText += `用户: ${userMessage}\n`;
|
||||||
|
analysisText += `${roleName}: ${aiResponse}\n\n`;
|
||||||
|
analysisText += `请基于对话内容和当前情绪状态,返回以下枚举值之一:calm_down, arousal, desire, passion, orgasm`;
|
||||||
|
|
||||||
|
return analysisText;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "624f6a81-539f-4347-aef1-d7c98c226a68",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -13,10 +13,26 @@ export class RoleConfigLoader {
|
|||||||
*/
|
*/
|
||||||
public static getRoleInstruction(roleId: number): string {
|
public static getRoleInstruction(roleId: number): string {
|
||||||
const AiCharacter = ConfigManager.tables.TbAiCharacters.get(roleId);
|
const AiCharacter = ConfigManager.tables.TbAiCharacters.get(roleId);
|
||||||
|
const globalPrompt = ConfigManager.tables.TbGlobalConfig.girlBasePrompt;
|
||||||
|
const prompt =
|
||||||
|
globalPrompt +
|
||||||
|
"\n" +
|
||||||
|
AiCharacter.basePrompt +
|
||||||
|
"\n" +
|
||||||
|
AiCharacter.additionPrompt;
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
|
|
||||||
return AiCharacter
|
/**
|
||||||
? AiCharacter.systemInstruction
|
* 根据角色ID获取对应的情绪机器人prompt
|
||||||
: ConfigManager.tables.TbAiCharacters.get(10001).systemInstruction;
|
* @param roleId 角色ID
|
||||||
|
* @returns System Instruction字符串,如果未找到则返回默认Role_1
|
||||||
|
*/
|
||||||
|
public static getEmotionInstruction(roleId: number): string {
|
||||||
|
const AiCharacter = ConfigManager.tables.TbAiCharacters.get(roleId);
|
||||||
|
const globalPrompt = ConfigManager.tables.TbGlobalConfig.EmotionRating;
|
||||||
|
const prompt = globalPrompt + "\n" + AiCharacter.basePrompt;
|
||||||
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import {
|
|||||||
Label,
|
Label,
|
||||||
Sprite,
|
Sprite,
|
||||||
UITransform,
|
UITransform,
|
||||||
|
VideoPlayer,
|
||||||
} from "cc";
|
} from "cc";
|
||||||
// 首先加载 polyfills 以确保兼容性
|
// 首先加载 polyfills 以确保兼容性
|
||||||
import "../../utils/polyfills";
|
import "../../utils/polyfills";
|
||||||
import { DialogManager } from "../../manager/DialogManager";
|
import { DialogManager } from "../../manager/DialogManager";
|
||||||
import { ChatAIService } from "../../core/ChatAIService";
|
import { ChatController, IChatPanelCallback } from "../../core/ChatController";
|
||||||
import li_BaseView from "db://assets/Scripts/Main/Common/li_BaseView";
|
import li_BaseView from "db://assets/Scripts/Main/Common/li_BaseView";
|
||||||
import Utils from "db://assets/Scripts/Main/Common/Utils";
|
import Utils from "db://assets/Scripts/Main/Common/Utils";
|
||||||
import { InnerMsgCode } from "db://assets/Scripts/Main/Config/InnerMsgCode";
|
import { InnerMsgCode } from "db://assets/Scripts/Main/Config/InnerMsgCode";
|
||||||
@@ -20,15 +21,15 @@ import GameRootUI from "db://assets/Scripts/Main/Common/GameRootUI";
|
|||||||
import { ImagePopup } from "../components/ImagePopup";
|
import { ImagePopup } from "../components/ImagePopup";
|
||||||
|
|
||||||
import ResManager from "db://assets/Scripts/Main/Manager/ResManager";
|
import ResManager from "db://assets/Scripts/Main/Manager/ResManager";
|
||||||
import { VideoRoleType } from "db://assets/Scripts/Main/Common/GlobalValue";
|
|
||||||
import ConfigManager from "../../manager/ConfigManager";
|
|
||||||
import LanguageUtils from "../../../Main/Common/LanguageUtils";
|
import LanguageUtils from "../../../Main/Common/LanguageUtils";
|
||||||
import { UITransitionHelper } from "../../utils/UITransitionHelper";
|
import { UITransitionHelper } from "../../utils/UITransitionHelper";
|
||||||
|
import { VideoEmotion, purchase } from "../../../schema/schema";
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
@ccclass("ChatPanel")
|
@ccclass("ChatPanel")
|
||||||
export class ChatPanel extends li_BaseView {
|
export class ChatPanel extends li_BaseView implements IChatPanelCallback {
|
||||||
manager: DialogManager = null;
|
manager: DialogManager = null;
|
||||||
|
private chatController: ChatController = new ChatController();
|
||||||
|
|
||||||
@property(EditBox)
|
@property(EditBox)
|
||||||
editBox: EditBox = null;
|
editBox: EditBox = null;
|
||||||
@@ -39,6 +40,9 @@ export class ChatPanel extends li_BaseView {
|
|||||||
@property(Sprite)
|
@property(Sprite)
|
||||||
girlImg: Sprite = null;
|
girlImg: Sprite = null;
|
||||||
|
|
||||||
|
@property(VideoPlayer)
|
||||||
|
girlVideo: VideoPlayer = null;
|
||||||
|
|
||||||
@property(ChatContentsLayout)
|
@property(ChatContentsLayout)
|
||||||
layout: ChatContentsLayout = null;
|
layout: ChatContentsLayout = null;
|
||||||
|
|
||||||
@@ -49,6 +53,8 @@ export class ChatPanel extends li_BaseView {
|
|||||||
private _nodeTab: any = {};
|
private _nodeTab: any = {};
|
||||||
|
|
||||||
nameKey: string;
|
nameKey: string;
|
||||||
|
private commercialVideos: purchase.CommercialVideo[] = [];
|
||||||
|
private currentEmotion: VideoEmotion | null = null;
|
||||||
|
|
||||||
private onLanguageChangeCallback = () => {
|
private onLanguageChangeCallback = () => {
|
||||||
if (!this.girlName) return;
|
if (!this.girlName) return;
|
||||||
@@ -57,9 +63,9 @@ export class ChatPanel extends li_BaseView {
|
|||||||
|
|
||||||
openUIDataCT(data) {
|
openUIDataCT(data) {
|
||||||
// 处理新的数据格式,支持过渡动画参数
|
// 处理新的数据格式,支持过渡动画参数
|
||||||
if (typeof data === 'object' && data.roleId !== undefined) {
|
if (typeof data === "object" && data.roleId !== undefined) {
|
||||||
this.id = data.roleId;
|
this.id = data.roleId;
|
||||||
|
|
||||||
// 如果标记了需要滑入动画,则执行动画
|
// 如果标记了需要滑入动画,则执行动画
|
||||||
if (data.withSlideTransition && this.node && this.node.isValid) {
|
if (data.withSlideTransition && this.node && this.node.isValid) {
|
||||||
// 延迟一帧执行动画,确保节点已正确加载到场景中
|
// 延迟一帧执行动画,确保节点已正确加载到场景中
|
||||||
@@ -71,10 +77,10 @@ export class ChatPanel extends li_BaseView {
|
|||||||
// 兼容原来的数字格式
|
// 兼容原来的数字格式
|
||||||
this.id = data;
|
this.id = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置当前聊天的角色ID
|
// 初始化ChatController
|
||||||
if (this.id && this.id > 0) {
|
if (this.id && this.id > 0) {
|
||||||
ChatAIService.Instance.setCurrentRole(this.id);
|
this.chatController.initialize(this.id, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +89,17 @@ export class ChatPanel extends li_BaseView {
|
|||||||
|
|
||||||
this.register();
|
this.register();
|
||||||
this.refresh(this.id);
|
this.refresh(this.id);
|
||||||
|
|
||||||
|
// Add video loaded event callback
|
||||||
|
if (this.girlVideo) {
|
||||||
|
this.girlVideo.node.on(
|
||||||
|
VideoPlayer.EventType.READY_TO_PLAY,
|
||||||
|
() => {
|
||||||
|
this.adjustVideoScale();
|
||||||
|
},
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
register() {
|
register() {
|
||||||
Utils.addInnerEL(
|
Utils.addInnerEL(
|
||||||
@@ -96,6 +113,12 @@ export class ChatPanel extends li_BaseView {
|
|||||||
this,
|
this,
|
||||||
this.onLanguageChangeCallback
|
this.onLanguageChangeCallback
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Utils.addInnerEL(
|
||||||
|
InnerMsgCode.Chat_EmotionInitialized,
|
||||||
|
this,
|
||||||
|
this.onEmotionInitialized
|
||||||
|
);
|
||||||
}
|
}
|
||||||
onDestroy(): void {
|
onDestroy(): void {
|
||||||
Utils.removeInnerEL(
|
Utils.removeInnerEL(
|
||||||
@@ -108,12 +131,37 @@ export class ChatPanel extends li_BaseView {
|
|||||||
this,
|
this,
|
||||||
this.onLanguageChangeCallback
|
this.onLanguageChangeCallback
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Utils.removeInnerEL(
|
||||||
|
InnerMsgCode.Chat_EmotionInitialized,
|
||||||
|
this,
|
||||||
|
this.onEmotionInitialized
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cleanup video event listeners
|
||||||
|
if (this.girlVideo && this.girlVideo.node) {
|
||||||
|
this.girlVideo.node.off(
|
||||||
|
VideoPlayer.EventType.READY_TO_PLAY,
|
||||||
|
this.adjustVideoScale,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销毁ChatController
|
||||||
|
if (this.chatController) {
|
||||||
|
this.chatController.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
refresh(id: number) {
|
refresh(id: number) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
const data = ConfigManager.tables.TbGirls.get(this.id);
|
|
||||||
const dataDetail = ConfigManager.tables.TbGirlsDetail.get(this.id);
|
// 通过ChatController获取角色数据
|
||||||
if (!data) return;
|
const roleData = this.chatController.getRoleData();
|
||||||
|
if (!roleData || !roleData.basic) return;
|
||||||
|
|
||||||
|
const data = roleData.basic;
|
||||||
|
const dataDetail = roleData.detail;
|
||||||
|
|
||||||
this.nameKey = data.nameKey;
|
this.nameKey = data.nameKey;
|
||||||
this.girlName.string = LanguageUtils.getText(data.nameKey);
|
this.girlName.string = LanguageUtils.getText(data.nameKey);
|
||||||
ResManager.I.changeBundleSpriteFrame(
|
ResManager.I.changeBundleSpriteFrame(
|
||||||
@@ -129,15 +177,172 @@ export class ChatPanel extends li_BaseView {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
let uiTransform: UITransform =
|
|
||||||
this._nodeTab.BgFrame.getComponent(UITransform);
|
|
||||||
|
|
||||||
Utils.sendInnerMsg(InnerMsgCode.SimplePlayVide, {
|
// Store commercialVideos for emotion-based switching
|
||||||
path: dataDetail.pics[0],
|
if (dataDetail && dataDetail.commercialVideos) {
|
||||||
size: uiTransform.contentSize,
|
this.commercialVideos = dataDetail.commercialVideos;
|
||||||
bgFrameNode: this._nodeTab.BgFrame,
|
console.log(`Loaded ${this.commercialVideos.length} videos for role ${this.id}`);
|
||||||
});
|
|
||||||
|
// Load initial video based on current emotion
|
||||||
|
if (this.commercialVideos.length > 0 && this.girlVideo) {
|
||||||
|
// Get current emotion from ChatController
|
||||||
|
let currentEmotion = VideoEmotion.calm_down; // Default fallback
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.chatController) {
|
||||||
|
currentEmotion = this.chatController.getCurrentEmotion();
|
||||||
|
console.log(`Current emotion for role ${this.id}: ${VideoEmotion[currentEmotion]}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Failed to get current emotion during initialization, using calm_down as default:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find video matching current emotion or fallback
|
||||||
|
const initialVideo = this.commercialVideos.find(video => video.emotion === currentEmotion)
|
||||||
|
|| this.commercialVideos.find(video => video.emotion === VideoEmotion.calm_down)
|
||||||
|
|| this.commercialVideos[0]; // Final fallback to first video
|
||||||
|
|
||||||
|
console.log(`Loading initial video: ${initialVideo.path} (emotion: ${VideoEmotion[initialVideo.emotion]})`);
|
||||||
|
|
||||||
|
// Set current emotion to the loaded video's emotion
|
||||||
|
this.currentEmotion = initialVideo.emotion;
|
||||||
|
|
||||||
|
ResManager.I.changeBundleVideo(
|
||||||
|
this.girlVideo,
|
||||||
|
initialVideo.path,
|
||||||
|
"Chat18x"
|
||||||
|
);
|
||||||
|
// Also immediately try to adjust scale (in case already loaded)
|
||||||
|
this.adjustVideoScale();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.commercialVideos = [];
|
||||||
|
this.currentEmotion = null; // Reset current emotion when no videos available
|
||||||
|
console.log("No commercialVideos data available for role", this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (dataDetail && dataDetail.commercialVideos && dataDetail.commercialVideos.length > 0) {
|
||||||
|
// let uiTransform: UITransform =
|
||||||
|
// this._nodeTab.BgFrame.getComponent(UITransform);
|
||||||
|
|
||||||
|
// Utils.sendInnerMsg(InnerMsgCode.SimplePlayVide, {
|
||||||
|
// path: dataDetail.commercialVideos[0].path,
|
||||||
|
// size: uiTransform.contentSize,
|
||||||
|
// bgFrameNode: this._nodeTab.BgFrame,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adjustVideoScale() {
|
||||||
|
if (!this.girlVideo || !this.girlVideo.node.parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tryAdjustScale = () => {
|
||||||
|
const parentTransform =
|
||||||
|
this.girlVideo.node.parent.getComponent(UITransform);
|
||||||
|
const videoTransform = this.girlVideo.node.getComponent(UITransform);
|
||||||
|
|
||||||
|
if (parentTransform && videoTransform && videoTransform.height > 0) {
|
||||||
|
const scale = parentTransform.height / videoTransform.height;
|
||||||
|
this.girlVideo.node.setScale(scale, scale, 1);
|
||||||
|
console.log(
|
||||||
|
`ChatPanel Video scaled to: ${scale}, parent height: ${parentTransform.height}, video height: ${videoTransform.height}`
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Immediately try once
|
||||||
|
if (!tryAdjustScale()) {
|
||||||
|
// If failed, delay and try again
|
||||||
|
this.scheduleOnce(() => {
|
||||||
|
if (!tryAdjustScale()) {
|
||||||
|
// Try again with longer delay
|
||||||
|
this.scheduleOnce(() => {
|
||||||
|
tryAdjustScale();
|
||||||
|
}, 0.5);
|
||||||
|
}
|
||||||
|
}, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setVideoEnable(enable: boolean) {
|
||||||
|
if (!this.girlVideo) return;
|
||||||
|
|
||||||
|
this.girlVideo.enabled = enable;
|
||||||
|
if (enable) {
|
||||||
|
this.girlVideo.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private switchVideoByEmotion(emotion: VideoEmotion): void {
|
||||||
|
if (!this.girlVideo || !this.commercialVideos || this.commercialVideos.length === 0) {
|
||||||
|
console.warn("Cannot switch video: missing video player or commercialVideos data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if emotion has changed
|
||||||
|
if (this.currentEmotion === emotion) {
|
||||||
|
console.log(`Emotion ${VideoEmotion[emotion]} unchanged, skipping video switch`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find video matching the emotion
|
||||||
|
const matchingVideo = this.commercialVideos.find(video => video.emotion === emotion);
|
||||||
|
|
||||||
|
if (matchingVideo) {
|
||||||
|
console.log(`Switching to video for emotion ${VideoEmotion[emotion]}: ${matchingVideo.path} (from ${this.currentEmotion !== null ? VideoEmotion[this.currentEmotion] : 'null'})`);
|
||||||
|
|
||||||
|
// Load the new video
|
||||||
|
ResManager.I.changeBundleVideo(
|
||||||
|
this.girlVideo,
|
||||||
|
matchingVideo.path,
|
||||||
|
"Chat18x"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update current emotion state
|
||||||
|
this.currentEmotion = emotion;
|
||||||
|
|
||||||
|
// Adjust scale and enable playback
|
||||||
|
this.adjustVideoScale();
|
||||||
|
this.setVideoEnable(true);
|
||||||
|
} else {
|
||||||
|
console.warn(`No video found for emotion ${VideoEmotion[emotion]}, falling back to first available video`);
|
||||||
|
|
||||||
|
// Fallback to first video if no match found
|
||||||
|
if (this.commercialVideos.length > 0) {
|
||||||
|
const fallbackVideo = this.commercialVideos[0];
|
||||||
|
console.log(`Loading fallback video: ${fallbackVideo.path} (emotion: ${VideoEmotion[fallbackVideo.emotion]})`);
|
||||||
|
|
||||||
|
ResManager.I.changeBundleVideo(
|
||||||
|
this.girlVideo,
|
||||||
|
fallbackVideo.path,
|
||||||
|
"Chat18x"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update current emotion state to the fallback video's emotion
|
||||||
|
this.currentEmotion = fallbackVideo.emotion;
|
||||||
|
|
||||||
|
this.adjustVideoScale();
|
||||||
|
this.setVideoEnable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onEmotionInitialized(data: {roleId: number, emotion: VideoEmotion}): void {
|
||||||
|
// 检查是否是当前角色
|
||||||
|
if (data.roleId === this.id) {
|
||||||
|
console.log(`Emotion initialized for current role ${this.id}: ${VideoEmotion[data.emotion]}`);
|
||||||
|
|
||||||
|
// 切换到对应的视频
|
||||||
|
this.switchVideoByEmotion(data.emotion);
|
||||||
|
} else {
|
||||||
|
console.log(`Emotion initialized for role ${data.roleId} (not current role ${this.id}), ignoring`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onDialogUpdate() {
|
onDialogUpdate() {
|
||||||
if (this.layout) {
|
if (this.layout) {
|
||||||
this.layout.UpdateDialog(DialogManager.getInstance().getDialogs());
|
this.layout.UpdateDialog(DialogManager.getInstance().getDialogs());
|
||||||
@@ -152,24 +357,90 @@ export class ChatPanel extends li_BaseView {
|
|||||||
public async OnClickSend() {
|
public async OnClickSend() {
|
||||||
const str = this.editBox.string;
|
const str = this.editBox.string;
|
||||||
if (!str || str == "") return;
|
if (!str || str == "") return;
|
||||||
console.log("post:" + str);
|
|
||||||
|
|
||||||
|
// 清空输入框
|
||||||
this.editBox.string = "";
|
this.editBox.string = "";
|
||||||
|
|
||||||
DialogManager.getInstance().updateDialog(true, str, true);
|
// 通过ChatController发送消息
|
||||||
|
await this.chatController.sendMessage(str);
|
||||||
// 使用新的sendMessage方法,传入角色ID
|
|
||||||
const ret = await ChatAIService.Instance.sendMessage(this.id, str);
|
|
||||||
|
|
||||||
if (this.manager) {
|
|
||||||
this.manager.updateDialog(false, ret);
|
|
||||||
}
|
|
||||||
console.log("ret:" + ret);
|
|
||||||
|
|
||||||
//测试
|
//测试
|
||||||
//this.popUpImage.refresh("Image/1/blur_naked_1");
|
//this.popUpImage.refresh("Image/1/blur_naked_1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === IChatPanelCallback 接口实现 ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息发送开始回调
|
||||||
|
* @param message 用户发送的消息
|
||||||
|
*/
|
||||||
|
public onMessageSent(message: string): void {
|
||||||
|
console.log("Message sent:", message);
|
||||||
|
// 可以在这里添加发送中的UI状态显示,比如显示loading等
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收到AI回复回调
|
||||||
|
* @param response AI的回复内容
|
||||||
|
*/
|
||||||
|
public onMessageReceived(response: string): void {
|
||||||
|
console.log("Message received:", response);
|
||||||
|
// 可以在这里添加接收到回复的UI效果,比如播放声音等
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话更新回调
|
||||||
|
*/
|
||||||
|
public onDialogUpdated(): void {
|
||||||
|
// 更新对话显示
|
||||||
|
this.onDialogUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误处理回调
|
||||||
|
* @param error 错误信息
|
||||||
|
*/
|
||||||
|
public onError(error: Error): void {
|
||||||
|
console.error("ChatPanel error:", error);
|
||||||
|
// 可以在这里显示错误提示给用户
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 情绪状态更新回调 (实现IChatPanelCallback接口)
|
||||||
|
* @param emotion 当前情绪状态
|
||||||
|
*/
|
||||||
|
public onEmotionUpdated(emotion: VideoEmotion): void {
|
||||||
|
console.log(`ChatPanel: Emotion updated to ${VideoEmotion[emotion]} (${emotion})`);
|
||||||
|
|
||||||
|
// Switch video based on the new emotion
|
||||||
|
this.switchVideoByEmotion(emotion);
|
||||||
|
|
||||||
|
// Additional UI updates can be added here based on emotion
|
||||||
|
// 例如:改变角色表情、背景色、播放相应的动画等
|
||||||
|
switch (emotion) {
|
||||||
|
case VideoEmotion.calm_down:
|
||||||
|
// 平静状态的UI更新
|
||||||
|
console.log("UI state: Calm");
|
||||||
|
break;
|
||||||
|
case VideoEmotion.arousal:
|
||||||
|
// 兴奋状态的UI更新
|
||||||
|
console.log("UI state: Arousal");
|
||||||
|
break;
|
||||||
|
case VideoEmotion.desire:
|
||||||
|
// 渴望状态的UI更新
|
||||||
|
console.log("UI state: Desire");
|
||||||
|
break;
|
||||||
|
case VideoEmotion.passion:
|
||||||
|
// 激情状态的UI更新
|
||||||
|
console.log("UI state: Passion");
|
||||||
|
break;
|
||||||
|
case VideoEmotion.orgasm:
|
||||||
|
// 高潮状态的UI更新
|
||||||
|
console.log("UI state: Orgasm");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
returnBtn() {
|
returnBtn() {
|
||||||
this.onClose();
|
this.onClose();
|
||||||
GameRootUI.I.showDefaultView();
|
GameRootUI.I.showDefaultView();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import LanguageUtils from "../../../Main/Common/LanguageUtils";
|
|||||||
import Utils from "../../../Main/Common/Utils";
|
import Utils from "../../../Main/Common/Utils";
|
||||||
import { InnerMsgCode } from "../../../Main/Config/InnerMsgCode";
|
import { InnerMsgCode } from "../../../Main/Config/InnerMsgCode";
|
||||||
import { SimpleToggle } from "../components/SimpleToggle";
|
import { SimpleToggle } from "../components/SimpleToggle";
|
||||||
import { GirlDetail } from "../../../schema/schema";
|
import { GirlDetail, purchase } from "../../../schema/schema";
|
||||||
|
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
@@ -151,31 +151,37 @@ export class GirlDetailPanel extends li_BaseView {
|
|||||||
}
|
}
|
||||||
this.tags.string = desc;
|
this.tags.string = desc;
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
this.starParent.children[i].active = i < data.starCount;
|
this.starParent.children[i].active = i < 5;
|
||||||
}
|
}
|
||||||
this.descAge.string = data.age.toString();
|
this.descAge.string =
|
||||||
|
LanguageUtils.getText("girldetailpanel.age") + data.age.toString();
|
||||||
this.desc.string = dataDetail.detailDesc;
|
this.desc.string = dataDetail.detailDesc;
|
||||||
|
|
||||||
ResManager.I.changeBundleVideo(
|
ResManager.I.changeBundleVideo(
|
||||||
this.avatarVideo,
|
this.avatarVideo,
|
||||||
dataDetail.vids[0],
|
dataDetail.commercialVideos[0].path,
|
||||||
"Chat18x"
|
"Chat18x"
|
||||||
);
|
);
|
||||||
|
|
||||||
// 也立即尝试调整(以防已经加载完成)
|
// 也立即尝试调整(以防已经加载完成)
|
||||||
this.adjustVideoScale();
|
this.adjustVideoScale();
|
||||||
this.vids = dataDetail.vids.slice(0);
|
this.vids = dataDetail.commercialVideos.slice(0);
|
||||||
this.refreshImgs(dataDetail.pics, true);
|
const paths = dataDetail.commercialImages.map((a) => a.path);
|
||||||
|
this.refreshImgs(paths, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
vids: string[];
|
vids: purchase.CommercialVideo[];
|
||||||
bindImgToggle() {
|
bindImgToggle() {
|
||||||
const dataDetail = ConfigManager.tables.TbGirlsDetail.get(this.id);
|
const dataDetail = ConfigManager.tables.TbGirlsDetail.get(this.id);
|
||||||
this.refreshImgs(dataDetail.pics, true);
|
const paths = dataDetail.commercialImages.map((a) => a.path);
|
||||||
|
|
||||||
|
this.refreshImgs(paths, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindVideoToggle() {
|
bindVideoToggle() {
|
||||||
this.refreshImgs(this.vids, false);
|
const paths = this.vids.map((a) => a.path);
|
||||||
|
|
||||||
|
this.refreshImgs(paths, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshImgs(paths: string[], isImg: boolean) {
|
refreshImgs(paths: string[], isImg: boolean) {
|
||||||
|
|||||||
+120
-16
@@ -60,6 +60,16 @@ export enum PriceType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export enum VideoEmotion {
|
||||||
|
calm_down = 0,
|
||||||
|
arousal = 1,
|
||||||
|
desire = 2,
|
||||||
|
passion = 3,
|
||||||
|
orgasm = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -69,7 +79,8 @@ export class AiCharacter {
|
|||||||
|
|
||||||
constructor(_buf_: ByteBuf) {
|
constructor(_buf_: ByteBuf) {
|
||||||
this.id = _buf_.readInt()
|
this.id = _buf_.readInt()
|
||||||
this.systemInstruction = _buf_.readString()
|
this.basePrompt = _buf_.readString()
|
||||||
|
this.additionPrompt = _buf_.readString()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,13 +88,18 @@ export class AiCharacter {
|
|||||||
*/
|
*/
|
||||||
readonly id: number
|
readonly id: number
|
||||||
/**
|
/**
|
||||||
* 系统指令
|
* 个人信息基础prompt(聊天&评分都需要)
|
||||||
*/
|
*/
|
||||||
readonly systemInstruction: string
|
readonly basePrompt: string
|
||||||
|
/**
|
||||||
|
* 额外信息prompt(聊天&评分都需要)
|
||||||
|
*/
|
||||||
|
readonly additionPrompt: string
|
||||||
|
|
||||||
resolve(tables:Tables) {
|
resolve(tables:Tables) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +117,6 @@ export class Girl {
|
|||||||
this.tagKey = _buf_.readString()
|
this.tagKey = _buf_.readString()
|
||||||
this.priceType = _buf_.readInt()
|
this.priceType = _buf_.readInt()
|
||||||
this.avatarPath = _buf_.readString()
|
this.avatarPath = _buf_.readString()
|
||||||
this.starCount = _buf_.readInt()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,10 +147,6 @@ export class Girl {
|
|||||||
* 头像路径
|
* 头像路径
|
||||||
*/
|
*/
|
||||||
readonly avatarPath: string
|
readonly avatarPath: string
|
||||||
/**
|
|
||||||
* 评价星数
|
|
||||||
*/
|
|
||||||
readonly starCount: number
|
|
||||||
|
|
||||||
resolve(tables:Tables) {
|
resolve(tables:Tables) {
|
||||||
|
|
||||||
@@ -145,7 +156,6 @@ export class Girl {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,8 +168,8 @@ export class GirlDetail {
|
|||||||
constructor(_buf_: ByteBuf) {
|
constructor(_buf_: ByteBuf) {
|
||||||
this.id = _buf_.readInt()
|
this.id = _buf_.readInt()
|
||||||
this.detailDesc = _buf_.readString()
|
this.detailDesc = _buf_.readString()
|
||||||
{ let n = Math.min(_buf_.readSize(), _buf_.size); this.pics = []; for(let i = 0 ; i < n ; i++) { let _e0 ;_e0 = _buf_.readString(); this.pics.push(_e0);}}
|
{ let n = Math.min(_buf_.readSize(), _buf_.size); this.commercialImages = []; for(let i = 0 ; i < n ; i++) { let _e0 ;_e0 = new purchase.CommercialImage(_buf_); this.commercialImages.push(_e0);}}
|
||||||
{ let n = Math.min(_buf_.readSize(), _buf_.size); this.vids = []; for(let i = 0 ; i < n ; i++) { let _e0 ;_e0 = _buf_.readString(); this.vids.push(_e0);}}
|
{ let n = Math.min(_buf_.readSize(), _buf_.size); this.commercialVideos = []; for(let i = 0 ; i < n ; i++) { let _e0 ;_e0 = new purchase.CommercialVideo(_buf_); this.commercialVideos.push(_e0);}}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,19 +181,19 @@ export class GirlDetail {
|
|||||||
*/
|
*/
|
||||||
readonly detailDesc: string
|
readonly detailDesc: string
|
||||||
/**
|
/**
|
||||||
* 图片资源列表
|
* 类型
|
||||||
*/
|
*/
|
||||||
readonly pics: string[]
|
readonly commercialImages: purchase.CommercialImage[]
|
||||||
/**
|
/**
|
||||||
* 视频资源列表
|
* 视频资源列表
|
||||||
*/
|
*/
|
||||||
readonly vids: string[]
|
readonly commercialVideos: purchase.CommercialVideo[]
|
||||||
|
|
||||||
resolve(tables:Tables) {
|
resolve(tables:Tables) {
|
||||||
|
|
||||||
|
|
||||||
|
for (let _e of this.commercialImages) { _e?.resolve(tables); }
|
||||||
|
for (let _e of this.commercialVideos) { _e?.resolve(tables); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,18 +205,25 @@ export class GlobalConfig {
|
|||||||
|
|
||||||
constructor(_buf_: ByteBuf) {
|
constructor(_buf_: ByteBuf) {
|
||||||
this.ApiKey = _buf_.readString()
|
this.ApiKey = _buf_.readString()
|
||||||
|
this.emotionApiKey = _buf_.readString()
|
||||||
this.Model = _buf_.readString()
|
this.Model = _buf_.readString()
|
||||||
this.Temperature = _buf_.readFloat()
|
this.Temperature = _buf_.readFloat()
|
||||||
this.MaxTokens = _buf_.readInt()
|
this.MaxTokens = _buf_.readInt()
|
||||||
this.Timeout = _buf_.readInt()
|
this.Timeout = _buf_.readInt()
|
||||||
this.FreeChatTimes = _buf_.readInt()
|
this.FreeChatTimes = _buf_.readInt()
|
||||||
this.GameName = _buf_.readString()
|
this.GameName = _buf_.readString()
|
||||||
|
this.EmotionRating = _buf_.readString()
|
||||||
|
this.girlBasePrompt = _buf_.readString()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* api键
|
* api键
|
||||||
*/
|
*/
|
||||||
readonly ApiKey: string
|
readonly ApiKey: string
|
||||||
|
/**
|
||||||
|
* 情绪判断机器人api
|
||||||
|
*/
|
||||||
|
readonly emotionApiKey: string
|
||||||
/**
|
/**
|
||||||
* ai模型
|
* ai模型
|
||||||
*/
|
*/
|
||||||
@@ -228,6 +245,14 @@ export class GlobalConfig {
|
|||||||
* 游戏名
|
* 游戏名
|
||||||
*/
|
*/
|
||||||
readonly GameName: string
|
readonly GameName: string
|
||||||
|
/**
|
||||||
|
* 情绪评分机器人system_prompt,需拼接角色prompt
|
||||||
|
*/
|
||||||
|
readonly EmotionRating: string
|
||||||
|
/**
|
||||||
|
* ai机器人基础规则
|
||||||
|
*/
|
||||||
|
readonly girlBasePrompt: string
|
||||||
|
|
||||||
resolve(tables:Tables) {
|
resolve(tables:Tables) {
|
||||||
|
|
||||||
@@ -237,6 +262,9 @@ export class GlobalConfig {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +315,70 @@ export class Language {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export namespace purchase {
|
||||||
|
export class CommercialImage {
|
||||||
|
|
||||||
|
constructor(_buf_: ByteBuf) {
|
||||||
|
this.imageType = _buf_.readInt()
|
||||||
|
this.imagePrice = _buf_.readFloat()
|
||||||
|
this.path = _buf_.readString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1=详情页展示<br/>2=聊天触发
|
||||||
|
*/
|
||||||
|
readonly imageType: number
|
||||||
|
/**
|
||||||
|
* 价格
|
||||||
|
*/
|
||||||
|
readonly imagePrice: number
|
||||||
|
/**
|
||||||
|
* 资源路径
|
||||||
|
*/
|
||||||
|
readonly path: string
|
||||||
|
|
||||||
|
resolve(tables:Tables) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export namespace purchase {
|
||||||
|
export class CommercialVideo {
|
||||||
|
|
||||||
|
constructor(_buf_: ByteBuf) {
|
||||||
|
this.path = _buf_.readString()
|
||||||
|
this.emotion = _buf_.readInt()
|
||||||
|
this.videoPrice = _buf_.readFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源路径
|
||||||
|
*/
|
||||||
|
readonly path: string
|
||||||
|
/**
|
||||||
|
* 关联情绪
|
||||||
|
*/
|
||||||
|
readonly emotion: VideoEmotion
|
||||||
|
/**
|
||||||
|
* 价格
|
||||||
|
*/
|
||||||
|
readonly videoPrice: number
|
||||||
|
|
||||||
|
resolve(tables:Tables) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class PurchaseConfig {
|
export class PurchaseConfig {
|
||||||
|
|
||||||
@@ -625,6 +717,10 @@ export class TbGlobalConfig {
|
|||||||
* api键
|
* api键
|
||||||
*/
|
*/
|
||||||
get ApiKey(): string { return this._data.ApiKey; }
|
get ApiKey(): string { return this._data.ApiKey; }
|
||||||
|
/**
|
||||||
|
* 情绪判断机器人api
|
||||||
|
*/
|
||||||
|
get emotionApiKey(): string { return this._data.emotionApiKey; }
|
||||||
/**
|
/**
|
||||||
* ai模型
|
* ai模型
|
||||||
*/
|
*/
|
||||||
@@ -646,6 +742,14 @@ export class TbGlobalConfig {
|
|||||||
* 游戏名
|
* 游戏名
|
||||||
*/
|
*/
|
||||||
get GameName(): string { return this._data.GameName; }
|
get GameName(): string { return this._data.GameName; }
|
||||||
|
/**
|
||||||
|
* 情绪评分机器人system_prompt,需拼接角色prompt
|
||||||
|
*/
|
||||||
|
get EmotionRating(): string { return this._data.EmotionRating; }
|
||||||
|
/**
|
||||||
|
* ai机器人基础规则
|
||||||
|
*/
|
||||||
|
get girlBasePrompt(): string { return this._data.girlBasePrompt; }
|
||||||
|
|
||||||
resolve(tables:Tables) {
|
resolve(tables:Tables) {
|
||||||
this._data.resolve(tables)
|
this._data.resolve(tables)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -163,7 +163,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 1094.75,
|
"y": 1087.5,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -218,8 +218,8 @@
|
|||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": -520,
|
"x": -510,
|
||||||
"y": -28.75,
|
"y": -83.42099999999982,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -259,8 +259,8 @@
|
|||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 36,
|
"width": 89,
|
||||||
"height": 64
|
"height": 90
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -404,9 +404,9 @@
|
|||||||
},
|
},
|
||||||
"_alignFlags": 9,
|
"_alignFlags": 9,
|
||||||
"_target": null,
|
"_target": null,
|
||||||
"_left": 20,
|
"_left": 30,
|
||||||
"_right": 0,
|
"_right": 0,
|
||||||
"_top": 40,
|
"_top": 75.92099999999982,
|
||||||
"_bottom": 30,
|
"_bottom": 30,
|
||||||
"_horizontalCenter": 0,
|
"_horizontalCenter": 0,
|
||||||
"_verticalCenter": 0,
|
"_verticalCenter": 0,
|
||||||
@@ -419,7 +419,7 @@
|
|||||||
"_originalWidth": 0,
|
"_originalWidth": 0,
|
||||||
"_originalHeight": 64,
|
"_originalHeight": 64,
|
||||||
"_alignMode": 2,
|
"_alignMode": 2,
|
||||||
"_lockFlags": 12,
|
"_lockFlags": 4,
|
||||||
"_id": ""
|
"_id": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -469,7 +469,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": -0.9460000000000051,
|
"y": -56.11999999999989,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -607,7 +607,7 @@
|
|||||||
"_target": null,
|
"_target": null,
|
||||||
"_left": 0,
|
"_left": 0,
|
||||||
"_right": 0,
|
"_right": 0,
|
||||||
"_top": 20,
|
"_top": 82.4239999999999,
|
||||||
"_bottom": 17.180999999999948,
|
"_bottom": 17.180999999999948,
|
||||||
"_horizontalCenter": 0,
|
"_horizontalCenter": 0,
|
||||||
"_verticalCenter": 0,
|
"_verticalCenter": 0,
|
||||||
@@ -676,7 +676,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 1080,
|
"width": 1080,
|
||||||
"height": 150.5
|
"height": 165
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -817,7 +817,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": -69.10000000000002,
|
"y": -87.5,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -4942,7 +4942,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 1080,
|
"width": 1080,
|
||||||
"height": 2201.8
|
"height": 2165
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -5087,7 +5087,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 1080,
|
"width": 1080,
|
||||||
"height": 2201.8
|
"height": 2165
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -5240,7 +5240,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 1080,
|
"width": 1080,
|
||||||
"height": 2201.8
|
"height": 2165
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -5269,7 +5269,7 @@
|
|||||||
"_target": null,
|
"_target": null,
|
||||||
"_left": 0,
|
"_left": 0,
|
||||||
"_right": 0,
|
"_right": 0,
|
||||||
"_top": 138.2,
|
"_top": 175,
|
||||||
"_bottom": 0,
|
"_bottom": 0,
|
||||||
"_horizontalCenter": 0,
|
"_horizontalCenter": 0,
|
||||||
"_verticalCenter": 0,
|
"_verticalCenter": 0,
|
||||||
|
|||||||
@@ -175,7 +175,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 1059,
|
"y": 1060,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -231,7 +231,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": -501.7,
|
"x": -501.7,
|
||||||
"y": -52.75199999999995,
|
"y": -53.75199999999995,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 54.803999999999995,
|
"y": -22.478999999999814,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -508,7 +508,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 410.224609375,
|
"width": 410.224609375,
|
||||||
"height": 112.39200000000001
|
"height": 93.87
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -549,7 +549,7 @@
|
|||||||
"_actualFontSize": 70,
|
"_actualFontSize": 70,
|
||||||
"_fontSize": 70,
|
"_fontSize": 70,
|
||||||
"_fontFamily": "NotoSans-Regular",
|
"_fontFamily": "NotoSans-Regular",
|
||||||
"_lineHeight": 89.2,
|
"_lineHeight": 74.5,
|
||||||
"_overflow": 0,
|
"_overflow": 0,
|
||||||
"_enableWrapText": true,
|
"_enableWrapText": true,
|
||||||
"_font": null,
|
"_font": null,
|
||||||
@@ -605,7 +605,7 @@
|
|||||||
"_target": null,
|
"_target": null,
|
||||||
"_left": 0,
|
"_left": 0,
|
||||||
"_right": 0,
|
"_right": 0,
|
||||||
"_top": 0,
|
"_top": 85.54399999999981,
|
||||||
"_bottom": 108.25899999999993,
|
"_bottom": 108.25899999999993,
|
||||||
"_horizontalCenter": 0,
|
"_horizontalCenter": 0,
|
||||||
"_verticalCenter": 0,
|
"_verticalCenter": 0,
|
||||||
@@ -926,7 +926,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 207.47199999999998,
|
"x": 207.47199999999998,
|
||||||
"y": -57.613000000000056,
|
"y": -105.53999999999996,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -1240,7 +1240,7 @@
|
|||||||
"_left": 194.1,
|
"_left": 194.1,
|
||||||
"_right": 282.528,
|
"_right": 282.528,
|
||||||
"_top": 0,
|
"_top": 0,
|
||||||
"_bottom": 53.386999999999944,
|
"_bottom": 4.460000000000036,
|
||||||
"_horizontalCenter": 0,
|
"_horizontalCenter": 0,
|
||||||
"_verticalCenter": 0,
|
"_verticalCenter": 0,
|
||||||
"_isAbsLeft": true,
|
"_isAbsLeft": true,
|
||||||
@@ -1306,7 +1306,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 365.847,
|
"x": 365.847,
|
||||||
"y": -58.52800000000002,
|
"y": -106.45499999999993,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -1818,7 +1818,7 @@
|
|||||||
"_left": 194.1,
|
"_left": 194.1,
|
||||||
"_right": 124.15300000000002,
|
"_right": 124.15300000000002,
|
||||||
"_top": 0,
|
"_top": 0,
|
||||||
"_bottom": 52.47199999999998,
|
"_bottom": 3.5450000000000728,
|
||||||
"_horizontalCenter": 0,
|
"_horizontalCenter": 0,
|
||||||
"_verticalCenter": 0,
|
"_verticalCenter": 0,
|
||||||
"_isAbsLeft": true,
|
"_isAbsLeft": true,
|
||||||
@@ -1871,7 +1871,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 29.928999999999974,
|
"x": 29.928999999999974,
|
||||||
"y": -54.12300000000005,
|
"y": -103.05000000000018,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -1968,8 +1968,8 @@
|
|||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": -195.44499999999994,
|
"x": -400.672,
|
||||||
"y": -57.613000000000056,
|
"y": -105.53999999999996,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -2281,9 +2281,9 @@
|
|||||||
"_alignFlags": 36,
|
"_alignFlags": 36,
|
||||||
"_target": null,
|
"_target": null,
|
||||||
"_left": 194.1,
|
"_left": 194.1,
|
||||||
"_right": 685.4449999999999,
|
"_right": 890.672,
|
||||||
"_top": 0,
|
"_top": 0,
|
||||||
"_bottom": 53.386999999999944,
|
"_bottom": 4.460000000000036,
|
||||||
"_horizontalCenter": 0,
|
"_horizontalCenter": 0,
|
||||||
"_verticalCenter": 0,
|
"_verticalCenter": 0,
|
||||||
"_isAbsLeft": true,
|
"_isAbsLeft": true,
|
||||||
@@ -2330,7 +2330,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 1080,
|
"width": 1080,
|
||||||
"height": 222
|
"height": 220
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -2433,8 +2433,6 @@
|
|||||||
"__id__": 0
|
"__id__": 0
|
||||||
},
|
},
|
||||||
"fileId": "43dBOZjJBF/btIjHVb4m2d",
|
"fileId": "43dBOZjJBF/btIjHVb4m2d",
|
||||||
"instance": null,
|
|
||||||
"targetOverrides": null,
|
|
||||||
"nestedPrefabInstanceRoots": null
|
"nestedPrefabInstanceRoots": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2474,7 +2472,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": -86.60000000000002,
|
"y": -110,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -2534,7 +2532,7 @@
|
|||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 902.3710000000001,
|
"y": 878.971,
|
||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"_lrot": {
|
"_lrot": {
|
||||||
@@ -3811,6 +3809,8 @@
|
|||||||
"__id__": 0
|
"__id__": 0
|
||||||
},
|
},
|
||||||
"fileId": "40sHQFKzNMq51+Dtkb1mcc",
|
"fileId": "40sHQFKzNMq51+Dtkb1mcc",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null,
|
||||||
"nestedPrefabInstanceRoots": null
|
"nestedPrefabInstanceRoots": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -5590,7 +5590,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 1060,
|
"width": 1060,
|
||||||
"height": 1750.8195000000003
|
"height": 1704.0195
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -5735,7 +5735,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 1060,
|
"width": 1060,
|
||||||
"height": 1750.8195000000003
|
"height": 1704.0195
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -5888,7 +5888,7 @@
|
|||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
"width": 1080,
|
"width": 1080,
|
||||||
"height": 2166.8
|
"height": 2120
|
||||||
},
|
},
|
||||||
"_anchorPoint": {
|
"_anchorPoint": {
|
||||||
"__type__": "cc.Vec2",
|
"__type__": "cc.Vec2",
|
||||||
@@ -5962,7 +5962,7 @@
|
|||||||
"_target": null,
|
"_target": null,
|
||||||
"_left": 0,
|
"_left": 0,
|
||||||
"_right": 0,
|
"_right": 0,
|
||||||
"_top": 173.2,
|
"_top": 220,
|
||||||
"_bottom": 0,
|
"_bottom": 0,
|
||||||
"_horizontalCenter": 0,
|
"_horizontalCenter": 0,
|
||||||
"_verticalCenter": 0,
|
"_verticalCenter": 0,
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,4 +6,4 @@ Maman sexyHeiße Mama
category_1004Binding捆绑बंधनBondageFesseln
|
|||||||
Sana Reddy�娜साना रेड�डी
|
Sana Reddy�娜साना रेड�डी
|
||||||
Sana Reddy
|
Sana Reddy
|
||||||
Sana Reddygirl_10004_nameScarlettScarlettसà¥�कारलेटScarlettScarlettgirl_10001_tag Hot|Horny␍ç�«è¾£|饥渴"सेकà¥�सी|कामà¥�कChaude|Excitée
|
Sana Reddygirl_10004_nameScarlettScarlettसà¥�कारलेटScarlettScarlettgirl_10001_tag Hot|Horny␍ç�«è¾£|饥渴"सेकà¥�सी|कामà¥�कChaude|Excitée
|
||||||
Heiß|Geilgirl_10002_tag%Earth-tone linens,handcrafted jewelry土色亚麻制å“�|手工ç� å®�Yà¤à¥‚रे रंग के लिनन|हसà¥�तनिरà¥�मित गहनेLinge terreux,Bijoux artisanaux'Erdfarbene Leinen,Handgemachter Schmuckgirl_10003_tagSinger-Songwriter,Dancer␍舞者|æŒæ‰‹2गायक-गीतकार|नरà¥�तकीChanteuse-compositrice,Danseuse Sängerin-Songwriterin,Tänzeringirl_10004_tag!RadiantOldSoul,BlueprintsAndBeats!RadiantOldSoul,BlueprintsAndBeats!RadiantOldSoul,BlueprintsAndBeats!RadiantOldSoul,BlueprintsAndBeats!RadiantOldSoul,BlueprintsAndBeats
|
Heiß|Geilgirl_10002_tag%Earth-tone linens,handcrafted jewelry土色亚麻制å“�|手工ç� å®�Yà¤à¥‚रे रंग के लिनन|हसà¥�तनिरà¥�मित गहनेLinge terreux,Bijoux artisanaux'Erdfarbene Leinen,Handgemachter Schmuckgirl_10003_tagSinger-Songwriter,Dancer␍舞者|æŒæ‰‹2गायक-गीतकार|नरà¥�तकीChanteuse-compositrice,Danseuse Sängerin-Songwriterin,Tänzeringirl_10004_tagGoldenDreamer,BlueEyesAndPearlsGoldenDreamer,BlueEyesAndPearlsGoldenDreamer,BlueEyesAndPearlsGoldenDreamer,BlueEyesAndPearlsGoldenDreamer,BlueEyesAndPearls
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 2.9 KiB |
@@ -46,10 +46,10 @@
|
|||||||
"offsetY": 0,
|
"offsetY": 0,
|
||||||
"trimX": 0,
|
"trimX": 0,
|
||||||
"trimY": 0,
|
"trimY": 0,
|
||||||
"width": 36,
|
"width": 89,
|
||||||
"height": 64,
|
"height": 90,
|
||||||
"rawWidth": 36,
|
"rawWidth": 89,
|
||||||
"rawHeight": 64,
|
"rawHeight": 90,
|
||||||
"borderTop": 0,
|
"borderTop": 0,
|
||||||
"borderBottom": 0,
|
"borderBottom": 0,
|
||||||
"borderLeft": 0,
|
"borderLeft": 0,
|
||||||
@@ -61,17 +61,17 @@
|
|||||||
"meshType": 0,
|
"meshType": 0,
|
||||||
"vertices": {
|
"vertices": {
|
||||||
"rawPosition": [
|
"rawPosition": [
|
||||||
-18,
|
-44.5,
|
||||||
-32,
|
-45,
|
||||||
0,
|
0,
|
||||||
18,
|
44.5,
|
||||||
-32,
|
-45,
|
||||||
0,
|
0,
|
||||||
-18,
|
-44.5,
|
||||||
32,
|
45,
|
||||||
0,
|
0,
|
||||||
18,
|
44.5,
|
||||||
32,
|
45,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
"indexes": [
|
"indexes": [
|
||||||
@@ -84,12 +84,12 @@
|
|||||||
],
|
],
|
||||||
"uv": [
|
"uv": [
|
||||||
0,
|
0,
|
||||||
64,
|
90,
|
||||||
36,
|
89,
|
||||||
64,
|
90,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
36,
|
89,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
"nuv": [
|
"nuv": [
|
||||||
@@ -103,13 +103,13 @@
|
|||||||
1
|
1
|
||||||
],
|
],
|
||||||
"minPos": [
|
"minPos": [
|
||||||
-18,
|
-44.5,
|
||||||
-32,
|
-45,
|
||||||
0
|
0
|
||||||
],
|
],
|
||||||
"maxPos": [
|
"maxPos": [
|
||||||
18,
|
44.5,
|
||||||
32,
|
45,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
Submodule xchat_cfg updated: c9f321db06...d4dd6a5bdf
Reference in New Issue
Block a user