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