HumanBreak/public/project/plugins.js
2024-03-02 19:20:15 +08:00

1329 lines
53 KiB
JavaScript
Raw 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.

///<reference path="../../src/types/core.d.ts"/>
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 +=
'&nbsp;'.repeat(8) +
`${i + 1}. <span style="color: ${
specials[v].color
}">${fromFunc(
specials[v].name,
enemy
)}</span>: ${fromFunc(
specials[v].desc,
enemy
)}<br />`;
});
return (
`怪物周围${enemy.haloSquare ? '九宫格' : '十字'}${
enemy.haloRange
}格范围内所有怪物获得以下特殊属性,` +
`特殊属性数值间为${
enemy.specialMultiply ? '相乘' : '相加'
}关系:<br />` +
content
);
},
color: '#ff0'
}
);
},
battle: function () {
// 这个插件负责战斗相关内容
// --------------- 战后脚本
// 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;
}
if (special.includes(3)) {
// 由于坚固的特性,只能放到这来计算了
if (atk > enemy.def) heroPerDamage = 1;
else return null;
} else {
// 模仿
if (special.includes(10)) {
monAtk = atk;
monDef = def;
}
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];
this.col.list.forEach(v => {
if (v.x === x + dx * 2 && v.y === y + dy * 2) {
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.atkBuff ?? 0;
e.defBuff += enemy.defBuff ?? 0;
e.hpBuff += 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:
'function() { ' +
`core.battle(${enemy.x}, ${enemy.y}, true, core.doAction); ` +
'}',
async: true
});
}
core.insertAction(actions);
}
});
}
};