import { _decorator, AudioSource, EditBox, isValid, Label, Node, Sprite, tween, utils, Vec3 } from 'cc'; import { GButton } from '../../Main/Common/GButton'; import Utils from '../../Main/Common/Utils'; import { ViewManager } from '../../Main/Manager/ViewManager'; import { InnerMsgCode } from '../../Main/Config/InnerMsgCode'; import li_BaseView from '../../Main/Common/li_BaseView'; import SubManager from '../SubManager'; import GameManager, { E_TalkStatusType } from '../../Main/Manager/GameManager'; import { I_LevelConfig, I_NpcTalkBack } from '../../Main/Config/CommonConfig'; import HttpUnit from '../../Main/Common/HttpUnit'; import { SDKManager } from '../../Main/Channel/SDKManager'; import ResManager from '../../Main/Manager/ResManager'; const { ccclass, property } = _decorator; /**对话阶段 */ const enum TalkStage { /**开场白 */ prologue, /**我的对话 */ myTalk, /**等待NPC回复 */ waitNpcTalk, /**NPC对话 */ npcTalk, /**NPC固定话术 */ npcFixedTalk, /**更新对话状态 */ upTalkStatus, } //聊天界面 @ccclass('Talk_UI') export class Talk_UI extends li_BaseView { private _nodeTab: any = {}; private _data; private dazijiInfo = {stage: 0, idx: 0, str: "", tm:0, ts:0.05, lab:null}; //打字机信息 stage:0-未开始,1-正在打字,2-打字结束 private npcTalkInfo = {stage: 0, str:"", dotStr:"...", dotIdx:0, tm:0, ts:0.3}; //等待npc回复 stage:0-未开始,1-获取中,2-获取完成 private ebTalk: EditBox; private labNpcTalk: Label; private AIAudioSource: AudioSource; //AI语音组件 private curStage: TalkStage = TalkStage.prologue; //当前对话阶段 private lvCfg: I_LevelConfig = null; //当前关卡配置 private lastRandTalkIdx: number = -1; //上一次随机对话的索引 private srouceCnt: number = 0; //已用对话轮次 //----重写父类接口--------------------------------------- onLoadCT() { this.registerListenner(); Utils.parseNode(this.node, this._nodeTab) this.ebTalk = this._nodeTab.ebTalk.getComponent(EditBox); this.labNpcTalk = this._nodeTab.npcTalk.getComponent(Label); this.ebTalk.node.on('editing-did-began', this.onEditingBegan, this); this.ebTalk.node.on('editing-did-ended', this.onEditingEnded, this); this.ebTalk.node.on('editing-return', this.onEditingReturn, this); this.AIAudioSource = this._nodeTab.videoNode.getComponent(AudioSource); this._nodeTab.arrowNode.active = false; let npcNextArrow:Node = this._nodeTab.npcNextArrow tween(npcNextArrow) .to(0.5, {position: new Vec3(15, 0, 0)}) .to(0.5, {position: new Vec3(-15, 0, 0)}) .union() .repeatForever() .start() } openUIDataCT(data: any): void { this._data = data } registerListenner() { Utils.addInnerEL(InnerMsgCode.Data_NpcTalkBack, this, this.resiveNpcTalkBack) } start() { GButton.BandClick(this._nodeTab.btnBack, this.onBackClick, this); GButton.BandClick(this._nodeTab.btnSend, this.onSendClick, this); GButton.BandClick(this._nodeTab.btnSuiji, this.onSuijiClick, this); GButton.BandClick(this._nodeTab.npcTalkFrame, ()=>{ this.onNpcFramClick(); }, this, 0, null, null, false); GButton.BandClick(this._nodeTab.btnRecord, ()=>{ ViewManager.I.openBundlesView("UI/StartUI/Record_UI") }, this); GButton.BandClick(this._nodeTab.btnTishi, ()=>{ ViewManager.I.openBundlesView("UI/StartUI/Tips_UI") }, this); GButton.BandClick(this._nodeTab.btnAD, ()=>{ ViewManager.I.openBundlesView("UI/StartUI/TalkAdDialog", { content: "观看广告可获取额外对话次数", adCallback: ()=>{ SDKManager.show_reward_video_ad(this.node.uuid, 1) }, shareCallback: ()=>{ SDKManager.show_reward_share(this.node.uuid, 1) } }) }, this); //ai语音开关 GButton.BandClick(this._nodeTab.btnYuyin, ()=>{ GameManager.AIVoiceSwt = !GameManager.AIVoiceSwt; this.refreshAIVoiceState() }, this); this.refreshAIVoiceState() if (!SDKManager.checkSupportBase64Audio()) { this._nodeTab.btnYuyin.active = false } SDKManager.register_video_reward(this.node.uuid, (tag:number)=>{ console.log("视频广告回调", tag) if (tag == 1) { // 加次数 HttpUnit.ins.setUserAd((data) => { let addCnt = HttpUnit.GetAdAddNum() SubManager.ShowPrompt("观看视频成功,获得聊天次数") this.doAddLevelCnt(addCnt) }) } }) SDKManager.register_share_reward(this.node.uuid, (tag:number)=>{ console.log("分享回调", tag) if (tag == 1) { // 加次数 HttpUnit.ins.setUserShare((data) => { let addCnt = HttpUnit.GetAdAddNum() SubManager.ShowPrompt("分享成功,获得聊天次数") this.doAddLevelCnt(addCnt) }) } }) this.lvCfg = GameManager.getLevelCfg(GameManager.CurLevelId) //以npc开场白 this.showNpcPrologue() //刷新状态显示 this.srouceCnt = 0 this.refreshTalkStepData() } onDestroy () { if (isValid(this.ebTalk)) { this.ebTalk.node.off('editing-did-began', this.onEditingBegan, this); this.ebTalk.node.off('editing-did-ended', this.onEditingEnded, this); this.ebTalk.node.off('editing-return', this.onEditingReturn, this); } } update(deltaTime: number) { //region 打字机效果逻辑 if (this.dazijiInfo.stage == 1) { this.dazijiInfo.tm += deltaTime if (this.dazijiInfo.tm >= this.dazijiInfo.ts) { this.dazijiInfo.tm = 0 this.dazijiInfo.lab.string = this.dazijiInfo.str.substring(0, this.dazijiInfo.idx) this.dazijiInfo.idx += 1 if (this.dazijiInfo.idx > this.dazijiInfo.str.length) { this.dazijiInfo.stage = 2 } } } else if (this.dazijiInfo.stage == 2) { console.log("文本打字机效果结束") this.dazijiInfo.stage = 0 this.dazijiInfo.lab.string = this.dazijiInfo.str this._nodeTab.arrowNode.active = true; // if (this.curStage == TalkStage.prologue || this.curStage == TalkStage.npcTalk) { // this.scheduleOnce(() => { // this.turnToMyTalk() // }, 2) // } } //接收npc回复逻辑 if (this.npcTalkInfo.stage == 1) { this.npcTalkInfo.tm += deltaTime if (this.npcTalkInfo.tm >= this.npcTalkInfo.ts) { this.npcTalkInfo.tm = 0 GameManager.I.getNpcTalkBack({}) this.labNpcTalk.string = this.npcTalkInfo.dotStr.substring(0, this.npcTalkInfo.dotIdx) this.npcTalkInfo.dotIdx += 1 if (this.npcTalkInfo.dotIdx > this.npcTalkInfo.dotStr.length) { this.npcTalkInfo.dotIdx = 0 } } } } //接收到npc回复数据流 resiveNpcTalkBack(data:I_NpcTalkBack) { if (this.curStage != TalkStage.waitNpcTalk) return if (data.isFinished) { this.npcTalkInfo.stage = 0 console.log("npc回复:", data) GameManager.CurNpcTalkData = data this.turnToNpcTalk(data) } } //输入框开始编辑事件 onEditingBegan(param1:any, param2:any) { console.log("聊天界面1: onEditingBegan", param1, param2) } //输入框回车事件 onEditingReturn(param1:any, param2:any) { console.log("聊天界面2: onEditingReturn", param1, param2) } //输入框结束编辑事件 onEditingEnded(param1:any, param2:any) { console.log("聊天界面3: onEditingEnded", param1, param2) } //刷新AI语音状态 private refreshAIVoiceState() { let voicePath = GameManager.AIVoiceSwt ? "lv_4" : "lv_5" let sptYuyin = this._nodeTab.sptYuyin.getComponent(Sprite) ResManager.I.changeBundleSpriteFrame(sptYuyin, voicePath, "Zhujiemian") } //播放AI语音 private playAIVoice(str:string) { if (!GameManager.AIVoiceSwt) { return //开关没开 } if (!SDKManager.checkSupportBase64Audio()) { return //不支持base64音频 } HttpUnit.ins.getAIVoice({record_id: GameManager.RecordId, text:str}, (data) => { if (data) { console.log("播放AI语音11", data) SDKManager.playBase64Audio(data.url, false) // let qianzhui = "data:audio/x-wav;base64," // let substring = data.url.substring(qianzhui.length, data.url.length) // this.base64ToAudio(substring, (localUrl)=>{ // console.log("播放AI语音22", localUrl) // // 创建音频元素并播放 // const audio = new Audio(localUrl); // audio.play(); // // ResManager.I.loadRemoteAudio(localUrl, (audio) => { // // this.AIAudioSource.clip = audio // // this.AIAudioSource.play() // // this.AIAudioSource.volume = 1 // // // this.AIAudioSource.playOneShot(audio, 1) // // }) // }) } }) } //显示NPC开场白 private showNpcPrologue() { this.curStage = TalkStage.prologue this._nodeTab.NpcTalkNode.active = true this._nodeTab.MyTalkNode.active = false this._nodeTab.arrowNode.active = false; //名称 Utils.setString(this._nodeTab.npcName, this.lvCfg.name) //开场白 this.dazijiInfo.stage = 1 this.dazijiInfo.idx = 0 this.dazijiInfo.lab = this.labNpcTalk this.dazijiInfo.str = this.lvCfg.prologueMessage this.dazijiInfo.tm = this.dazijiInfo.ts GameManager.AddTalkRecord({talk:this.lvCfg.prologueMessage, isMe:false}) // this.playAIVoice(this.lvCfg.prologueMessage) } /**切换到玩家说话状态 */ private turnToMyTalk() { if (this.curStage == TalkStage.myTalk) return this.curStage = TalkStage.myTalk this._nodeTab.NpcTalkNode.active = false this._nodeTab.MyTalkNode.active = true this.ebTalk.string = "" } /**切换到等待NPC说话状态 */ private turnToWaitNpcTalk() { if (this.curStage == TalkStage.waitNpcTalk) return this.curStage = TalkStage.waitNpcTalk this._nodeTab.NpcTalkNode.active = true this._nodeTab.MyTalkNode.active = false this._nodeTab.arrowNode.active = false; this.labNpcTalk.string = "" } /**切换到NPC说话状态 */ private turnToNpcTalk(data:I_NpcTalkBack) { if (this.curStage == TalkStage.npcTalk) return this.curStage = TalkStage.npcTalk this._nodeTab.NpcTalkNode.active = true this._nodeTab.MyTalkNode.active = false this._nodeTab.arrowNode.active = false; GameManager.AddStepScore(data.aiScore) //记录本次分数 //npc回复 // this.labNpcTalk.string = data.aiResponse this.dazijiInfo.stage = 1 this.dazijiInfo.idx = 0 this.dazijiInfo.lab = this.labNpcTalk this.dazijiInfo.str = data.aiResponse this.dazijiInfo.tm = this.dazijiInfo.ts GameManager.AddTalkRecord({talk:data.aiResponse, isMe:false}) this.playAIVoice(data.aiResponse) //npc表情 //分数 //对话轮次 this.srouceCnt = data.sourceCnt this.refreshTalkStepData() } /**切换到npc固定话术展示状态 */ private turnToNpcFixedTalk(str:string) { if (this.curStage == TalkStage.npcFixedTalk) return this.curStage = TalkStage.npcFixedTalk this._nodeTab.NpcTalkNode.active = true this._nodeTab.MyTalkNode.active = false this._nodeTab.arrowNode.active = false; //npc固定话术 this.dazijiInfo.stage = 1 this.dazijiInfo.idx = 0 this.dazijiInfo.lab = this.labNpcTalk this.dazijiInfo.str = str this.dazijiInfo.tm = this.dazijiInfo.ts } /**切换到更新聊天状态状态 */ private turnToUpdateStage() { if (this.curStage == TalkStage.upTalkStatus) return this.curStage = TalkStage.upTalkStatus HttpUnit.ins.getTalkStage({record_id: GameManager.RecordId}, (data) => { if (data) { GameManager.CurLevelData = data.level this.checkIsFinish() } }) } /**刷新当前状态显示 */ private refreshTalkStepData() { let stepData = GameManager.CurLevelData Utils.setString(this._nodeTab.lunciNum, `${this.srouceCnt}/${stepData.total_cnt}`) } /**检查对话轮次是否用完 * @returns [是否结束对话,是否显示广告加次数] */ private checkIsFinish() { // //测试代码 // if (true) { // return this.srouceCnt >= 1 // } let isFinish = false //是否结束对话 let isShowAD = false //是否显示广告 let stepData = GameManager.CurLevelData if (stepData.status == E_TalkStatusType.succ) { //已成功 isFinish = true isShowAD = false } else if (stepData.status == E_TalkStatusType.fail) { //已失败 isFinish = true isShowAD = false } else if (stepData.status == E_TalkStatusType.noCount) { //次数已用完 isFinish = true isShowAD = true } if (isFinish) { if (isShowAD) { ViewManager.I.openBundlesView("UI/StartUI/TalkAdDialog", { content: "观看广告可获取额外对话次数", adCallback: ()=>{ SDKManager.show_reward_video_ad(this.node.uuid, 1) }, shareCallback: ()=>{ SDKManager.show_reward_share(this.node.uuid, 1) }, closeCallback: ()=>{ this.doJiesuan() } }) } else { this.doJiesuan() } } else { this.turnToMyTalk() } } //增加聊天次数 private doAddLevelCnt(add:number = 1) { HttpUnit.ins.addTalkCnt({record_id: GameManager.RecordId, add_cnt:add}, (data) => { if (data) { GameManager.CurLevelData = data.level this.refreshTalkStepData() //判断是否继续对话 if (this.srouceCnt < data.level.total_cnt) { this.turnToMyTalk() } else { this.doJiesuan() } } }) } //结算 private doJiesuan() { let [score_all, score] = GameManager.getGameScore() HttpUnit.ins.sendLevelFinish({record_id: GameManager.RecordId, score:score, score_all:score_all}, (data) => { if (data && data.level) { //成功 ViewManager.I.openBundlesView("UI/StartUI/Finish_UI", data.level) } else { //失败 this.doClose(); Utils.sendInnerMsg(InnerMsgCode.UI_Talk_Back, {}) } }) } //退出聊天 onBackClick() { SubManager.ShowConfirm({ content: "确定退出吗?", yesCallback: () => { this.doJiesuan(); } }) } //发送消息 onSendClick() { let str = this.ebTalk.string; if (str.length == 0) { SubManager.ShowPrompt("请先输入内容") return } this.turnToWaitNpcTalk() this.ebTalk.string = "" HttpUnit.ins.sendUserTalk({record_id: GameManager.RecordId, user_input: str}, (data) => { if (data) { GameManager.AddTalkRecord({talk:str, isMe:true}) //开始接收npc回复 this.npcTalkInfo.stage = 1 this.npcTalkInfo.str = "" this.npcTalkInfo.tm = this.npcTalkInfo.ts this.npcTalkInfo.dotIdx = 0 } }) } //随机回复对话 onSuijiClick() { let sjLen = this.lvCfg.randomInputPool.length let randIdx = Utils.getRandomInt(0, sjLen-1) console.log("随机回复对话:", randIdx, sjLen) if (randIdx == this.lastRandTalkIdx) { randIdx++ if (randIdx >= sjLen) randIdx = 0 } this.lastRandTalkIdx = randIdx this.ebTalk.string = this.lvCfg.randomInputPool[randIdx] } //点击npc对话背景 onNpcFramClick() { if (this.dazijiInfo.stage == 1) { this.dazijiInfo.stage = 2 } else { if (this.curStage == TalkStage.prologue) { this.turnToUpdateStage() } else if (this.curStage == TalkStage.npcTalk) { let npcTalk = GameManager.CurNpcTalkData if (npcTalk.aiScore > 80) { this.turnToNpcFixedTalk(this.lvCfg.encourageMessage) console.log("固定鼓励话术展示!") } else if (npcTalk.aiScore >= 20 && npcTalk.aiScore <= 40) { console.log("固定厌恶话术展示!") this.turnToNpcFixedTalk(this.lvCfg.hateMessage) } else { this.turnToUpdateStage() } } else if (this.curStage == TalkStage.npcFixedTalk) { this.turnToUpdateStage() } } } }