import { _decorator, Component, EditBox, Node, Label, Sprite, UITransform, VideoPlayer, } from "cc"; // 首先加载 polyfills 以确保兼容性 import "../../utils/polyfills"; import { DialogManager } from "../../manager/DialogManager"; import { ChatController, IChatPanelCallback } from "../../core/ChatController"; import li_BaseView from "db://assets/Scripts/Main/Common/li_BaseView"; import Utils from "db://assets/Scripts/Main/Common/Utils"; import { InnerMsgCode } from "db://assets/Scripts/Main/Config/InnerMsgCode"; import { ChatContentsLayout } from "../components/ChatContentsLayout"; import { ViewManager } from "db://assets/Scripts/Main/Manager/ViewManager"; import GameRootUI from "db://assets/Scripts/Main/Common/GameRootUI"; import { ImagePopup } from "../components/ImagePopup"; import ResManager from "db://assets/Scripts/Main/Manager/ResManager"; import LanguageUtils from "../../../Main/Common/LanguageUtils"; import { UITransitionHelper } from "../../utils/UITransitionHelper"; import { VideoEmotion, purchase } from "../../../schema/schema"; const { ccclass, property } = _decorator; @ccclass("ChatPanel") export class ChatPanel extends li_BaseView implements IChatPanelCallback { manager: DialogManager = null; private chatController: ChatController = new ChatController(); @property(EditBox) editBox: EditBox = null; @property(Label) girlName: Label = null; @property(Sprite) girlImg: Sprite = null; @property(VideoPlayer) girlVideo: VideoPlayer = null; @property(ChatContentsLayout) layout: ChatContentsLayout = null; @property(ImagePopup) popUpImage: ImagePopup = null; id: number; private _nodeTab: any = {}; nameKey: string; private commercialVideos: purchase.CommercialVideo[] = []; private currentEmotion: VideoEmotion | null = null; private onLanguageChangeCallback = () => { if (!this.girlName) return; this.girlName.string = LanguageUtils.getText(this.nameKey); }; openUIDataCT(data) { // 处理新的数据格式,支持过渡动画参数 if (typeof data === "object" && data.roleId !== undefined) { this.id = data.roleId; // 如果标记了需要滑入动画,则执行动画 if (data.withSlideTransition && this.node && this.node.isValid) { // 延迟一帧执行动画,确保节点已正确加载到场景中 this.scheduleOnce(() => { UITransitionHelper.slideInFromRight(this.node, 0.3); }, 0); } } else { // 兼容原来的数字格式 this.id = data; } // 初始化ChatController if (this.id && this.id > 0) { this.chatController.initialize(this.id, this); } } onLoadCT() { Utils.parseNode(this.node, this._nodeTab); this.register(); 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() { Utils.addInnerEL( InnerMsgCode.Chat_DialogRefresh, this, this.onDialogUpdate ); Utils.addInnerEL( InnerMsgCode.LanguageChange, this, this.onLanguageChangeCallback ); Utils.addInnerEL( InnerMsgCode.Chat_EmotionInitialized, this, this.onEmotionInitialized ); } onDestroy(): void { Utils.removeInnerEL( InnerMsgCode.Chat_DialogRefresh, this, this.onDialogUpdate ); Utils.removeInnerEL( InnerMsgCode.LanguageChange, this, 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) { this.id = id; // 通过ChatController获取角色数据 const roleData = this.chatController.getRoleData(); if (!roleData || !roleData.basic) return; const data = roleData.basic; const dataDetail = roleData.detail; this.nameKey = data.nameKey; this.girlName.string = LanguageUtils.getText(data.nameKey); ResManager.I.changeBundleSpriteFrame( this.girlImg, data.avatarPath, "Chat18x", () => { let sizeTran = this.girlImg.node.parent.getComponent(UITransform); Utils.adjustBgPixelRatioToSize( sizeTran.contentSize, this.girlImg.node, 2 ); } ); // Store commercialVideos for emotion-based switching if (dataDetail && dataDetail.commercialVideos) { this.commercialVideos = dataDetail.commercialVideos; 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() { if (this.layout) { this.layout.UpdateDialog(DialogManager.getInstance().getDialogs()); } } protected onEnable(): void { if (!this.manager) this.manager = DialogManager.getInstance(); GameRootUI.I.hideDefaultView(); } public async OnClickSend() { const str = this.editBox.string; if (!str || str == "") return; // 清空输入框 this.editBox.string = ""; // 通过ChatController发送消息 await this.chatController.sendMessage(str); //测试 //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() { this.onClose(); GameRootUI.I.showDefaultView(); //ViewManager.I.openBundlesView("GirlListPanel"); } onClickRecord() { ViewManager.I.openBundlesView("RecordPanel", this.id); } }