feat: 战斗

This commit is contained in:
unanmed 2024-02-05 19:06:00 +08:00
parent c8e45f2a22
commit 6ce86501f1
15 changed files with 656 additions and 500 deletions

View File

@ -2933,6 +2933,11 @@ control.prototype.getBuff = function (name) {
return core.getFlag('__' + name + '_buff__', 1);
};
////// 获得或移除毒衰咒效果 //////
control.prototype.triggerDebuff = function (action, type) {
return this.controldata.triggerDebuff(action, type);
};
////// 设置勇士的位置 //////
control.prototype.setHeroLoc = function (name, value, noGather) {
if (!core.status.hero) return;

View File

@ -16,7 +16,7 @@ var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
"bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"special":[9]},
"redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"brownWizard": {"name":"初级巫师","hp":100,"atk":120,"def":0,"money":16,"exp":0,"point":0,"special":[15],"value":100,"range":2},
"redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"special":[15,28],"value":200,"zoneSquare":true,"specialHalo":[6,16],"n":9,"haloRange":2,"haloSquare":true},
"redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"special":[15,28],"value":200,"zoneSquare":true,"specialHalo":[6,16],"n":9,"haloRange":2,"haloSquare":true,"zone":200},
"swordsman": {"name":"双手剑士","hp":100,"atk":120,"def":0,"money":6,"exp":0,"point":0,"special":[4]},
"soldier": {"name":"冥战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"yellowKnight": {"name":"金骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},

View File

@ -365,6 +365,63 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
if (callback) callback();
});
},
triggerDebuff: function (action, type) {
// 毒衰咒效果的获得与解除
// action获得还是解除'get'表示获得,'remove'表示解除
// type一个数组表示获得了哪些毒衰咒效果poison, weakcurse
if (!(type instanceof Array)) type = [type];
if (action == 'get') {
if (core.inArray(type, 'poison') && !core.hasFlag('poison')) {
// 获得毒效果
core.setFlag('poison', true);
}
if (core.inArray(type, 'weak') && !core.hasFlag('weak')) {
// 获得衰效果
core.setFlag('weak', true);
if (core.values.weakValue >= 1) {
// >=1直接扣数值
core.addStatus('atk', -core.values.weakValue);
core.addStatus('def', -core.values.weakValue);
} else {
// <1扣比例
core.addBuff('atk', -core.values.weakValue);
core.addBuff('def', -core.values.weakValue);
}
}
if (core.inArray(type, 'curse') && !core.hasFlag('curse')) {
// 获得咒效果
core.setFlag('curse', true);
}
} else if (action == 'remove') {
var success = false;
if (core.inArray(type, 'poison') && core.hasFlag('poison')) {
success = true;
// 移除毒效果
core.setFlag('poison', false);
}
if (core.inArray(type, 'weak') && core.hasFlag('weak')) {
success = true;
// 移除衰效果
core.setFlag('weak', false);
if (core.values.weakValue >= 1) {
// >=1直接扣数值
core.addStatus('atk', core.values.weakValue);
core.addStatus('def', core.values.weakValue);
} else {
// <1扣比例
core.addBuff('atk', core.values.weakValue);
core.addBuff('def', core.values.weakValue);
}
}
if (core.inArray(type, 'curse') && core.hasFlag('curse')) {
success = true;
// 移除咒效果
core.setFlag('curse', false);
}
if (success) core.playSound('回血');
}
},
updateStatusBar: function () {
// 更新状态栏

View File

@ -361,7 +361,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
name: '破甲',
desc: enemy =>
`战斗前,怪物附加角色防御的${Math.floor(
100 * (enemy.breakArmor || core.values.breakArmor)
100 * (enemy.breakArmor ?? core.values.breakArmor)
)}%作为伤害"`,
color: '#b67'
},
@ -370,7 +370,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
name: '反击',
desc: enemy =>
`战斗时,怪物每回合附加角色攻击的${Math.floor(
100 * (enemy.counterAttack || core.values.counterAttack)
100 * (enemy.counterAttack ?? core.values.counterAttack)
)}%作为伤害无视角色防御`,
color: '#fa4'
},
@ -379,7 +379,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
name: '净化',
desc: enemy =>
`战斗前,怪物附加角色护盾的${
enemy.purify || core.values.purify
enemy.purify ?? core.values.purify
}倍作为伤害`,
color: '#80eed6'
},
@ -578,6 +578,166 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
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 Damage = Mota.require('module', 'Damage');
@ -590,7 +750,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
// 返回null表示不能战斗返回Infinity也可以
Mota.rewrite(Damage, 'calDamageWith', 'full', (info, hero) => {
// 获取勇士属性这几个属性直接从core.status.hero获取
const { hp, mana } = core.status.hero;
const { hp, mana, manamax } = core.status.hero;
// 获取勇士属性,这几个属性从勇士真实属性获取
// 分开获取是因为获取勇士真实属性会对性能造成一定影响
let { atk, def, mdef, hpmax } = hero;
@ -601,21 +761,40 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
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) || special.includes(13)) {
if (special.includes(2)) {
enemyPerDamage = monAtk;
} else {
enemyPerDamage = monAtk - def;
@ -623,22 +802,69 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
}
// 先攻
if (special.includes(17)) {
if (special.includes(1)) {
damage += enemyPerDamage;
}
// 连击
if (special.includes(4)) enemyPerDamage *= 2;
if (special.includes(5)) enemyPerDamage *= 3;
if (special.includes(6)) enemyPerDamage *= enemy.n;
if (special.includes(6)) enemyPerDamage *= info.n;
/** 战斗回合 */
let turn = Math.ceil(monHp / heroPerDamage);
// 破甲
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;
});
@ -660,13 +886,33 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
if (info.special.includes(3)) {
return Infinity;
}
// 模仿,不计算临界
if (info.special.includes(10)) {
return Infinity;
}
return add;
}
);
// --------------- 地图伤害
// 全量复写地图伤害的计算函数注意此处不能使用箭头函数因为这是在原型上的函数其this指向实例也即怪物(DamageEnemy实例)
const { getHeroStatusOn } = Mota.requireAll('fn');
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即可将伤害传到对象中
@ -678,21 +924,27 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
// 功能函数,计算曼哈顿距离,和判断一个值是否存在
const { manhattan, has } = Mota.Plugin.require('utils_g');
// 判断这个怪物是不是在地图上
if (!has(this.x) || !has(this.y) || !has(this.floorId))
if (
!has(this.x) ||
!has(this.y) ||
!has(this.floorId) ||
!has(this.col)
) {
return damage;
const enemy = this.enemy;
}
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.value ?? 0) - hero.def, 0);
const dam = Math.max(enemy.zone ?? 0, 0);
for (let x = startX; x <= endX; x++) {
for (let y = startY; y <= endY; y++) {
@ -703,16 +955,15 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
continue;
}
const loc = `${x},${y}`;
this.setMapDamage(damage, loc, dam, '突刺');
this.setMapDamage(damage, loc, dam, '领域');
}
}
}
// 射击
// 激光
if (this.info.special.includes(24)) {
const dirs = ['left', 'down', 'up', 'right'];
const dam = Math.max((enemy.atk ?? 0) - hero.def, 0);
const objs = core.getMapBlocksObj(this.floorId);
const dam = Math.max(enemy.laser ?? 0, 0);
for (const dir of dirs) {
let x = this.x;
@ -722,22 +973,75 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
x += dx;
y += dy;
const loc = `${x},${y}`;
const block = objs[loc];
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) {
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 (
block &&
block.event.noPass &&
block.event.cls !== 'enemys' &&
block.event.cls !== 'enemy48' &&
block.id !== 141 &&
block.id !== 151
!enemy.zoneSquare &&
manhattan(x, y, this.x, this.y) > range
) {
break;
continue;
}
this.setMapDamage(damage, loc, dam, '射击');
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;
}
);
@ -750,7 +1054,7 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
// 光环属性列表是一个集合Set你可以在这里配置会被视为光环的属性
const haloSpecials = Mota.require('module', 'Damage').haloSpecials;
haloSpecials.add(8).add(11);
haloSpecials.add(25).add(26).add(28);
// ----- 计算第二类光环,即普通光环,这类光环更常见,因此放到前面了
Mota.rewrite(
@ -770,78 +1074,58 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
// 获取所有还没有计算的光环注意这里不能直接获取haloSpecial
const special = this.getHaloSpecials();
const square7 = [];
const square5 = [];
// e 是被加成怪的属性enemy 是施加光环的怪
for (const halo of special) {
switch (halo) {
case 8:
square5.push((e, enemy) => {
if (
e.special.includes(8) &&
(e.x !== this.x || this.y !== e.y)
) {
e.atkBuff += enemy.together ?? 0;
e.defBuff += enemy.together ?? 0;
// 普通光环
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;
}
});
this.providedHalo.add(8);
break;
case 21:
square7.push(e => {
// e.damageDecline += this.enemy.iceHalo ?? 0;
});
col.haloList.push({
type: 'square',
data: { x: this.x, y: this.y, d: 7 },
special: 21,
from: this
});
this.providedHalo.add(21);
break;
case 26:
square5.push(e => {
e.defBuff += this.enemy.iceCore ?? 0;
});
col.haloList.push({
type: 'square',
data: { x: this.x, y: this.y, d: 5 },
special: 26,
from: this
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;
case 27:
square5.push(e => {
e.atkBuff += this.enemy.fireCore ?? 0;
});
col.haloList.push({
type: 'square',
data: { x: this.x, y: this.y, d: 5 },
special: 27,
from: this
});
this.providedHalo.add(27);
break;
}
}
col.applyHalo(
'square',
{ x: this.x, y: this.y, d: 7 },
square7
);
col.applyHalo(
'square',
{ x: this.x, y: this.y, d: 5 },
square5
);
}
}
);
// ----- 计算第一类光环
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',
@ -850,11 +1134,47 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
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) {
default:
break;
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;
e.special.push(...s);
// 然后计算特殊属性数值
for (const spec of s) {
const toChange = changeable.get(spec);
if (!toChange) continue;
for (const key of toChange) {
if (enemy.specialMultiply) {
e[key] = s[key] ?? 1;
e[key] *= enemy[key];
} else {
e[key] = s[key] ?? 0;
e[key] += enemy[key];
}
}
}
},
// true表示递归计算视为第一类光环
true
);
}
}
}
}
@ -870,5 +1190,96 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
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);
}
});
}
};

