Files
18xchat/.svn/pristine/78/78bd9facde8e503603bb56bd6ebfc2471f0b848e.svn-base
2025-07-17 17:18:21 +08:00

506 lines
18 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()
}
}
}
}