import { _decorator, Animation, AnimationClip, AudioClip, AudioSource, CharacterController, Color, Component, director, EventMouse, EventTouch, Label, Node, NodeEventType, ProgressBar, Sprite, tween, Tween, UIOpacity, UITransform, v3, Vec2, Vec3 } from "cc" import { noticePannelCtr } from "./noticePannelCtr" import { StoryPannelCtr } from "./StoryPannelCtr" import { OptionsCtr } from "./OptionsCtr" import { rankCtr } from "./rankCtr" import { RoleCtr } from "./RoleCtr" import { processCtr } from "./socket/processCtr" import { SocketClient } from "./socket/SocketClient" const { ccclass, property } = _decorator @ccclass("GameCtr") export class GameCtr extends Component { @property({ type: noticePannelCtr }) public noticeCtr: noticePannelCtr | null = null @property({ type: SocketClient }) public socketClient: SocketClient | null = null @property({ type: processCtr }) public processCtr: processCtr | null = null @property({ type: RoleCtr }) public roleCtr: RoleCtr | null = null @property({ type: StoryPannelCtr }) public storyPannelCtr: StoryPannelCtr | null = null @property({ type: OptionsCtr }) public optionsCtr: OptionsCtr | null = null @property({ type: rankCtr }) public rankCtr: rankCtr | null = null @property({ type: Node }) public titlePannel: Node = null @property({ type: Label }) private dayLabel: Label | null = null @property(Animation) SunAnim: Animation = null @property(Animation) MoonAnim: Animation = null private sunIsUp: boolean = false private num: number = 1 public roomId: number = 1 public gameId: number = 1 public resetNum: number = 1 private gameInfo: any = null private historyInfo: any[] = [] private initStoryPosition: Vec3 = null private initOptionsPosition: Vec3 = null //剧情布局变化状态:0:展示剧情 ,1:出现选项框 2:已选选项下方展示剧情,3:已选移除剧情上移出现选项框 private storyActionStatus: number = 0 //重启动画 @property({ type: Label }) public resetLabel: Label = null @property({ type: ProgressBar }) public progressBar: ProgressBar = null @property({ type: Node }) public resetNode: Node = null private _dotsCounter: number = 0 private _dotsInterval: any = null //剧情选项相关 private options: any[] = [] private optionsLabel: string[] = [] private storyHeight = 510 private selectedOptionNum: number = 4 //排名相关 private rankInfo: any[] = [] //角色信息 private npc: any[] = null private player: any[] = null //简历角色名-node 字典 private roleNodes = {} private modifyHp: any[] //确认正式同步后台数据正式开始标签 private realStart: boolean = false //通知相关 private noticeStr: string = "" @property(AudioClip) public moveClip: AudioClip = null! @property(AudioClip) public showOptionsClip: AudioClip = null! @property(AudioClip) public selectedOptionClip: AudioClip = null! public audioSource: AudioSource = null! @property(AudioSource) public bg1AudioSource: AudioSource = null! @property(AudioSource) public bg2AudioSource: AudioSource = null! private isPlayBgm1 = true public originUrl:string = null public hostName:string = null private globalUrl:string = 'https://airpg1.izouma.com' private globalHostname:string = 'airpg1.izouma.com' private moveNum:number = 1 init() { //播放第一个背景音乐 this.bg1AudioSource.play() console.log(location.href) if(location.hostname === 'localhost') { this.originUrl = this.globalUrl this.hostName = this.globalHostname } else{ this.originUrl = location.origin this.hostName = location.hostname } let roomId = new URLSearchParams(location.search).get("roomId") if (roomId != null) { this.roomId = Number(roomId) console.log("get roomId from URL:" + roomId) } else { console.log("use defult roomId") } const roomUrl = `${this.originUrl}/api/room/${this.roomId}` console.log(roomUrl) fetch(roomUrl) .then((response) => { if (!response.ok) { throw new Error("Network response was not ok") } return response.json() }) .then((data) => { //获取游戏ID,房间信息 console.log(data.currentGameId + " " + data.active) this.gameId = data.currentGameId this.noticeStr = data.notice if (this.socketClient.socket) { this.socketClient.socket.disconnect() } else { this.socketClient.gameId = this.gameId this.socketClient.initSocketConnection() } const gameUrl = `${this.originUrl}/api/game/${this.gameId}` fetch(gameUrl) .then((response) => { if (!response.ok) { throw new Error("Network response was not ok") } return response.json() }) .then((data) => { console.log(data) const historyUrl = `${this.originUrl}/api/game/${this.gameId}/history` this.gameInfo = data this.resetNum = data.resetNum fetch(historyUrl) .then((response) => { if (!response.ok) { throw new Error("Network response was not ok") } return response.json() }) .then((data) => { console.log("history") console.log(data) this.historyInfo = data //根据游戏信息与历史信息更新房间组件 this.updateGame(this.gameInfo, this.historyInfo) }) .catch((error) => { console.error("Fetch error:", error) }) }) .catch((error) => { console.error("Fetch error:", error) }) }) .catch((error) => { console.error("Fetch error:", error) }) this.initRank(roomUrl + "/survivalRank") } initRank(url: string) { fetch(url) .then((response) => { if (!response.ok) { throw new Error("Network response was not ok") } return response.json() }) .then((data) => { console.log("rank") console.log(data) this.rankInfo = data //根据游戏信息与历史信息更新房间组件 this.rankCtr.updateRank(this.rankInfo) }) .catch((error) => { console.error("Fetch error:", error) }) } start() { //加载音频播放组件 this.audioSource = this.node.getComponent(AudioSource) this.startResetAnimation() // this.scheduleOnce(() => { this.accessTargetComponent() }, 0.1) this.scheduleOnce(() => { this.init() }, 10) this.processCtr.init(this) this.schedule(this.chackGameInfo, 5 * 60) } chackGameInfo() { const roomUrl = `${this.originUrl}/api/room/${this.roomId}` fetch(roomUrl) .then((response) => { if (!response.ok) { throw new Error("Network response was not ok") } return response.json() }) .then((data) => { //获取游戏ID,房间信息 console.log(data.currentGameId + " " + data.active) if (this.gameId == data.currentGameId) { if (this.noticeStr == data.notice) { return } else { this.noticeCtr.updateText(data.notice) this.noticeStr = data.notice } } else { director.loadScene("game") } }) .catch((error) => { console.error("Fetch error:", error) }) } update(deltaTime: number) {} onButtonClick() { //director.loadScene(director.getScene().name); // this.exchangeTime() // this.noticeCtr.updateText( // "在 Cocos Creator 编辑器中,将富文本组件拖放到脚本的 richText 属性框中。运行游戏,现在你应该可以看到富文本的内容逐字出现。" // ) //options初始化 this.moveNum++ // this.optionsCtr.showVotes(this.moveNum) // this.optionsCtr.selectOption(3) //重置测试 //this.startResetAnimation() //测试排行切换 // this.rankCtr.updateRankTop() //测试剧情Pannel移动 // this.optionsAppearAction() //测试被攻击 this.roleCtr.attack(this.roleCtr.node.getChildByName("role_icon_npc1")) // this.roleCtr.attack(this.roleCtr.node.getChildByName('role_icon_npc3')); this.roleCtr.treat(this.roleCtr.node.getChildByName("role_icon_npc2")) // this.roleCtr.newPlayer(this.roleCtr.node.getChildByName('role_icon_player1')); } //重启动画相关 startResetAnimation() { // Fade in the black mask if (this._dotsInterval) { clearInterval(this._dotsInterval) this._dotsInterval = null } this.resetNode.getComponent(UIOpacity).opacity = 0 this.progressBar.progress = 0 this.resetNode.active = true tween(this.resetNode.getComponent(UIOpacity)) .to(1, { opacity: 255 }) .call(() => this.showResetTextAndProgress()) .start() } showResetTextAndProgress() { this.updateDots() this._dotsInterval = setInterval(() => this.updateDots(), 500) // Simulate progress let progress = 0 const progressInterval = setInterval(() => { progress += 0.1 this.progressBar.progress = progress if (progress >= 1) { clearInterval(progressInterval) this.endResetAnimation() } }, 1000) } updateDots() { const dots = ".".repeat((this._dotsCounter % 3) + 1) this.resetLabel.string = `世界重置中${dots}` this._dotsCounter++ } endResetAnimation() { clearInterval(this._dotsInterval) // Fade out the black mask and reset tween(this.resetNode.getComponent(UIOpacity)) .to(1, { opacity: 0 }) .call(() => { this.progressBar.progress = 0 this.resetNode.active = false }) .start() } handleStoryPannelMove(data: any) { this.storyPannelCtr.storyContent = data.data this.storyPannelCtr.isOnProcess = true //0:初始化后第一次收到剧情,直接展示剧情 //3:接收到了最后一次剧情展示后清除队列 if (this.storyActionStatus == 0 || this.storyActionStatus == 3) { if (this.storyPannelCtr) { this.storyPannelCtr.updateStory(data.data) } else { console.log("plot error") } this.realStart = true this.noticeCtr.updateText(this.noticeStr) } else if (this.storyActionStatus == 2) { this.optionsAppearAction() } } //处理收到选项事件 handleOptions(data: any) { if (this.realStart) { this.options = data.data //更新optionsLabel for (let i = 0; i < 4; i++) { this.optionsLabel[i] = this.options[i].content } if (this.storyActionStatus === 0 || this.storyActionStatus === 3) { this.optionsAppearAction() } } else { console.log('handleOptions处理完成处理下一个') this.processCtr.doneProcessing() } } //处理收到被选项事件 handleVoteResul(data: any) { if (this.realStart) { this.modifyHp = data.data.modifyHp for (let i = 0; i < 4; i++) { if (this.options[i].content == data.data.content) { this.selectedOptionNum = i } } if (this.storyActionStatus === 1) { this.optionsAppearAction() } } else { console.log('handleVoteResul处理完成处理下一个') this.processCtr.doneProcessing() } } //处理新玩家加入 handleNewPlayer(data: any) { this.player.push(data.data) let playerIndex = this.player.length - 1 if (playerIndex === 8) { return } const rolePannelName: string = "role_icon_player" + (playerIndex + 1) const role: Node = this.roleCtr.node.getChildByName(rolePannelName) role.active = true console.log("new player@!!!!!!:" + data.data) this.roleCtr.newPlayer(role, this.player[playerIndex]) this.roleNodes[data.data.name] = role console.log('handleNewPlayer处理完成处理下一个') this.processCtr.doneProcessing() } //单独处理死亡 handleDeath(data: any) { this.roleCtr.deathNodes = data.data this.processCtr.doneProcessing() } //处理收到state时间:如果realStart == false ,则更新游戏初始状态 handleStateResul(data: any) { if (!this.realStart) { console.log("处理开始前的status") this.historyInfo.push(data.data) this.updateGame(this.gameInfo, this.historyInfo) console.log('handleStateResul处理完成处理下一个') this.processCtr.doneProcessing() } else{ console.log("处理开始后的status完成,处理下一个") this.processCtr.doneProcessing() } } //处理重置 handleReset(data: any) { //director.reset() director.loadScene("game") this.processCtr.doneProcessing() } //处理投票进度 handleVotes(data: any) { if(this.realStart && this.storyActionStatus == 1 && Math.max(...(data.data as number[]))>0) { let votes:number[] = data.data this.optionsCtr.showVotes(votes,this.processCtr) } else { console.log('handle votes do nothing:'+this.realStart+" "+this.storyActionStatus+" "+Math.max(...(data.data as number[]))) this.processCtr.doneProcessing() } } //处理所有剧情、选项的移动逻辑 optionsAppearAction() { console.log("storyActionStatus:" + this.storyActionStatus) //展示选项,折叠剧情,开启投票统计 if (this.storyActionStatus == 0) { const storyNode = this.storyPannelCtr.node const storyUI = storyNode.getComponent(UITransform) if (storyUI.height > this.storyHeight) { this.initStoryPosition = storyNode.position.clone() tween(this.storyPannelCtr.node) .by(0.5, { position: new Vec3(0, storyUI.height - this.storyHeight, 0) }) .call(() => { this.optionsCtr.setOptions(this.optionsLabel) //此处需要额外处理死亡,因为前一步有演示播放攻击动画,不排除死亡名单中有攻击后自然死亡角色 this.roleCtr.dealDeath(this.roleNodes) }) .start() } else { this.optionsCtr.setOptions(this.optionsLabel) } this.playShowOptionsOneShot() console.log("展示选项处理完成,处理下一个") this.processCtr.doneProcessing() this.storyActionStatus = 1 } else if (this.storyActionStatus === 1) { //选中备选项 this.optionsCtr.resetVotes() console.log("selectedOptionNum:" + this.selectedOptionNum) this.optionsCtr.selectOption(this.selectedOptionNum + 1) this.playSelectedOneShot() console.log("选中选项处理完成,处理下一个") this.processCtr.doneProcessing() this.storyActionStatus = 2 } else if (this.storyActionStatus === 2) { this.scheduleOnce(() => { // 隐藏未被选中选项 for (let i = 0; i < 4; i++) { var optionNode: Node = this.optionsCtr.optionNodes[i] if (i !== this.selectedOptionNum) { const optionTween = tween(optionNode.getComponent(UIOpacity)) optionTween.to(2.5, { opacity: 0 }).start() } } //播放移动声音 this.playMoveOneShot() //隐藏上边剧情面板 tween(this.storyPannelCtr.node.parent.getComponent(UIOpacity)) .to(2.5, { opacity: 0 }) .call(() => { this.storyPannelCtr.node.getComponent(Label).string = "" this.storyPannelCtr.node.position = new Vec3(0, 0, 0) this.initOptionsPosition = this.optionsCtr.node.position.clone() const storyY = this.storyPannelCtr.node.parent.position.y const optionsY = this.optionsCtr.node.position.y const selectedOption = this.optionsCtr.optionNodes[this.selectedOptionNum] const selectedOptionHeight = selectedOption.getComponent(UITransform).contentSize.height //选中选项上移 tween(this.optionsCtr.node) .by(1.5, { position: new Vec3( 0, storyY - optionsY - selectedOption.position.y - selectedOptionHeight / 2, 0 ) }) .call(() => { this.storyPannelCtr.node.parent.getComponent(UIOpacity).opacity = 255 //剧情面板在下方出现并开始展示 tween(this.storyPannelCtr.node) .by(0.01, { position: new Vec3(0, -selectedOptionHeight, 0) }) .call(() => { this.storyPannelCtr.isOnProcess = true this.storyPannelCtr.updateStory(this.storyPannelCtr.storyContent) this.storyActionStatus = 3 //延时触发扣血加血动画 this.scheduleOnce(() => { this.updateHp() }, 2) }) .start() }) .start() }) .start() }, 2) } else if (this.storyActionStatus == 3) { const selectedOption = this.optionsCtr.optionNodes[this.selectedOptionNum] //顶部备选项消失 tween(selectedOption.getComponent(UIOpacity)) .to(0.5, { opacity: 0 }) .call(() => { //重置选项位置与透明度 this.optionsCtr.node.position = this.initOptionsPosition // this.optionsCtr.node.getComponent(UIOpacity).opacity = 0 // this.optionsCtr.node.getChildByName() // 这是被选中的选项 selectedOption.getChildByName("text_option").getComponent(Label).color = Color.WHITE // 移除选中背景图 const spriteComponent = selectedOption.getComponent(Sprite) if (spriteComponent) { spriteComponent.spriteFrame = this.optionsCtr.notSelectedSprite.spriteFrame } //剧情上移开始展示 tween(this.storyPannelCtr.node) .to(0.2, { position: new Vec3(0, 0, 0) }) .call(() => { this.storyActionStatus = 0 this.optionsAppearAction() console.log("剧情上移处理完成,处理下一个") this.processCtr.doneProcessing() }) .start() }) .start() } } updateGame(gameInfo: any, historyInfo: any[]) { if (gameInfo && history) { this.updateDateByHistory(historyInfo[0], historyInfo[historyInfo.length - 1]) const firstHistory = historyInfo[0] const lastHistory = historyInfo[historyInfo.length - 1] const charactors: any[] = lastHistory.charactors //初始化剧情!!! //触发剧情框文字 if (this.storyPannelCtr) { // this.storyPannelCtr.updateStory(lastHistory.plot) } else { console.log("plot error") } //初始化角色!!! this.player = [] this.npc = [] this.roleNodes = {} for (let i = 0; i < charactors.length; i++) { if (charactors[i].danmuUserId) { this.player.push(charactors[i]) } else { this.npc.push(charactors[i]) } } for (let i = 0; i < this.npc.length; i++) { const rolePannelName: string = "role_icon_npc" + (i + 1) console.log(rolePannelName) const role: Node = this.roleCtr.node.getChildByName(rolePannelName) role.active = true this.roleCtr.newPlayer(role, this.npc[i]) this.roleNodes[this.npc[i].name] = role } for (let i = 0; i < this.player.length; i++) { const rolePannelName: string = "role_icon_player" + (i + 1) const role: Node = this.roleCtr.node.getChildByName(rolePannelName) role.active = true this.roleCtr.newPlayer(role, this.player[i]) this.roleNodes[this.player[i].name] = role } } else { throw new Error("gameInfo or historyInfo is null") } } //更新日期 updateDateByHistory(firstHistory: any, lastHistory: any) { //初始化标题与天数!!! const time: string = lastHistory.time if (time === "evening") { this.sunIsUp = false this.exchangeTime() } this.titlePannel.getChildByName("text_title").getComponent(Label).string = this.gameInfo.name this.titlePannel.getChildByName("text_gameNo").getComponent(Label).string = "No." + (this.resetNum + 1) // 计算日期差(结果单位为毫秒) const lastHistoryDate = new Date(lastHistory.date) const firstHistoryDate = new Date(firstHistory.date) const differenceInMilliseconds = lastHistoryDate.getTime() - firstHistoryDate.getTime() // 将毫秒转为天数 const differenceInDays = differenceInMilliseconds / (1000 * 60 * 60 * 24) + 1 this.dayLabel.string = "第" + differenceInDays + "天" } updateDateByWS(data: any) { const time: string = data.data.time if (time === "evening") { this.sunIsUp = false this.exchangeTime() } else { this.sunIsUp = true this.exchangeTime() } this.titlePannel.getChildByName("text_title").getComponent(Label).string = this.gameInfo.name this.titlePannel.getChildByName("text_gameNo").getComponent(Label).string = "No." + (this.resetNum + 1) // 计算日期差(结果单位为毫秒) const lastHistoryDate = new Date(data.data.date) const firstHistoryDate = new Date(this.historyInfo[0].date) const differenceInMilliseconds = lastHistoryDate.getTime() - firstHistoryDate.getTime() // 将毫秒转为天数 const differenceInDays = differenceInMilliseconds / (1000 * 60 * 60 * 24) + 1 this.dayLabel.string = "第" + differenceInDays + "天" console.log("更新日期处理完成,处理下一个") this.processCtr.doneProcessing() } exchangeTime() { if (this.sunIsUp) { this.MoonAnim.play("moonUp") this.MoonAnim.on(Animation.EventType.FINISHED, () => { this.SunAnim.play("sunDown") this.MoonAnim.off(Animation.EventType.FINISHED) }) this.sunIsUp = false } else { this.SunAnim.play("sunUp") this.SunAnim.on( Animation.EventType.FINISHED, () => { this.MoonAnim.play("moonDown") this.SunAnim.off(Animation.EventType.FINISHED) }, this ) this.sunIsUp = true } } updateHp() { if (this.modifyHp) { let changBgm2 = false for (let i = 0; i < this.modifyHp.length; i++) { const roleNode: Node = this.roleNodes[this.modifyHp[i].name] const pbNode: Node = roleNode.getChildByName("progressbar_hp") const orgHp = pbNode.getComponent(ProgressBar).progress //已死亡不触发 if (orgHp == 0) { continue } let newHp = orgHp + this.modifyHp[i].changeValue / 100 if (newHp <= 0) { newHp = 0 } else if (newHp > 1) { newHp = 1 } pbNode.getComponent(ProgressBar).progress = newHp console.log("updata HP: name:" + this.modifyHp[i].name + " orgHp:" + orgHp + " newHp:" + newHp) console.log(pbNode) if (this.modifyHp[i].changeValue > 0) { this.roleCtr.treat(roleNode) } else { this.roleCtr.attack(roleNode) //发生战斗,更换BGM changBgm2 = true } } if (changBgm2 && this.isPlayBgm1) { this.bg1AudioSource.pause() this.bg2AudioSource.play() this.isPlayBgm1 = false } else if (!changBgm2 && !this.isPlayBgm1) { this.bg2AudioSource.pause() this.bg1AudioSource.play() this.isPlayBgm1 = true } } } playMoveOneShot() { this.audioSource.playOneShot(this.moveClip, 0.2) } playSelectedOneShot() { this.audioSource.playOneShot(this.selectedOptionClip, 0.2) } playShowOptionsOneShot() { this.audioSource.playOneShot(this.showOptionsClip, 0.2) } }