View File

@ -19,11 +19,14 @@
class="special-text"
v-if="has(enemy.special) && enemy.special.length > 0"
>
<template v-for="(text, i) in enemy.showSpecial">
<span
v-for="(text, i) in enemy.showSpecial"
v-if="i < (isMobile ? 1 : 2)"
:style="{ color: text[2] }"
>&nbsp;{{ text[0] }}&nbsp;</span
>
<span v-if="i === (isMobile ? 1 : 2)">...</span>
</template>
</div>
<div class="special-text" v-else>无属性</div>
</div>

View File

@ -58,116 +58,6 @@ function init() {
callback?.();
};
core.events.afterBattle = function afterBattle(
enemy: DamageEnemy,
x?: number,
y?: number
) {
const floorId = core.status.floorId;
const special = enemy.info.special;
// 播放战斗动画
let animate: AnimationIds = '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++;
// 智慧之源
if (special.includes(14) && flags.hard === 2) {
core.addFlag(
'inte_' + floorId,
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10
);
core.status.hero.mdef -=
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10;
}
// 极昼永夜
if (special.includes(22)) {
flags[`night_${floorId}`] ??= 0;
flags[`night_${floorId}`] -= enemy.enemy.night!;
}
if (special.includes(23)) {
flags[`night_${floorId}`] ??= 0;
flags[`night_${floorId}`] += enemy.enemy.day;
}
// if (core.plugin.skillTree.getSkillLevel(11) > 0) {
// core.plugin.study.declineStudiedSkill();
// }
// 如果是融化怪,需要特殊标记一下
if (special.includes(25) && has(x) && has(y)) {
flags[`melt_${floorId}`] ??= {};
flags[`melt_${floorId}`][`${x},${y}`] = enemy.enemy.melt;
}
// 获得金币
const money = enemy.enemy.money;
core.status.hero.money += money;
core.status.hero.statistics.money += money;
// 获得经验
const exp = enemy.enemy.exp;
core.status.hero.exp += exp;
core.status.hero.statistics.exp += exp;
const hint =
'打败 ' + enemy.enemy.name + ',金币+' + money + ',经验+' + exp;
core.drawTip(hint, enemy.id);
if (core.getFlag('bladeOn') && core.getFlag('blade')) {
core.setFlag('blade', false);
}
if (core.getFlag('shieldOn') && core.getFlag('shield')) {
core.setFlag('shield', false);
}
// 事件的处理
const todo: MotaEvent = [];
// 战后事件
if (has(core.status.floorId)) {
const loc = `${x},${y}` as LocString;
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);
core.removeBlock(x, y);
} else core.drawHeroAnimate(animate);
// 如果已有事件正在处理中
if (core.status.event.id == null) core.continueAutomaticRoute();
else core.clearContinueAutomaticRoute();
};
core.enemys.getCurrentEnemys = function getCurrentEnemys(
floorId = core.status.floorId
) {

View File

@ -14,9 +14,14 @@ interface HaloType {
y: number;
d: number;
};
manhattan: {
x: number;
y: number;
d: number;
};
}
interface EnemyInfo {
interface EnemyInfo extends Partial<SelectType<Enemy, number | undefined>> {
atk: number;
def: number;
hp: number;
@ -25,6 +30,7 @@ interface EnemyInfo {
defBuff: number;
hpBuff: number;
enemy: Enemy;
guard?: DamageEnemy[];
x?: number;
y?: number;
floorId?: FloorIds;
@ -39,7 +45,8 @@ interface DamageInfo {
interface MapDamage {
damage: number;
type: Set<string>;
mockery?: LocArr[];
repulse?: LocArr[];
ambush?: DamageEnemy[];
}
interface HaloData<T extends keyof HaloType = keyof HaloType> {
@ -103,6 +110,8 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
this.haloList = [];
this.list.forEach(v => {
v.reset();
});
this.list.forEach(v => {
v.preProvideHalo();
});
this.list.forEach(v => {
@ -146,21 +155,22 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
applyHalo<K extends keyof HaloType>(
type: K,
data: HaloType[K],
enemy: DamageEnemy,
halo: HaloFn | HaloFn[],
recursion: boolean = false
) {
const arr = ensureArray(halo);
const enemy = this.range.scan(type, data);
const enemys = this.range.scan(type, data);
if (!recursion) {
arr.forEach(v => {
enemy.forEach(e => {
e.injectHalo(v, e.enemy);
enemys.forEach(e => {
e.injectHalo(v, enemy.enemy);
});
});
} else {
enemy.forEach(e => {
enemys.forEach(e => {
arr.forEach(v => {
e.injectHalo(v, e.enemy);
e.injectHalo(v, enemy.enemy);
e.preProvideHalo();
});
});
@ -260,6 +270,26 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
alpha: 1
});
}
if (dam.ambush) {
core.status.damage.extraData.push({
text: '!',
px: 32 * x + 16,
py: 32 * y + 16,
color: '#fa3',
alpha: 1
});
}
if (dam.repulse) {
core.status.damage.extraData.push({
text: '阻',
px: 32 * x + 16,
py: 32 * y + 16,
color: '#fa3',
alpha: 1
});
}
}
}
}
@ -320,8 +350,15 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
y: this.y,
floorId: this.floorId
};
for (const [key, value] of Object.entries(enemy)) {
if (!(key in this.info) && has(value)) {
// @ts-ignore
this.info[key] = value;
}
}
this.progress = 0;
this.providedHalo = new Set();
this.providedHalo.clear();
}
/**
@ -381,6 +418,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
if (!this.floorId) return [];
if (!has(this.x) || !has(this.y)) return [];
const special = this.info.special ?? this.enemy.special;
const filter = special.filter(v => {
return Damage.haloSpecials.has(v) && !this.providedHalo.has(v);
});
@ -397,92 +435,12 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
/**
*
*/
preProvideHalo() {
if (this.progress !== 0) return;
this.progress = 1;
const special = this.getHaloSpecials();
for (const halo of special) {
switch (halo) {
default:
break;
}
}
}
preProvideHalo() {}
/**
*
*/
provideHalo() {
if (this.progress !== 2) return;
this.progress = 3;
if (!this.floorId) return;
if (!has(this.x) || !has(this.y)) return;
const col = this.col ?? core.status.maps[this.floorId].enemy;
if (!col) return;
const special = this.getHaloSpecials();
const square7: HaloFn[] = [];
const square5: HaloFn[] = [];
// e 是被加成怪的属性enemy 是施加光环的怪
for (const halo of special) {
switch (halo) {
case 8:
square5.push((e, enemy) => {
if (
e.special.includes(8) &&
(e.x !== this.x || this.y !== e.y)
) {
e.atkBuff += enemy.together ?? 0;
e.defBuff += enemy.together ?? 0;
}
});
this.providedHalo.add(8);
break;
case 21:
square7.push(e => {
// e.damageDecline += this.enemy.iceHalo ?? 0;
});
col.haloList.push({
type: 'square',
data: { x: this.x, y: this.y, d: 7 },
special: 21,
from: this
});
this.providedHalo.add(21);
break;
case 26:
square5.push(e => {
e.defBuff += this.enemy.iceCore ?? 0;
});
col.haloList.push({
type: 'square',
data: { x: this.x, y: this.y, d: 5 },
special: 26,
from: this
});
this.providedHalo.add(26);
break;
case 27:
square5.push(e => {
e.atkBuff += this.enemy.fireCore ?? 0;
});
col.haloList.push({
type: 'square',
data: { x: this.x, y: this.y, d: 5 },
special: 27,
from: this
});
this.providedHalo.add(27);
break;
}
}
col.applyHalo('square', { x: this.x, y: this.y, d: 7 }, square7);
col.applyHalo('square', { x: this.x, y: this.y, d: 5 }, square5);
}
provideHalo() {}
/**
*
@ -507,64 +465,6 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
damage: Record<string, MapDamage> = {},
hero: Partial<HeroStatus> = getHeroStatusOn(Damage.realStatus)
) {
if (!has(this.x) || !has(this.y) || !has(this.floorId)) return damage;
const enemy = this.enemy;
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.value ?? 0) - hero.def!, 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: Dir[] = ['left', 'down', 'up', 'right'];
// const dam = Math.max((enemy.atk ?? 0) - hero.def!, 0);
// const objs = core.getMapBlocksObj(this.floorId);
// 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}` as LocString;
// const block = objs[loc];
// if (
// block &&
// block.event.noPass &&
// block.event.cls !== 'enemys' &&
// block.event.cls !== 'enemy48' &&
// block.id !== 141 &&
// block.id !== 151
// ) {
// break;
// }
// this.setMapDamage(damage, loc, dam, '射击');
// }
// }
}
return damage;
}
@ -572,11 +472,11 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
damage: Record<string, MapDamage>,
loc: string,
dam: number,
type: string
type?: string
) {
damage[loc] ??= { damage: 0, type: new Set() };
damage[loc].damage += dam;
damage[loc].type.add(type);
if (type) damage[loc].type.add(type);
}
private calEnemyDamageOf(hero: Partial<HeroStatus>, enemy: EnemyInfo) {
@ -730,6 +630,12 @@ export namespace Damage {
/** 光环属性 */
export const haloSpecials: Set<number> = new Set();
/** 会被第一类光环修改的怪物特殊属性数值 */
export const changeableHaloValue: Map<
number,
SelectKey<Enemy, number | undefined>[]
> = new Map();
/**
* buff加成core.status.hero取
*/
@ -744,55 +650,7 @@ export namespace Damage {
info: EnemyInfo,
hero: Partial<HeroStatus>
): number | null {
const { hp, hpmax, mana, mdef } = core.status.hero;
let { atk, def } = hero as HeroStatus;
let { hp: monHp, atk: monAtk, def: monDef, special, enemy } = info;
let damage = 0;
// 饥渴
if (special.includes(7)) {
const delta = Math.floor((atk * enemy.hungry!) / 100);
atk -= delta;
monAtk += delta;
}
let heroPerDamage: number;
// 绝对防御
if (special.includes(3)) {
// 由于坚固的特性,只能放到这来计算了
if (atk > enemy.def) heroPerDamage = 1 + mana;
else return null;
} else {
heroPerDamage = atk - monDef;
if (heroPerDamage > 0) heroPerDamage += mana;
else return null;
}
let enemyPerDamage: number;
// 魔攻
if (special.includes(2) || special.includes(13)) {
enemyPerDamage = monAtk;
} else {
enemyPerDamage = monAtk - def;
if (enemyPerDamage < 0) enemyPerDamage = 0;
}
// 先攻
if (special.includes(17)) {
damage += enemyPerDamage;
}
// 连击
if (special.includes(4)) enemyPerDamage *= 2;
if (special.includes(5)) enemyPerDamage *= 3;
if (special.includes(6)) enemyPerDamage *= enemy.n!;
let turn = Math.ceil(monHp / heroPerDamage);
return damage;
return null;
}
export function ensureFloorDamage(floorId: FloorIds) {

View File

@ -164,7 +164,6 @@ interface PluginInterface {
heroFourFrames_g: typeof import('../plugin/game/fx/heroFourFrames');
rewrite_g: typeof import('../plugin/game/fx/rewrite');
itemDetail_g: typeof import('../plugin/game/fx/itemDetail');
checkBlock_g: typeof import('../plugin/game/enemy/checkblock');
}
interface PackageInterface {

View File

@ -24,40 +24,7 @@ const MAX_ROTATE = 0.5;
/** 碎裂动画的速率曲线函数 */
const FRAG_TIMING = linear();
export function init() {
Mota.rewrite(core.events, 'afterBattle', 'add', (_, enemy, x, y) => {
// 打怪特效
const setting = Mota.require('var', 'mainSetting');
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();
});
}
});
}
export function init() {}
export function applyFragWith(
canvas: HTMLCanvasElement,

View File

@ -1,35 +0,0 @@
export function init() {
// 伤害弹出
// 复写阻激夹域检测
control.prototype.checkBlock = function (forceMockery: boolean = false) {
const x = core.getHeroLoc('x'),
y = core.getHeroLoc('y'),
loc = x + ',' + y;
const info = core.status.thisMap.enemy.mapDamage[loc];
const damage = info?.damage;
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();
}
}
};
}

View File

@ -6,7 +6,6 @@ import * as removeMap from './removeMap';
import * as shop from './shop';
import * as utils from './utils';
import * as remainEnemy from './enemy/remainEnemy';
import * as checkBlock from './enemy/checkblock';
Mota.Plugin.register('utils_g', utils);
Mota.Plugin.register('shop_g', shop);
@ -16,7 +15,6 @@ Mota.Plugin.register('heroFourFrames_g', heroFourFrames, heroFourFrames.init);
Mota.Plugin.register('rewrite_g', rewrite, rewrite.init);
Mota.Plugin.register('itemDetail_g', itemDetail, itemDetail.init);
Mota.Plugin.register('remainEnemy_g', remainEnemy);
Mota.Plugin.register('checkBlock_g', checkBlock, checkBlock.init);
// export {
// halo,

View File

@ -96,3 +96,21 @@ Range.registerRangeType(
);
}
);
Range.registerRangeType(
'manhattan',
(col, { x, y, d }) => {
const list = col.collection.list;
return list.filter(v => {
if (!has(v.x) || !has(v.y)) return;
const dx = Math.abs(v.x - x);
const dy = Math.abs(v.y - y);
return dx + dy < d;
});
},
(col, { x, y, d }, item) => {
if (!has(item.x) || !has(item.y)) return false;
const dx = Math.abs(item.x - x);
const dy = Math.abs(item.y - y);
return dx + dy < d;
}
);

View File

@ -160,9 +160,10 @@ export function boundary(arr: any, key?: any) {
export function findDir(from: Loc, to: Loc): Dir2 | 'none' {
const dx = to.x - from.x;
const dy = to.y - from.y;
return (
(Object.entries(core.utils.scan2).find(v => {
v[1].x === dx && v[1].y === dy;
return v[1].x === dx && v[1].y === dy;
})?.[0] as Dir2) ?? 'none'
);
}

View File

@ -28,19 +28,17 @@ export function getDetailedEnemy(
) => {
return typeof func === 'string' ? func : func(enemy);
};
const special: [string, string, string][] = enemy.enemy.special.map(vv => {
const special: [string, string, string][] = enemy.info.special.map(vv => {
const s = Mota.require('var', 'enemySpecials')[vv];
const info = { ...enemy.enemy, ...enemy.info };
return [
fromFunc(s.name, enemy.enemy),
fromFunc(s.desc, enemy.enemy),
fromFunc(s.name, info),
fromFunc(s.desc, info),
s.color as string
];
});
const l = isMobile ? 1 : 2;
const showSpecial =
special.length > l
? special.slice(0, l).concat([['...', '', '#fff']])
: special.slice();
const showSpecial = special;
const damageColor = getDamageColor(dam) as string;

16
src/types/enemy.d.ts vendored
View File

@ -15,21 +15,7 @@ type PartialNumbericEnemyProperty =
| 'purify'
| 'atkValue'
| 'defValue'
| 'damage'
| 'iceDecline'
| 'iceCore'
| 'fireCore'
| 'together'
| 'hungry'
| 'ice'
| 'crit'
| 'courage'
| 'charge'
| 'paleShield'
| 'iceHalo'
| 'day'
| 'night'
| 'melt';
| 'damage';
type BooleanEnemyProperty =
| 'zoneSquare'