///
var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
fiveLayer: function () {
// 注册插件
Mota.Plugin.register('fiveLayer_g', { init }, init);
// 创建新图层
function createCanvas(name, zIndex) {
if (!name) return;
var canvas = document.createElement('canvas');
canvas.id = name;
canvas.className = 'gameCanvas no-anti-aliasing';
// 编辑器模式下设置zIndex会导致加入的图层覆盖优先级过高
if (main.mode != 'editor') canvas.style.zIndex = zIndex || 0;
// 将图层插入进游戏内容
document.getElementById('gameDraw').appendChild(canvas);
var ctx = canvas.getContext('2d');
core.canvas[name] = ctx;
return canvas;
}
function init() {
var bg2Canvas = createCanvas('bg2', 20);
var fg2Canvas = createCanvas('fg2', 63);
// 大地图适配
core.bigmap.canvas = [
'bg2',
'fg2',
'bg',
'event',
'event2',
'fg',
'damage'
];
core.initStatus.bg2maps = {};
core.initStatus.fg2maps = {};
if (main.mode == 'editor') {
/*插入编辑器的图层 不做此步新增图层无法在编辑器显示*/
// 编辑器图层覆盖优先级 eui > efg > fg(前景层) > event2(48*32图块的事件层) > event(事件层) > bg(背景层)
// 背景层2(bg2) 插入事件层(event)之前(即bg与event之间)
document
.getElementById('mapEdit')
.insertBefore(bg2Canvas, document.getElementById('event'));
// 前景层2(fg2) 插入编辑器前景(efg)之前(即fg之后)
document
.getElementById('mapEdit')
.insertBefore(fg2Canvas, document.getElementById('ebm'));
// 原本有三个图层 从4开始添加
var num = 4;
// 新增图层存入editor.dom中
editor.dom.bg2c = core.canvas.bg2.canvas;
editor.dom.bg2Ctx = core.canvas.bg2;
editor.dom.fg2c = core.canvas.fg2.canvas;
editor.dom.fg2Ctx = core.canvas.fg2;
editor.dom.maps.push('bg2map', 'fg2map');
editor.dom.canvas.push('bg2', 'fg2');
// 创建编辑器上的按钮
var createCanvasBtn = name => {
// 电脑端创建按钮
var input = document.createElement('input');
// layerMod4/layerMod5
var id = 'layerMod' + num++;
// bg2map/fg2map
var value = name + 'map';
input.type = 'radio';
input.name = 'layerMod';
input.id = id;
input.value = value;
editor.dom[id] = input;
input.onchange = () => {
editor.uifunctions.setLayerMod(value);
};
return input;
};
var createCanvasBtn_mobile = name => {
// 手机端往选择列表中添加子选项
var input = document.createElement('option');
var id = 'layerMod' + num++;
var value = name + 'map';
input.name = 'layerMod';
input.value = value;
editor.dom[id] = input;
return input;
};
if (!editor.isMobile) {
var input = createCanvasBtn('bg2');
var input2 = createCanvasBtn('fg2');
// 获取事件层及其父节点
var child = document.getElementById('layerMod'),
parent = child.parentNode;
// 背景层2插入事件层前
parent.insertBefore(input, child);
// 不能直接更改背景层2的innerText 所以创建文本节点
var txt = document.createTextNode('背2');
// 插入事件层前(即新插入的背景层2前)
parent.insertBefore(txt, child);
// 向最后插入前景层2(即插入前景层后)
parent.appendChild(input2);
var txt2 = document.createTextNode('前2');
parent.appendChild(txt2);
} else {
var input = createCanvasBtn_mobile('bg2');
var input2 = createCanvasBtn_mobile('fg2');
// 手机端因为是选项 所以可以直接改innerText
input.innerText = '背景2';
input2.innerText = '前景2';
var parent = document.getElementById('layerMod');
parent.insertBefore(input, parent.children[1]);
parent.appendChild(input2);
}
}
maps.prototype._loadFloor_doNotCopy = function () {
return [
'firstArrive',
'eachArrive',
'blocks',
'parallelDo',
'map',
'bgmap',
'fgmap',
'bg2map',
'fg2map',
'events',
'changeFloor',
'afterBattle',
'afterGetItem',
'afterOpenDoor',
'cannotMove',
'enemy'
];
};
////// 绘制背景和前景层 //////
maps.prototype._drawBg_draw = function (
floorId,
toDrawCtx,
cacheCtx,
config
) {
config.ctx = cacheCtx;
core.maps._drawBg_drawBackground(floorId, config);
// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。
core.maps._drawFloorImages(
floorId,
config.ctx,
'bg',
null,
null,
config.onMap
);
core.maps._drawBgFgMap(floorId, 'bg', config);
if (config.onMap) {
core.drawImage(
toDrawCtx,
cacheCtx.canvas,
core.bigmap.v2 ? -32 : 0,
core.bigmap.v2 ? -32 : 0
);
core.clearMap('bg2');
core.clearMap(cacheCtx);
}
core.maps._drawBgFgMap(floorId, 'bg2', config);
if (config.onMap)
core.drawImage(
'bg2',
cacheCtx.canvas,
core.bigmap.v2 ? -32 : 0,
core.bigmap.v2 ? -32 : 0
);
config.ctx = toDrawCtx;
};
maps.prototype._drawFg_draw = function (
floorId,
toDrawCtx,
cacheCtx,
config
) {
config.ctx = cacheCtx;
// ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。
core.maps._drawFloorImages(
floorId,
config.ctx,
'fg',
null,
null,
config.onMap
);
core.maps._drawBgFgMap(floorId, 'fg', config);
if (config.onMap) {
core.drawImage(
toDrawCtx,
cacheCtx.canvas,
core.bigmap.v2 ? -32 : 0,
core.bigmap.v2 ? -32 : 0
);
core.clearMap('fg2');
core.clearMap(cacheCtx);
}
core.maps._drawBgFgMap(floorId, 'fg2', config);
if (config.onMap)
core.drawImage(
'fg2',
cacheCtx.canvas,
core.bigmap.v2 ? -32 : 0,
core.bigmap.v2 ? -32 : 0
);
config.ctx = toDrawCtx;
};
////// 移动判定 //////
maps.prototype._generateMovableArray_arrays = function (floorId) {
return {
bgArray: this.getBgMapArray(floorId),
fgArray: this.getFgMapArray(floorId),
eventArray: this.getMapArray(floorId),
bg2Array: this._getBgFgMapArray('bg2', floorId),
fg2Array: this._getBgFgMapArray('fg2', floorId)
};
};
}
},
uiRewrite: function () {
Mota.Plugin.register('ui_g', { init }, init);
function init() {
const { mainUi, fixedUi } = Mota.requireAll('var');
ui.prototype.drawBook = function () {
if (!core.isReplaying()) return mainUi.open('book');
};
ui.prototype._drawToolbox = function () {
if (!core.isReplaying()) return mainUi.open('toolbox');
};
ui.prototype._drawEquipbox = function () {
if (!core.isReplaying()) return mainUi.open('equipbox');
};
ui.prototype.drawFly = function () {
if (!core.isReplaying()) return mainUi.open('fly');
};
control.prototype.updateStatusBar_update = function () {
core.control.updateNextFrame = false;
if (!core.isPlaying() || core.hasFlag('__statistics__')) return;
core.control.controldata.updateStatusBar();
if (!core.control.noAutoEvents) core.checkAutoEvents();
core.control._updateStatusBar_setToolboxIcon();
core.clearRouteFolding();
core.control.noAutoEvents = true;
// 更新vue状态栏
updateVueStatusBar();
Mota.require('var', 'hook').emit('statusBarUpdate');
};
// todo: 多个状态栏分离与控制
control.prototype.showStatusBar = function () {
if (main.mode == 'editor') return;
core.removeFlag('hideStatusBar');
if (!fixedUi.hasName('statusBar')) {
fixedUi.open('statusBar');
}
core.dom.tools.hard.style.display = 'block';
core.dom.toolBar.style.display = 'block';
};
control.prototype.hideStatusBar = function (showToolbox) {
if (main.mode == 'editor') return;
// 如果原本就是隐藏的,则先显示
if (!core.domStyle.showStatusBar) this.showStatusBar();
if (core.isReplaying()) showToolbox = true;
fixedUi.closeByName('statusBar');
var toolItems = core.dom.tools;
core.setFlag('hideStatusBar', true);
core.setFlag('showToolbox', showToolbox || null);
if (
(!core.domStyle.isVertical && !core.flags.extendToolbar) ||
!showToolbox
) {
for (var i = 0; i < toolItems.length; ++i)
toolItems[i].style.display = 'none';
}
if (!core.domStyle.isVertical && !core.flags.extendToolbar) {
core.dom.toolBar.style.display = 'none';
}
};
}
function updateVueStatusBar() {
Mota.r(() => {
const status = Mota.require('var', 'status');
status.value = !status.value;
});
}
},
special: function () {
// 这个插件负责定义怪物属性
const specials = Mota.require('var', 'enemySpecials');
/**
* @param {string | ((enemy: Enemy) => string)} func
* @param {Enemy} enemy
*/
const fromFunc = (func, enemy) => {
return typeof func === 'string' ? func : func(enemy);
};
// 怪物特殊属性包含四个信息
// code: 索引,必须与该属性在数组内的索引一致,实际判断的时候也是根据索引判断,不会根据code判断
// name: 特殊属性名称,可以是一个函数,接受 enemy 作为参数,返回字符串
// desc: 特殊属性说明,也可以是一个函数,接受 enemy 作为参数,返回字符串
// color: 特殊属性颜色,会在怪物手册中显示出来
specials.push(
{
code: 0,
name: '空',
desc: '空',
color: '#fff'
},
{
code: 1,
name: '先攻',
desc: `怪物首先攻击`,
color: '#fc3'
},
{
code: 2,
name: '魔攻',
desc: '怪物攻击无视勇士的防御',
color: '#bbb0ff'
},
{
code: 3,
name: '坚固',
desc: '怪物防御不小于勇士攻击-1',
color: '#c0b088'
},
{
code: 4,
name: '2连击',
desc: '怪物每回合攻击2次',
color: '#fe7'
},
{
code: 5,
name: '3连击',
desc: '怪物每回合攻击3次',
color: '#fe7'
},
{
code: 6,
name: enemy => `${enemy.n ?? 4}连击`,
desc: enemy => `怪物每回合攻击${enemy.n ?? 4}次`,
color: '#fe7'
},
{
code: 7,
name: '破甲',
desc: enemy =>
`战斗前,怪物附加角色防御的${Math.floor(
100 * (enemy.breakArmor ?? core.values.breakArmor)
)}%作为伤害"`,
color: '#b67'
},
{
code: 8,
name: '反击',
desc: enemy =>
`战斗时,怪物每回合附加角色攻击的${Math.floor(
100 * (enemy.counterAttack ?? core.values.counterAttack)
)}%作为伤害,无视角色防御`,
color: '#fa4'
},
{
code: 9,
name: '净化',
desc: enemy =>
`战斗前,怪物附加角色护盾的${
enemy.purify ?? core.values.purify
}倍作为伤害`,
color: '#80eed6'
},
{
code: 10,
name: '模仿',
desc: `怪物的攻防和角色攻防相等`,
color: '#b0c0dd'
},
{
code: 11,
name: '吸血',
desc: enemy => {
const vampire = enemy.vampire || 0;
const hp = Mota.require('fn', 'getHeroStatusOn')('hp');
return (
`战斗前,怪物首先吸取角色的${Math.floor(
100 * vampire
)}%生命(约${Math.floor(hp * vampire)}点)作为伤害` +
(enemy.add ? ',并把伤害数值加到自身生命上' : '')
);
},
color: '#ff00d2'
},
{
code: 12,
name: '中毒',
desc: `战斗后,角色陷入中毒状态,每一步损失生命${core.data.values.poisonDamage}点`,
color: '#9e8'
},
{
code: 13,
name: '衰弱',
desc:
`战斗后,角色陷入衰弱状态,攻防暂时下降` +
(core.data.values.weakValue >= 1
? core.data.values.weakValue + '点'
: parseInt(core.data.values.weakValue * 100) + '%'),
color: '#bbb0ff'
},
{
code: 14,
name: '诅咒',
desc: '战斗后,角色陷入诅咒状态,战斗无法获得金币和经验',
color: '#bbeef0'
},
{
code: 15,
name: '领域',
desc: enemy =>
'经过怪物周围' +
(enemy.zoneSquare ? '九宫格' : '十字') +
'范围内' +
(enemy.range || 1) +
'格时自动减生命' +
(enemy.zone || 0) +
'点',
color: '#c677dd'
},
{
code: 16,
name: '夹击',
desc: '经过两只相同的怪物中间,角色生命值变成一半',
color: '#fff'
},
{
code: 17,
name: '仇恨',
desc: `战斗前,怪物附加之前积累的仇恨值作为伤害;战斗后,释放一半的仇恨值。(每杀死一个怪物获得${
core.data.values.hatred || 0
}点仇恨值)`,
color: '#b0b666'
},
{
code: 18,
name: '阻击',
desc: enemy =>
'经过怪物周围' +
(enemy.zoneSquare ? '九宫格' : '十字') +
'时自动减生命' +
(enemy.repulse || 0) +
'点,同时怪物后退一格',
color: '#8888e6'
},
{
code: 19,
name: '自爆',
desc: '战斗后角色的生命值变成1',
color: '#f66'
},
{
code: 20,
name: '无敌',
desc: `角色无法打败怪物,除非拥有十字架`,
color: '#aaa'
},
{
code: 21,
name: '退化',
desc: enemy =>
'战斗后角色永久下降' +
enemy.atkValue +
'点攻击和' +
enemy.defValue +
'点防御',
color: 'cyan'
},
{
code: 22,
name: '固伤',
desc: enemy =>
'战斗前,怪物对角色造成' +
enemy.damage +
'点固定伤害,未开启负伤时无视角色护盾。',
color: '#d8a'
},
{
code: 23,
name: '重生',
desc: '怪物被击败后,角色转换楼层则怪物将再次出现',
color: '#ffd'
},
{
code: 24,
name: '激光',
desc: enemy =>
'经过怪物同行或同列时自动减生命' + enemy.laser + '点',
color: '#dda0dd'
},
{
code: 25,
name: '光环',
desc: enemy =>
(enemy.range != null
? (enemy.haloSquare ? '该怪物九宫格' : '该怪物十字') +
enemy.haloRange +
'格范围内'
: '同楼层所有') +
'怪物生命提升' +
(enemy.hpBuff || 0) +
'%,攻击提升' +
(enemy.atkBuff || 0) +
'%,防御提升' +
(enemy.defBuff || 0) +
'%,' +
(enemy.haloAdd ? '可叠加' : '不可叠加'),
color: '#e6e099'
},
{
code: 26,
name: '支援',
desc: '当周围一圈的怪物受到攻击时将上前支援,并组成小队战斗。',
color: '#77c0b6'
},
{
code: 27,
name: '捕捉',
desc: enemy =>
'当走到怪物周围' +
(enemy.zoneSquare ? '九宫格' : '十字') +
'时会强制进行战斗。',
color: '#c0ddbb'
},
{
code: 28,
name: '特殊光环',
desc: enemy => {
let content = '';
enemy.specialHalo?.forEach((v, i) => {
content +=
' '.repeat(8) +
`${i + 1}. ${fromFunc(
specials[v].name,
enemy
)}: ${fromFunc(
specials[v].desc,
enemy
)}
`;
});
return (
`怪物周围${enemy.haloSquare ? '九宫格' : '十字'}${
enemy.haloRange
}格范围内所有怪物获得以下特殊属性,` +
`特殊属性数值间为${
enemy.specialMultiply ? '相乘' : '相加'
}关系:
` +
content
);
},
color: '#ff0'
}
);
},
battle: function () {
// 这个插件负责战斗相关内容
// 注意,对于电脑作者,极度推荐使用 vscode 进行代码编写,可以享受到新版的类型标注
// 同时由于类型标注过于复杂,样板编辑器无法部署,因此样板编辑器也无法享受到新API的代码补全等
// 因此极度推荐使用 vscode 进行编写
// --------------- 战后脚本
// enemy: DamageEnemy实例,也就是怪物本身
// x, y: 怪物坐标
Mota.rewrite(core.events, 'afterBattle', 'full', (enemy, x, y) => {
const { has } = Mota.Plugin.require('utils_g');
const floorId = core.status.floorId;
const special = enemy.info.special;
// 播放战斗动画
let animate = 'hand';
// 检查当前装备是否存在攻击动画
const equipId = core.getEquip(0);
if (equipId && (core.material.items[equipId].equip || {}).animate)
animate = core.material.items[equipId].equip.animate;
// 检查该动画是否存在SE,如果不存在则使用默认音效
if (!core.material.animates[animate]?.se)
core.playSound('attack.mp3');
// 战斗伤害
const info = enemy.calDamage(core.status.hero);
const damage = info.damage;
// 判定是否致死
if (damage >= core.status.hero.hp) {
core.status.hero.hp = 0;
core.updateStatusBar(false, true);
core.events.lose('战斗失败');
return;
}
// 扣减体力值并记录统计数据
core.status.hero.hp -= damage;
core.status.hero.statistics.battleDamage += damage;
core.status.hero.statistics.battle++;
// 获得金币
let money = enemy.enemy.money;
let exp = enemy.enemy.exp;
if (enemy.info.guard) {
enemy.info.guard.forEach(v => {
money += v.enemy.money;
exp += v.enemy.exp;
});
}
core.status.hero.money += money;
core.status.hero.statistics.money += money;
// 获得经验
core.status.hero.exp += exp;
core.status.hero.statistics.exp += exp;
const hint =
'打败 ' +
enemy.enemy.name +
',金币+' +
money +
',经验+' +
exp;
core.drawTip(hint, enemy.id);
// 中毒
if (special.includes(12)) {
core.triggerDebuff('get', 'poison');
}
// 衰弱
if (special.includes(13)) {
core.triggerDebuff('get', 'weak');
}
// 诅咒
if (special.includes(14)) {
core.triggerDebuff('get', 'curse');
}
// 仇恨怪物将仇恨值减半
if (special.includes(17)) {
core.setFlag(
'hatred',
Math.floor(core.getFlag('hatred', 0) / 2)
);
}
// 自爆
if (special.includes(19)) {
core.status.hero.statistics.battleDamage +=
core.status.hero.hp - 1;
core.status.hero.hp = 1;
}
// 退化
if (special.includes(21)) {
core.status.hero.atk -= enemy.atkValue || 0;
core.status.hero.def -= enemy.defValue || 0;
if (core.status.hero.atk < 0) core.status.hero.atk = 0;
if (core.status.hero.def < 0) core.status.hero.def = 0;
}
// 增加仇恨值
core.setFlag(
'hatred',
core.getFlag('hatred', 0) + core.values.hatred
);
// 事件的处理
const todo = [];
// 战后事件
if (has(core.status.floorId)) {
const loc = `${x},${y}`;
todo.push(
...(core.floors[core.status.floorId].afterBattle[loc] ?? [])
);
}
todo.push(...(enemy.enemy.afterBattle ?? []));
// 如果事件不为空,将其插入
if (todo.length > 0) core.insertAction(todo, x, y);
if (has(x) && has(y)) {
core.drawAnimate(animate, x, y);
if (special.includes(23)) core.hideBlock(x, y);
else core.removeBlock(x, y);
} else core.drawHeroAnimate(animate);
// 如果已有事件正在处理中
if (core.status.event.id == null) core.continueAutomaticRoute();
else core.clearContinueAutomaticRoute();
// 打怪特效
Mota.r(() => {
const setting = Mota.require('var', 'mainSetting');
const { applyFragWith } = Mota.Plugin.require('frag_r');
if (setting.getValue('fx.frag') && has(x) && has(y)) {
const frame = core.status.globalAnimateStatus % 2;
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
core.drawIcon(canvas, enemy.id, 0, 0, 32, 32, frame);
const manager = applyFragWith(canvas);
const frag = manager.canvas;
frag.style.imageRendering = 'pixelated';
frag.style.width = `${frag.width * core.domStyle.scale}px`;
frag.style.height = `${
frag.height * core.domStyle.scale
}px`;
const left =
(x * 32 + 16 - frag.width / 2 - core.bigmap.offsetX) *
core.domStyle.scale;
const top =
(y * 32 + 16 - frag.height / 2 - core.bigmap.offsetY) *
core.domStyle.scale;
frag.style.left = `${left}px`;
frag.style.top = `${top}px`;
frag.style.zIndex = '45';
frag.style.position = 'absolute';
frag.style.filter = 'sepia(20%)brightness(120%)';
core.dom.gameDraw.appendChild(frag);
manager.onEnd.then(() => {
frag.remove();
});
}
});
});
// --------------- 战斗伤害
const { getHeroStatusOn } = Mota.requireAll('fn');
const Damage = Mota.require('module', 'Damage');
// 这个数组常量控制着在战斗时哪些属性计算真实属性,也就是经过buff加成的属性
// 如果有属性不会经过buff加成等,请将其去除,可以提高性能表现
Damage.realStatus = ['atk', 'def', 'mdef', 'hpmax'];
// 怪物属性计算,用于获取怪物的初始属性,不经过任何光环加成
// 一般对于坚固、模仿等怪物会在这计算
Mota.rewrite(
Mota.require('class', 'DamageEnemy').prototype,
'calAttribute',
'full',
function () {
const { has } = Mota.Plugin.require('utils_g');
if (this.progress !== 1 && has(this.x) && has(this.floorId))
return;
this.progress = 2;
const special = this.info.special;
const info = this.info;
const floorId = this.floorId ?? core.status.floorId;
const status = getHeroStatusOn(Damage.realStatus, floorId);
// 坚固
if (special.includes(3)) {
info.def = status.atk - 1;
}
// 模仿
if (special.includes(10)) {
info.atk = status.atk;
info.def = status.def;
}
}
);
// 复写系统的伤害计算函数即可,全量复写
// 函数接受两个参数,分别是怪物信息和勇士信息,返回一个数字作为伤害
// 返回null表示不能战斗,返回Infinity也可以
Mota.rewrite(Damage, 'calDamageWith', 'full', (info, hero) => {
// 获取勇士属性,这几个属性直接从core.status.hero获取
const { hp, mana, manamax } = core.status.hero;
// 获取勇士属性,这几个属性从勇士真实属性获取
// 分开获取是因为获取勇士真实属性会对性能造成一定影响
let { atk, def, mdef, hpmax } = hero;
// 获取怪物信息,是在某点的信息
let { hp: monHp, atk: monAtk, def: monDef, special, enemy } = info;
/** 总伤害 */
let damage = 0;
/** 勇士单回合伤害 */
let heroPerDamage;
/** 战斗回合 */
let turn = 0;
// 无敌
if (special.includes(20) && !core.hasItem('cross')) {
return null;
}
heroPerDamage = atk - monDef;
if (heroPerDamage <= 0) return null;
// 吸血
if (special.includes(11)) {
const vampire = hp * (info.vampire ?? 0);
if (info.add) monHp += vampire;
damage += vampire;
}
/** 怪物单回合伤害 */
let enemyPerDamage;
// 魔攻
if (special.includes(2)) {
enemyPerDamage = monAtk;
} else {
enemyPerDamage = monAtk - def;
if (enemyPerDamage < 0) enemyPerDamage = 0;
}
// 先攻
if (special.includes(1)) {
damage += enemyPerDamage;
}
// 连击
if (special.includes(4)) enemyPerDamage *= 2;
if (special.includes(5)) enemyPerDamage *= 3;
if (special.includes(6)) enemyPerDamage *= info.n ?? 4;
// 破甲
if (special.includes(7)) {
damage += def * (info.breakArmor ?? core.values.breakArmor);
}
// 反击
if (special.includes(8)) {
enemyPerDamage +=
atk * (info.counterAttack ?? core.values.counterAttack);
}
// 净化
if (special.includes(9)) {
damage += mdef * (info.purify ?? core.values.purify);
}
turn = Math.ceil(monHp / heroPerDamage);
// 支援,支援信息由光环计算而得,直接使用即可
if (info.guard) {
const guardFirst = false;
const inGuard = core.getFlag('__inGuard__');
if (!inGuard)
core.setFlag('__extraTurn__', guardFirst ? 0 : turn);
core.setFlag('__inGuard__', true);
for (const enemy of info.guard) {
const info = enemy.getRealInfo();
damage +=
Damage.calDamageWith(info, {
...hero,
mdef: 0
}) ?? Infinity;
if (!isFinite(damage)) return null;
}
if (!inGuard) core.removeFlag('__inGuard__');
turn += core.getFlag('__extraTurn__', 0);
core.removeFlag('__extraTurn__');
}
// 计算最终伤害
damage += (turn - 1) * enemyPerDamage;
damage -= mdef;
if (!core.flags.enableNegativeDamage) damage = Math.max(0, damage);
// 仇恨
if (special.includes(17)) {
damage += core.getFlag('hatred', 0);
}
// 固伤
if (special.includes(22)) {
damage += info.damage;
}
return damage;
});
// --------------- 秒杀伤害计算
// 用于计算一些特殊属性怪物在一回合内秒杀所需的攻击,依此计算临界的上界
// 函数没有参数,返回一个数字,表示临界上界,Infinity表示没有上界,不计算临界
// 不能返回数字型外的量
Mota.rewrite(
Mota.require('class', 'DamageEnemy').prototype,
'getSeckillAtk',
'full',
function () {
// 获取怪物的属性
const info = this.getRealInfo();
// 对于一般的怪物,应该是怪物防御加上怪物血量
const add = info.def + info.hp;
// 坚固,不可能通过攻击秒杀
if (info.special.includes(3)) {
return Infinity;
}
// 模仿,不计算临界
if (info.special.includes(10)) {
return Infinity;
}
// 吸血
if (info.special.includes(11) && info.add) {
return add + core.status.hero.hp * (info.vampire ?? 0);
}
return add;
}
);
// --------------- 地图伤害
const caledBetween = new Set();
// 全量复写地图伤害计算,这个计算会调用所有的 DamageEnemy 的地图伤害计算
Mota.rewrite(
Mota.require('class', 'EnemyCollection').prototype,
'calMapDamage',
'full',
function () {
this.mapDamage = {};
caledBetween.clear();
const hero = getHeroStatusOn(Damage.realStatus, this.floorId);
this.list.forEach(v => {
v.calMapDamage(this.mapDamage, hero);
});
}
);
// 全量复写单个怪物地图伤害的计算函数,注意此处不能使用箭头函数,因为这是在原型上的函数,其this指向实例,也即怪物(DamageEnemy实例)
// 函数接收两个参数,damage和hero,前者表示要将结果存入的对象,后者是勇士真实属性
// 直接将damage返回即可,返回其他值有可能会引起出错
// 计算出伤害后直接调用this.setMapDamage即可将伤害传到对象中
Mota.rewrite(
Mota.require('class', 'DamageEnemy').prototype,
'calMapDamage',
'full',
function (damage = {}, hero = getHeroStatusOn(Damage.realStatus)) {
// 功能函数,计算曼哈顿距离,和判断一个值是否存在
const { manhattan, has } = Mota.Plugin.require('utils_g');
// 判断这个怪物是不是在地图上
if (
!has(this.x) ||
!has(this.y) ||
!has(this.floorId) ||
!has(this.col)
) {
return damage;
}
const enemy = this.info;
const floor = core.status.maps[this.floorId];
const w = floor.width;
const h = floor.height;
// 领域
if (this.info.special.includes(15)) {
const range = enemy.range ?? 1;
const startX = Math.max(0, this.x - range);
const startY = Math.max(0, this.y - range);
const endX = Math.min(floor.width - 1, this.x + range);
const endY = Math.min(floor.height - 1, this.y + range);
const dam = Math.max(enemy.zone ?? 0, 0);
for (let x = startX; x <= endX; x++) {
for (let y = startY; y <= endY; y++) {
if (
!enemy.zoneSquare &&
manhattan(x, y, this.x, this.y) > range
) {
continue;
}
const loc = `${x},${y}`;
this.setMapDamage(damage, loc, dam, '领域');
}
}
}
// 激光
if (this.info.special.includes(24)) {
const dirs = ['left', 'down', 'up', 'right'];
const dam = Math.max(enemy.laser ?? 0, 0);
for (const dir of dirs) {
let x = this.x;
let y = this.y;
const { x: dx, y: dy } = core.utils.scan[dir];
while (x >= 0 && y >= 0 && x < w && y < h) {
x += dx;
y += dy;
const loc = `${x},${y}`;
this.setMapDamage(damage, loc, dam, '激光');
}
}
}
// 夹击
if (this.info.special.includes(16)) {
const dirs = ['left', 'down', 'up', 'right'];
const dam = Math.floor(core.status.hero.hp / 2);
for (const dir of dirs) {
let x = this.x;
let y = this.y;
const { x: dx, y: dy } = core.utils.scan[dir];
if (caledBetween.has(`${x + dx},${y + dy}`)) continue;
const e = this.col.list.find(v => {
return v.x === x + dx * 2 && v.y === y + dy * 2;
});
if (e && e.info.special.includes(16)) {
const loc = `${x + dx},${y + dy}`;
this.setMapDamage(damage, loc, dam, '夹击');
caledBetween.add(loc);
}
}
}
// 阻击
if (this.info.special.includes(18)) {
const range = 1;
const startX = Math.max(0, this.x - range);
const startY = Math.max(0, this.y - range);
const endX = Math.min(floor.width - 1, this.x + range);
const endY = Math.min(floor.height - 1, this.y + range);
const dam = Math.max(enemy.repulse ?? 0, 0);
for (let x = startX; x <= endX; x++) {
for (let y = startY; y <= endY; y++) {
if (
!enemy.zoneSquare &&
manhattan(x, y, this.x, this.y) > range
) {
continue;
}
const loc = `${x},${y}`;
this.setMapDamage(damage, loc, dam, '阻击');
damage[loc].repulse = damage[loc].repulse ?? [];
damage[loc].repulse.push([this.x, this.y]);
}
}
}
// 捕捉
if (this.info.special.includes(27)) {
const dirs = ['left', 'down', 'up', 'right'];
for (const dir of dirs) {
let x = this.x;
let y = this.y;
const { x: dx, y: dy } = core.utils.scan[dir];
const loc = `${x + dx},${y + dy}`;
this.setMapDamage(damage, loc, 0);
damage[loc].ambush = damage[loc].ambush ?? [];
damage[loc].ambush.push(this);
}
}
return damage;
}
);
// --------------- 光环处理
// 光环分为两类,一类是会增强光环或者给怪物加光环的光环,另一类就是普通光环,这两种光环处理方式不同
// 对于前者,光环将会优先递归计算,同时每个光环将会确保只计算一次,直到没有光环需要计算
// 对于后者,不进行递归计算,只进行单次遍历计算。
// 光环使用 provideHalo 和 injectHalo 作为api,表示提供光环和接受光环
// 光环属性列表,是一个集合Set,你可以在这里配置会被视为光环的属性
const haloSpecials = Mota.require('module', 'Damage').haloSpecials;
haloSpecials.add(25).add(26).add(28);
// ----- 计算第二类光环,即普通光环,这类光环更常见,因此放到前面了
Mota.rewrite(
Mota.require('class', 'DamageEnemy').prototype,
'provideHalo',
'full',
function () {
// 这部分用于判断当前是否应该计算光环,即计算光环的函数是否在不应该被调用的时刻调用了
// 一般不需要改动
if (this.progress !== 2) return;
this.progress = 3;
if (!this.floorId) return;
const { has } = Mota.Plugin.require('utils_g');
if (!has(this.x) || !has(this.y)) return;
const col = this.col ?? core.status.maps[this.floorId].enemy;
if (!col) return;
// 获取所有还没有计算的光环,注意这里不能直接获取haloSpecial
const special = this.getHaloSpecials();
// e 是被加成怪的属性,enemy 是施加光环的怪
for (const halo of special) {
switch (halo) {
// 普通光环
case 25: {
const e = this.enemy;
const type = e.haloSquare ? 'square' : 'manhattan';
const r = Math.floor(e.haloRange);
const d = type === 'square' ? r * 2 + 1 : r;
const range = { x: this.x, y: this.y, d };
// 施加光环
col.applyHalo(type, range, this, (e, enemy) => {
e.atkBuff_ += enemy.enemy.atkBuff ?? 0;
e.defBuff_ += enemy.enemy.defBuff ?? 0;
e.hpBuff_ += enemy.enemy.hpBuff ?? 0;
});
// 向已施加的光环列表中添加
this.providedHalo.add(25);
break;
}
case 26: {
const range = { x: this.x, y: this.y, d: 1 };
// 支援
col.applyHalo('square', range, this, (e, enemy) => {
e.guard = e.guard ?? [];
e.guard.push(this);
});
this.providedHalo.add(26);
break;
}
}
}
}
);
// ----- 计算第一类光环
// 特殊属性对应的特殊属性数值
const changeable = Mota.require('module', 'Damage').changeableHaloValue;
changeable
.set(21, ['atkValue', 'defValue'])
.set(7, ['breakArmor'])
.set(8, ['counterAttack'])
.set(22, ['damage'])
.set(25, ['haloRange'])
.set(24, ['laser'])
.set(6, ['n'])
.set(9, ['purify'])
.set(15, ['range'])
.set(18, ['repulse'])
.set(11, ['vampire'])
.set(15, ['zone']);
Mota.rewrite(
Mota.require('class', 'DamageEnemy').prototype,
'preProvideHalo',
'full',
function () {
if (this.progress !== 0) return;
this.progress = 1;
const special = this.getHaloSpecials();
const col = this.col ?? core.status.maps[this.floorId].enemy;
for (const halo of special) {
switch (halo) {
case 28: {
// 特殊光环
const e = this.enemy;
const type = e.haloSquare ? 'square' : 'manhattan';
const r = Math.floor(e.haloRange);
const d = type === 'square' ? r * 2 + 1 : r;
const range = { x: this.x, y: this.y, d };
// 这一句必须放到applyHalo之前
this.providedHalo.add(28);
col.applyHalo(
type,
range,
this,
(e, enemy) => {
const s = enemy.specialHalo;
for (const spe of s) {
// 防止重复
if (!e.special.includes(spe))
e.special.push(spe);
}
// 如果是自身,就不进行特殊属性数值处理了
if (e === this.info) return;
// 然后计算特殊属性数值
for (const spec of s) {
const toChange = changeable.get(spec);
if (!toChange) continue;
for (const key of toChange) {
// 这种光环应该获取怪物的原始数值,而不是真实数值
if (enemy.enemy.specialMultiply) {
e[key] = s[key] ?? 1;
e[key] *= enemy[key];
} else {
e[key] = s[key] ?? 0;
e[key] += enemy[key];
}
}
}
},
// true表示递归计算,视为第一类光环
true
);
}
}
}
}
);
// ----- 接受光环处理
Mota.rewrite(
Mota.require('class', 'DamageEnemy').prototype,
'injectHalo',
'full',
function (halo, enemy) {
// 这里的 halo 是光环函数,enemy 是施加光环的怪物,this.info 是当前怪物信息
halo(this.info, enemy);
}
);
},
checkBlock: function () {
Mota.rewrite(core.control, 'checkBlock', 'full', function () {
const x = core.getHeroLoc('x'),
y = core.getHeroLoc('y'),
loc = x + ',' + y;
const info = core.status.thisMap.enemy.mapDamage[loc];
const damage = info?.damage;
const floor = core.status.thisMap;
if (damage) {
// 伤害弹出,在渲染进程中执行
Mota.r(() => {
Mota.Plugin.require('pop_r').addPop(
(x - core.bigmap.offsetX / 32) * 32 + 12,
(y - core.bigmap.offsetY / 32) * 32 + 20,
(-damage).toString()
);
});
core.status.hero.hp -= damage;
const type = [...info.type];
const text = type.join(',') || '伤害';
core.drawTip('受到' + text + damage + '点');
core.drawHeroAnimate('zone');
this._checkBlock_disableQuickShop();
core.status.hero.statistics.extraDamage += damage;
if (core.status.hero.hp <= 0) {
core.status.hero.hp = 0;
core.updateStatusBar();
core.events.lose();
return;
} else {
core.updateStatusBar();
}
}
const { findDir, ofDir } = Mota.Plugin.require('utils_g');
// 阻击处理
if (info?.repulse) {
const actions = [];
for (const [ex, ey] of info.repulse) {
const dir = findDir({ x, y }, { x: ex, y: ey });
const [tx, ty] = ofDir(ex, ey, dir);
if (
tx < 0 ||
ty < 0 ||
tx >= floor.width ||
ty >= floor.height ||
core.getBlock(tx, ty)
) {
continue;
}
actions.push({
type: 'move',
loc: [ex, ey],
steps: [findDir({ x, y }, { x: ex, y: ey })],
time: 250,
keep: true,
async: true
});
}
actions.push({ type: 'waitAsync' });
core.insertAction(actions);
}
// 捕捉处理
if (info?.ambush) {
const actions = [];
for (const enemy of info.ambush) {
actions.push({
type: 'move',
loc: [enemy.x, enemy.y],
steps: [findDir(enemy, { x, y })],
time: 250,
keep: false,
async: true
});
}
actions.push({ type: 'waitAsync' });
// 强制战斗
for (const enemy of info.ambush) {
actions.push({
type: 'function',
function: () => {
core.battle(enemy, void 0, true, core.doAction);
},
async: true
});
}
core.insertAction(actions);
}
});
},
misc: function () {
// 把一些杂项放在这了
const { loading } = Mota.requireAll('var');
Mota.r(() => {
// 楼层滤镜配置
loading.once('coreInit', () => {
const { filterMap } = Mota.Plugin.require('gameCanvas_r');
// 楼层滤镜是一系列数组,数组第一项是一个数组,表示所有使用这个滤镜的楼层,第二项是滤镜内容
filterMap.push(
[
['sample0', 'sample1', 'sample2'], // 楼层列表
'brightness(80%)' // 滤镜内容
],
[['MT0'], 'contrast(120%)']
);
});
// 点光源配置,参考插件库点光源插件的配置方式
loading.once('coreInit', () => {
const { shadowInfo, backgroundInfo, blurInfo, immersionInfo } =
Mota.Plugin.require('gameShadow_r');
const { pColor } = Mota.require('module', 'RenderUtils');
// 光源信息
shadowInfo.MT0 = [
{
id: 'mt0_1',
x: 144,
y: 144,
decay: 20,
r: 150,
color: pColor('#e953'),
noShelter: true
}
];
// 背景色
backgroundInfo.MT0 = pColor('#0006');
// 虚化程度
blurInfo.MT0 = 3;
// 浸入墙壁程度
immersionInfo.MT0 = 4;
});
});
}
};