Files

752 lines
20 KiB
JavaScript
Raw Permalink Normal View History

2026-05-24 10:21:26 +08:00
"use strict";
cc._RF.push(module, '375b0oNLwhN27LKAfsLuxn3', 'cmp.plot.dialogue.wnd');
// Scripts/mod/plot/cmps/cmp.plot.dialogue.wnd.js
"use strict";
/******************************************************************
* Copyright(C) 2019 - 2020 Nx Studio
*
* 剧情对话场景
*
*
******************************************************************/
var BridgeWindow = require("bridge.window");
var NxSpine = require("nx.fx.spine");
var BattleController = require("battle_controller");
var DARK = cc.color(57, 57, 57, 255);
cc.Class({
"extends": BridgeWindow,
properties: {
nodTouch: {
"default": null,
type: cc.Node
},
nodBG: {
"default": null,
type: cc.Node
},
spAnim: {
"default": null,
type: NxSpine
},
nodRoles: {
"default": null,
type: cc.Node
},
spAnimMiddle: {
"default": null,
type: NxSpine
},
nodDialog: {
"default": null,
type: cc.Node
},
nodTell: {
"default": null,
type: cc.Node
},
nodSkip: {
"default": null,
type: cc.Button
},
nodTemps: {
"default": null,
type: cc.Node
}
},
// 重载:参数打开
onOpenConfigs: function onOpenConfigs(_params) {
// 当前无剧本
this.scenario = nx.bridge.plot.vget("info");
if (nx.dt.objEmpty(this.scenario)) {
nx.error("$PlotDialogue:对白失败,当前无剧本!");
this.delayClose();
return;
}
// 初始化环境
this.doPrepare();
// 视图监听
nx.bridge.plot.vbind(this, [["step", this.onStepChanged.bind(this)], ["key", this.onKeyChanged.bind(this)]]);
// 自动开始
var step = nx.bridge.plot.vget("step");
if (step < 0) {
nx.bridge.plot.next();
}
nx.dg = this;
},
// 关闭
onDisable: function onDisable() {
this.stopSFX();
this.stopVoice();
// 视图解绑
nx.bridge.plot.vunbind(this);
},
// 初始化环境
doPrepare: function doPrepare() {
var _this = this;
// UI初始化
this.nodDialog.active = false;
this.nodTell.active = false;
// 可否跳过
this.nodSkip.node.active = false;
if (this.scenario && this.scenario.skip) {
this.nodSkip.interactable = false;
nx.tween.delayFadeIn(this.nodSkip, "", 3, 1, function () {
_this.nodSkip.interactable = true;
});
}
// 加载角色模型
this.loadRoles();
},
// 执行单步
doStep: function doStep(_data) {
// 无效
if (nx.dt.objEmpty(_data)) {
return;
}
// 当前步信息
this.step = _data;
// 背景
if (nx.dt.objNEmpty(_data.bg)) {
this.stepBG(_data.bg);
return;
}
// 背景音乐
if (nx.dt.objNEmpty(_data.bgm)) {
this.stepBGM(_data.bgm);
return;
}
// Spine
if (nx.dt.objNEmpty(_data.spine)) {
this.stepSpine(_data.spine);
return;
}
// Spine中间层
if (nx.dt.objNEmpty(_data.spine_mid)) {
this.stepSpineMiddle(_data.spine_mid);
return;
}
// 讲述
if (nx.dt.objNEmpty(_data.tell)) {
this.stepTell(_data.tell);
return;
}
// 对白
if (nx.dt.arrNEmpty(_data.talks)) {
this.stepTalks(_data.talks);
return;
}
// 角色出现
if (nx.dt.objNEmpty(_data.appear)) {
this.stepAppear(_data.appear);
return;
}
// 角色离开
if (nx.dt.objNEmpty(_data.disappear)) {
this.stepDisAppear(_data.disappear);
return;
}
// 角色聚焦
if (nx.dt.objNEmpty(_data.focus)) {
this.stepFocus(_data.focus);
return;
}
// 窗体操作
if (nx.dt.objNEmpty(_data.window)) {
this.stepWindow(_data.window);
return;
}
// 等待事件
if (nx.dt.objNEmpty(_data.event)) {
this.stepEvent(_data.event);
return;
}
// 特殊操作
if (nx.dt.objNEmpty(_data.special)) {
this.stepSpecail(_data.special);
return;
}
// 无效处理
nx.error("$PlotDialogue:无效步骤! ", nx.dt.enjson(_data));
nx.bridge.plot.next();
},
// 步数改变
onStepChanged: function onStepChanged(_step, _, _init) {
var step = nx.bridge.plot.stepData();
if (nx.dt.objEmpty(step)) {
return;
}
// 执行
this.doStep(step);
},
// ========================================================================
// 角色相关
// ========================================================================
// 加载角色模型
loadRoles: function loadRoles() {
var _this2 = this;
this.nodRoles.removeAllChildren(true);
var roles = nx.dt.objClone(this.scenario.roles);
var temp = nx.gui.find(this.nodTemps, "roleT");
this.scenario.roles.forEach(function (role) {
var node = cc.instantiate(temp);
node.opacity = 0;
node.parent = _this2.nodRoles;
node.scale = role[2] || 1.0;
node.offset = cc.v2(role[3] || 0, role[4] || 0);
node.y = role[4] || 0;
});
},
// ========================================================================
// 单步处理:角色
// ========================================================================
// 单步执行:角色出现
stepAppear: function stepAppear(_data) {
var node = this.nodRoles.children[_data.who];
if (!node) {
nx.error("$PlotDialogue:角色未找到! ", _data.who);
nx.bridge.plot.next();
return;
}
// 出现
var self = this;
var appear = function appear() {
// 出现方式
node.opacity = 255;
if (_data.way == "right") {
node.x = self.node.width / 2;
} else if (_data.way == "left") {
node.x = -self.node.width / 2;
}
// 自动布局
var nodes = [];
self.nodRoles.children.forEach(function (_n) {
if (_n && _n.opacity > 0) {
nodes.push(_n);
}
});
var space = self.scenario.spaces[nodes.length - 1];
for (var i = 0; i < nodes.length; ++i) {
var rd = nodes[i];
var pos = cc.v2(space[i], rd.y);
nx.tween.moveTo(rd, "", _data.secs, pos);
}
// 延迟下一步
self.scheduleOnce(function () {
nx.bridge.plot.next();
}, _data.secs * 0.9);
};
// 模型载入
if (!node.spine) {
var role = this.scenario.roles[_data.who];
var path = cc.path.join("resDB/models", role[1], "show");
node.spine = nx.gui.getComponent(node, "", "nx.fx.spine");
node.spine.play(path, _data.action, function (_event) {
if (_event == "start") {
appear();
}
}, true);
nx.gui.setColor(node.spine, "", DARK);
return;
}
appear();
},
// 单步执行:角色离开
stepDisAppear: function stepDisAppear(_data) {
var node = this.nodRoles.children[_data.who];
if (!node) {
nx.error("$PlotDialogue:角色未找到! ", _data.who);
nx.bridge.plot.next();
return;
}
// 自动布局
var self = this;
var autoSpace = function autoSpace() {
var nodes = [];
self.nodRoles.children.forEach(function (_n) {
if (_n && _n.opacity > 0) {
nodes.push(_n);
}
});
var space = self.scenario.spaces[nodes.length - 1];
for (var i = 0; i < nodes.length; ++i) {
var rd = nodes[i];
var pos = cc.v2(space[i], rd.y);
nx.tween.moveTo(rd, "", _data.secs / 2, pos);
}
// 延迟下一步
self.scheduleOnce(function () {
nx.bridge.plot.next();
}, _data.secs * 0.45);
};
nx.tween.fadeOut(node, "", _data.secs / 2, function () {
autoSpace();
});
},
// 单步执行:角色聚焦
stepFocus: function stepFocus(_data) {
// 音效
this.playSFX(_data.sfx, !!_data.loop);
// 重复聚焦
if (this.focus == _data.who) {
nx.bridge.plot.next();
return;
}
// 如果聚焦动作为空,则只改变对白对象
if (nx.dt.strEmpty(_data.action)) {
this.focus = _data.who;
var _role = this.scenario.roles[this.focus];
nx.gui.setString(this.nodDialog, "title/name", nx.text.getKey(_role[0]));
nx.gui.setString(this.nodDialog, "content", "");
this.scheduleOnce(function () {
nx.bridge.plot.next();
}, 0.1);
return;
}
// 老的还原
if (nx.dt.numGood(this.focus)) {
var _node = this.nodRoles.children[this.focus];
var _spnode = nx.gui.find(_node, "sp");
cc.Tween.stopAllByTarget(_spnode);
cc.tween(_spnode).to(_data.secs || 0.1, {
scale: 1,
color: DARK
}).start();
}
// 新的聚焦
this.focus = _data.who;
if (!nx.dt.numGood(this.focus)) {
return;
}
var node = this.nodRoles.children[this.focus];
var spnode = nx.gui.find(node, "sp");
cc.Tween.stopAllByTarget(spnode);
cc.tween(spnode).to(_data.secs || 0.1, {
scale: 1.05,
color: cc.Color.WHITE
}).call(function () {
nx.bridge.plot.next();
}).start();
if (nx.dt.strNEmpty(_data.action) && node.spine) {
node.spine.action(_data.action, true);
}
// 说话者信息
var role = this.scenario.roles[this.focus];
nx.gui.setString(this.nodDialog, "title/name", nx.text.getKey(role[0]));
nx.gui.setString(this.nodDialog, "content", "");
},
// ========================================================================
// 单步处理:动画
// ========================================================================
// 单步执行
stepSpine: function stepSpine(_data) {
var _this3 = this;
var path = cc.path.join("prefab/plot/spines", _data.file);
var done = function done() {
nx.bridge.plot.next();
};
// 播放
nx.gui.setOpacity(this.spAnim, "", 255);
this.spAnim.play(path, _data.action, function (_key) {
if (_key == "start") {
_this3.playSFX(_data.sfx, !!_data.loop);
return;
}
if (_key == "complete" && _data.wait) {
done();
return;
}
}, _data.loop);
if (!_data.wait) {
done();
}
},
// 单步执行
stepSpineMiddle: function stepSpineMiddle(_data) {
var _this4 = this;
var self = this;
var path = cc.path.join("prefab/plot/spines", _data.file);
var done = function done() {
self.spAnimMiddle.stop();
nx.bridge.plot.next();
};
// 播放
nx.gui.setOpacity(this.spAnimMiddle, "", 255);
this.spAnimMiddle.play(path, _data.action, function (_key) {
if (_key == "start") {
_this4.playSFX(_data.sfx, !!_data.loop);
return;
}
if (_key == "complete" && _data.wait) {
done();
return;
}
}, _data.loop);
if (!_data.wait) {
done();
}
},
// ========================================================================
// 单步处理:背景
// ========================================================================
// 单步执行
// { "bg": { "file":"bg002", "op":"fadeSpine", "opsecs": 5.3 } },
stepBG: function stepBG(_data) {
var _this5 = this;
var path = cc.path.join("prefab/plot/images", _data.file);
// 延迟更变背景,但是不延迟
if (_data.op == "delay") {
this.scheduleOnce(function () {
nx.gui.setSpriteFrame(_this5.nodBG, "", path);
}, _data.opsecs || 0.3);
nx.bridge.plot.next();
return;
}
nx.gui.setSpriteFrame(this.nodBG, "", path);
// 背景&&动画 淡进淡出切换
if (_data.op == "fadeSpine") {
nx.tween.fadeOut(this.spAnim, "", _data.opsecs || 0.5);
nx.tween.fadeIn(this.nodBG, "", _data.opsecs || 0.5, function () {
nx.bridge.plot.next();
});
return;
}
// 直接下一步
nx.bridge.plot.next();
},
// ========================================================================
// 单步处理:背景音乐
// ========================================================================
// 单步执行
// { "bg": { "file":"bg002" } },
stepBGM: function stepBGM(_data) {
var cmp = nx.gui.getComponent(this, "", "nx.fx.BGM");
if (cmp) {
cmp.reset();
cmp.bgmRK = cc.path.join("prefab/plot/audios", _data.file);
cmp.reActive();
}
// 直接下一步
nx.bridge.plot.next();
},
// ========================================================================
// 单步处理:对白
// ========================================================================
// 单步执行
stepTalks: function stepTalks(_data) {
this.talks = nx.dt.objClone(_data);
this.doTalk();
},
// 单步对话
doTalk: function doTalk() {
if (nx.dt.arrEmpty(this.talks)) {
this.nodDialog.active = false;
nx.bridge.plot.next();
return;
}
var data = this.talks.shift();
if (nx.dt.objEmpty(data)) {
this.doTalk();
return;
}
this.nodDialog.active = true;
// 当前动作
var node = this.nodRoles.children[this.focus];
if (node && node.spine) {
// 动作
if (nx.dt.strNEmpty(data.action)) {
node.spine.action(data.action, true);
}
// 播放速度
if (nx.dt.numPositive(data.speed, false)) {
node.spine.setTimeScale(data.speed);
}
}
// 内容
if (data.words && nx.dt.strNEmpty(data.words)) {
var txt = nx.text.getKey(data.words);
txt = nx.text.formatS("<outline color=black width=2>%s</outline>", txt);
var cmp = nx.gui.setString(this.nodDialog, "content", txt);
if (cmp) {
cmp.fontSize = data.fontSize || 22;
cmp.lineHeight = cmp.fontSize * 1.5;
var scale = nx.gui.getComponent(cmp, "", "nx.fx.local.text");
if (scale) {
scale.autoScale();
}
}
}
// 抖动
if (nx.dt.objNEmpty(data.shake)) {
var secs = data.shake.secs || 0.5;
var offset = cc.v2(data.shake.x || 10, data.shake.y || 10);
nx.tween.shake(this.nodDialog, "content", secs, offset);
}
// 语音
this.playVoice(data.voice);
// 继续
nx.gui.setActive(this.nodDialog, "continue", this.talks.length > 1);
},
// ========================================================================
// 单步处理:窗体
// ========================================================================
// 单步执行
stepWindow: function stepWindow(_data) {
// 关闭窗体
if (nx.dt.strNEmpty(_data.close)) {
nx.bridge.closePanel(_data.close);
}
// 打开窗体
if (nx.dt.strNEmpty(_data.open)) {
nx.bridge.createPanel(_data.open, _data.args || {});
}
nx.bridge.plot.next();
},
// ========================================================================
// 单步处理:事件
// ========================================================================
// 单步执行
stepEvent: function stepEvent(_data) {
this.wkey = _data.key || "";
},
// 触发事件改变
onKeyChanged: function onKeyChanged(_key, _, _init) {
if (_init || nx.dt.strEmpty(_key)) {
return;
}
if (this.wkey == _key) {
nx.bridge.plot.next();
}
},
// ========================================================================
// 单步处理:特殊操作
// ========================================================================
// 单步执行
stepSpecail: function stepSpecail(_data) {
var _this6 = this;
if (nx.dt.strEmpty(_data.key)) {
nx.error("$PlotDialogue:特殊操作失败,关键字缺失!");
nx.bridge.plot.next();
return;
}
// 全隐
if (_data.key == "hide") {
nx.bridge.plot.next();
this.scheduleOnce(function () {
_this6.node.opacity = 0;
_this6.nodTouch.active = false;
nx.gui.getComponent(_this6, "", cc.BlockInputEvents).enabled = false;
}, _data.delay || 0.1);
return;
}
// 全显
if (_data.key == "show") {
this.node.opacity = 255;
this.nodTouch.active = true;
nx.gui.getComponent(this, "", cc.BlockInputEvents).enabled = true;
nx.bridge.plot.next();
return;
}
// 首战
if (_data.key == "fstBattle") {
var BC = BattleController.getInstance();
BC.requestUseHeroBattle();
this.wkey = _data.event;
return;
}
},
// ========================================================================
// 单步处理:讲述
// ========================================================================
// 单步讲述
stepTell: function stepTell(_data) {
if (nx.dt.objEmpty(_data)) {
this.nodTell.active = false;
nx.bridge.plot.next();
return;
}
this.nodTell.active = true;
var txt = nx.text.getKey(_data.words);
nx.gui.setString(this.nodTell, "txt", txt);
var self = this;
nx.tween.fadeIn(this.nodTell, "txt", _data.fadeIn, function () {
self.scheduleOnce(function () {
nx.tween.fadeOut(self.nodTell, "txt", _data.fadeOut, function () {
self.nodTell.active = false;
nx.bridge.plot.next();
});
}, _data.delay);
});
this.playVoice(_data.voice);
},
// ========================================================================
// 音效处理
// ========================================================================
// 互斥播放
playSFX: function playSFX(_sfx, _loop) {
var _this7 = this;
if (_loop === void 0) {
_loop = true;
}
if (nx.dt.strEmpty(_sfx)) {
return;
}
var path = cc.path.join("prefab/plot/audios", _sfx);
// 老音效判断
if (this.sfxAction) {
// 是否已经结束
if (!nx.audio.isSFXPlaying(this.sfxAction.handle)) {
this.sfxAction.path = "";
this.sfxAction.handle = -1;
}
// 重复播放
if (this.sfxAction.path == path) {
return;
}
// 关闭
if (this.sfxAction.handle > 0) {
nx.audio.stopSFX(this.sfxAction.handle);
}
this.sfxAction = null;
}
nx.audio.playSFX(path, _loop, function (_err, _id, _path) {
if (!_err) {
_this7.sfxAction = {
path: _path || path,
handle: _id
};
}
});
},
// 立即清理
stopSFX: function stopSFX() {
if (this.sfxAction) {
nx.audio.stopSFX(this.sfxAction.handle);
this.sfxAction = null;
}
},
// ========================================================================
// 语音处理
// ========================================================================
// 播放
playVoice: function playVoice(_voice) {
var _this8 = this;
if (nx.dt.strEmpty(_voice)) {
return;
}
var path = cc.path.join("prefab/plot/audios", _voice);
// 老音效判断
if (this.sfxVoice) {
// 是否已经结束
if (!nx.audio.isSFXPlaying(this.sfxVoice.handle)) {
this.sfxVoice.path = "";
this.sfxVoice.handle = -1;
}
// 重复播放
if (this.sfxVoice.path == path) {
return;
}
// 关闭
if (this.sfxVoice.handle > 0) {
nx.audio.stopVoice(this.sfxVoice.handle);
}
this.sfxVoice = null;
}
nx.audio.playVoice(path, function (_err, _id, _path) {
if (!_err) {
_this8.sfxVoice = {
path: _path || path,
handle: _id
};
}
});
},
// 语音关闭
stopVoice: function stopVoice() {
if (this.sfxVoice) {
nx.audio.stopSFX(this.sfxVoice.handle);
this.sfxVoice = null;
}
},
// ========================================================================
// 用户输入
// ========================================================================
// 点击跳过
onTouchSkip: function onTouchSkip() {
nx.bridge.plot.done();
this.close();
},
// 点击继续
onTouchContinue: function onTouchContinue() {}
});
cc._RF.pop();