mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-02-28 17:37:07 +08:00
重写战斗
This commit is contained in:
parent
e8e6b202af
commit
351e136775
37
idea.md
37
idea.md
@ -24,27 +24,12 @@
|
|||||||
|
|
||||||
#### 技能
|
#### 技能
|
||||||
|
|
||||||
|
分成多条线,可以单独点一条线,也可以混搭
|
||||||
|
|
||||||
闪避:每 M 回合闪避一次,减少 N% 的伤害
|
闪避:每 M 回合闪避一次,减少 N% 的伤害
|
||||||
|
|
||||||
## 机制
|
## 机制
|
||||||
|
|
||||||
### 通用
|
|
||||||
|
|
||||||
- 实时天气
|
|
||||||
- 成就系统(完成)
|
|
||||||
- 装备合成、装备(孔)强化
|
|
||||||
- 宝石目标设定
|
|
||||||
- 自动宝物规划,选中两个或更多宝物后自动在本地图中规划出最优拾取路线,原则是尽量减少其余宝物的捡拾,自动切换主动技能,怪物造成的伤害最低的路线
|
|
||||||
- 临界显示方式,宝石数还是数值
|
|
||||||
- 怪物目标设定(完成)
|
|
||||||
- 木牌查看系统(完成)
|
|
||||||
- 宝物目标设定
|
|
||||||
- 每个怪物加一个怪物说明
|
|
||||||
- 歌词展示系统
|
|
||||||
- 小地图显示框,可以选择是否显示剩余怪物数量等
|
|
||||||
- 怪物死亡特效
|
|
||||||
- 区域名称显示特效,3D 粒子
|
|
||||||
|
|
||||||
### 第二章 智慧
|
### 第二章 智慧
|
||||||
|
|
||||||
- 苍蓝之殿 1: 利用点光源,照到的位置与没照到的位置内容不同,玩家可以选择是否装备手电筒
|
- 苍蓝之殿 1: 利用点光源,照到的位置与没照到的位置内容不同,玩家可以选择是否装备手电筒
|
||||||
@ -65,3 +50,21 @@ dam1.png ---- 存档 404
|
|||||||
dam2.png ---- 存档 285
|
dam2.png ---- 存档 285
|
||||||
dam3.png ---- 存档 243
|
dam3.png ---- 存档 243
|
||||||
dam4.png ---- 存档 59
|
dam4.png ---- 存档 59
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
[] 实时天气
|
||||||
|
[x] 成就系统
|
||||||
|
[] 装备合成、装备(孔)强化
|
||||||
|
[] 宝石目标设定
|
||||||
|
[] 自动宝物规划,选中两个或更多宝物后自动在本地图中规划出最优拾取路线,原则是尽量减少其余宝物的捡拾,自动切换主动技能,怪物造成的伤害最低的路线
|
||||||
|
[] 临界显示方式,宝石数还是数值
|
||||||
|
[x] 怪物目标设定
|
||||||
|
[x] 木牌查看系统(百科全书)
|
||||||
|
[] 宝物目标设定
|
||||||
|
[] 每个怪物加一个怪物说明
|
||||||
|
[] 歌词展示系统
|
||||||
|
[] 小地图显示框,可以选择是否显示剩余怪物数量等
|
||||||
|
[] 怪物死亡特效
|
||||||
|
[] 区域名称显示特效,3D 粒子
|
||||||
|
[] 单独的工具栏
|
||||||
|
@ -47,12 +47,6 @@ var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
|
|||||||
_lint: true,
|
_lint: true,
|
||||||
_data: '楼层飞行'
|
_data: '楼层飞行'
|
||||||
},
|
},
|
||||||
beforeBattle: {
|
|
||||||
_leaf: true,
|
|
||||||
_type: 'textarea',
|
|
||||||
_lint: true,
|
|
||||||
_data: '战前判定'
|
|
||||||
},
|
|
||||||
afterBattle: {
|
afterBattle: {
|
||||||
_leaf: true,
|
_leaf: true,
|
||||||
_type: 'textarea',
|
_type: 'textarea',
|
||||||
|
@ -203,14 +203,11 @@ enemys.prototype.getEnemyValue = function (enemy, name, x, y, floorId) {
|
|||||||
|
|
||||||
////// 能否获胜 //////
|
////// 能否获胜 //////
|
||||||
enemys.prototype.canBattle = function (enemy, x, y, floorId) {
|
enemys.prototype.canBattle = function (enemy, x, y, floorId) {
|
||||||
// todo: 重写这个函数
|
// Deprecated. See src/plugin/game/battle.ts
|
||||||
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
|
||||||
var damage = this.getDamage(enemy, x, y, floorId);
|
|
||||||
return damage != null && damage < core.status.hero.hp;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enemys.prototype.getDamageString = function (enemy, x, y, floorId, hero) {
|
enemys.prototype.getDamageString = function (enemy, x, y, floorId, hero) {
|
||||||
// todo: 重写这个函数
|
// todo: 删除
|
||||||
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
||||||
var damage = this.getDamage(enemy, x, y, floorId, hero);
|
var damage = this.getDamage(enemy, x, y, floorId, hero);
|
||||||
|
|
||||||
@ -237,7 +234,7 @@ enemys.prototype.getDamageString = function (enemy, x, y, floorId, hero) {
|
|||||||
|
|
||||||
////// 接下来N个临界值和临界减伤计算 //////
|
////// 接下来N个临界值和临界减伤计算 //////
|
||||||
enemys.prototype.nextCriticals = function (enemy, number, x, y, floorId, hero) {
|
enemys.prototype.nextCriticals = function (enemy, number, x, y, floorId, hero) {
|
||||||
// todo: 删除 getDamageInfo
|
// todo: 删除这个函数
|
||||||
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
||||||
number = number || 1;
|
number = number || 1;
|
||||||
|
|
||||||
@ -284,7 +281,7 @@ enemys.prototype._nextCriticals_overAtk = function (
|
|||||||
floorId,
|
floorId,
|
||||||
hero
|
hero
|
||||||
) {
|
) {
|
||||||
// todo: 删除 getDamageInfo
|
// todo: 删除这个函数
|
||||||
var calNext = function (currAtk, maxAtk) {
|
var calNext = function (currAtk, maxAtk) {
|
||||||
var start = currAtk,
|
var start = currAtk,
|
||||||
end = maxAtk;
|
end = maxAtk;
|
||||||
@ -342,7 +339,7 @@ enemys.prototype._nextCriticals_useBinarySearch = function (
|
|||||||
floorId,
|
floorId,
|
||||||
hero
|
hero
|
||||||
) {
|
) {
|
||||||
// todo: 删除 getDamageInfo
|
// todo: 删除这个函数
|
||||||
var mon_hp = info.mon_hp,
|
var mon_hp = info.mon_hp,
|
||||||
hero_atk = core.getStatusOrDefault(hero, 'atk'),
|
hero_atk = core.getStatusOrDefault(hero, 'atk'),
|
||||||
mon_def = info.mon_def,
|
mon_def = info.mon_def,
|
||||||
@ -417,7 +414,7 @@ enemys.prototype.getDefDamage = function (enemy, k, x, y, floorId, hero) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enemys.prototype.getEnemyInfo = function (enemy, hero, x, y, floorId) {
|
enemys.prototype.getEnemyInfo = function (enemy, hero, x, y, floorId) {
|
||||||
// throw new Error(`This function has been deprecated.`);
|
// todo: 删除这个函数
|
||||||
if (enemy == null) return null;
|
if (enemy == null) return null;
|
||||||
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
||||||
return this.enemydata.getEnemyInfo(enemy, hero, x, y, floorId);
|
return this.enemydata.getEnemyInfo(enemy, hero, x, y, floorId);
|
||||||
@ -425,7 +422,7 @@ enemys.prototype.getEnemyInfo = function (enemy, hero, x, y, floorId) {
|
|||||||
|
|
||||||
////// 获得战斗伤害信息(实际伤害计算函数) //////
|
////// 获得战斗伤害信息(实际伤害计算函数) //////
|
||||||
enemys.prototype.getDamageInfo = function (enemy, hero, x, y, floorId) {
|
enemys.prototype.getDamageInfo = function (enemy, hero, x, y, floorId) {
|
||||||
// throw new Error(`This function has been deprecated.`);
|
// todo: 删除这个函数
|
||||||
if (enemy == null) return null;
|
if (enemy == null) return null;
|
||||||
// 移动到了脚本编辑 - getDamageInfo中
|
// 移动到了脚本编辑 - getDamageInfo中
|
||||||
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
||||||
@ -434,11 +431,12 @@ enemys.prototype.getDamageInfo = function (enemy, hero, x, y, floorId) {
|
|||||||
|
|
||||||
////// 获得在某个勇士属性下怪物伤害 //////
|
////// 获得在某个勇士属性下怪物伤害 //////
|
||||||
enemys.prototype.getDamage = function (enemy, x, y, floorId, hero) {
|
enemys.prototype.getDamage = function (enemy, x, y, floorId, hero) {
|
||||||
|
// todo: 修改这个函数的参数
|
||||||
return this._getDamage(enemy, hero, x, y, floorId);
|
return this._getDamage(enemy, hero, x, y, floorId);
|
||||||
};
|
};
|
||||||
|
|
||||||
enemys.prototype._getDamage = function (enemy, hero, x, y, floorId) {
|
enemys.prototype._getDamage = function (enemy, hero, x, y, floorId) {
|
||||||
// todo: 删除 getDamageInfo
|
// todo: 重写这个函数
|
||||||
if (enemy == null) enemy = core.getBlockId(x, y, floorId);
|
if (enemy == null) enemy = core.getBlockId(x, y, floorId);
|
||||||
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
|
||||||
if (enemy == null) return null;
|
if (enemy == null) return null;
|
||||||
@ -451,7 +449,7 @@ enemys.prototype._getDamage = function (enemy, hero, x, y, floorId) {
|
|||||||
|
|
||||||
////// 获得当前楼层的怪物列表 //////
|
////// 获得当前楼层的怪物列表 //////
|
||||||
enemys.prototype.getCurrentEnemys = function (floorId) {
|
enemys.prototype.getCurrentEnemys = function (floorId) {
|
||||||
// todo: 删除 getEnemyInfo - _getCurrentEnemys_addEnemy
|
// todo: 重写这个函数
|
||||||
floorId = floorId || core.status.floorId;
|
floorId = floorId || core.status.floorId;
|
||||||
var enemys = [],
|
var enemys = [],
|
||||||
used = {};
|
used = {};
|
||||||
|
@ -430,16 +430,7 @@ events.prototype._trigger_ignoreChangeFloor = function (block) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
events.prototype._sys_battle = function (data, callback) {
|
events.prototype._sys_battle = function (data, callback) {
|
||||||
// 检查是否需要改变朝向
|
// todo: 重写这个函数的一部分
|
||||||
/* if (data.x == core.nextX() && data.y == core.nextY()) {
|
|
||||||
var dir = core.turnDirection(":back");
|
|
||||||
var id = data.event.id, toId = (data.event.faceIds || {})[dir];
|
|
||||||
if (toId && id != toId) {
|
|
||||||
var number = core.getNumberById(toId);
|
|
||||||
if (number > 0)
|
|
||||||
core.setBlock(number, data.x, data.y);
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
// 检查战前事件
|
// 检查战前事件
|
||||||
var beforeBattle = [];
|
var beforeBattle = [];
|
||||||
@ -471,35 +462,17 @@ events.prototype._sys_battle = function (data, callback) {
|
|||||||
|
|
||||||
////// 战斗 //////
|
////// 战斗 //////
|
||||||
events.prototype.battle = function (id, x, y, force, callback) {
|
events.prototype.battle = function (id, x, y, force, callback) {
|
||||||
// todo: 重写这个函数的一部分,修改参数
|
// Deprecated. See src/plugin/game/battle.ts
|
||||||
core.saveAndStopAutomaticRoute();
|
|
||||||
id = id || core.getBlockId(x, y);
|
|
||||||
if (!id) return core.clearContinueAutomaticRoute(callback);
|
|
||||||
// 非强制战斗
|
|
||||||
if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) {
|
|
||||||
core.stopSound();
|
|
||||||
core.playSound('操作失败');
|
|
||||||
core.drawTip('你打不过此怪物!', id);
|
|
||||||
return core.clearContinueAutomaticRoute(callback);
|
|
||||||
}
|
|
||||||
// 自动存档
|
|
||||||
if (!core.status.event.id) core.autosave(true);
|
|
||||||
// 战前事件
|
|
||||||
if (!this.beforeBattle(id, x, y))
|
|
||||||
return core.clearContinueAutomaticRoute(callback);
|
|
||||||
// 战后事件
|
|
||||||
this.afterBattle(id, x, y);
|
|
||||||
if (callback) callback();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
////// 战斗前触发的事件 //////
|
////// 战斗前触发的事件 //////
|
||||||
events.prototype.beforeBattle = function (enemyId, x, y) {
|
events.prototype.beforeBattle = function (enemyId, x, y) {
|
||||||
return this.eventdata.beforeBattle(enemyId, x, y);
|
// Deprecated. See src/plugin/game/battle.ts
|
||||||
};
|
};
|
||||||
|
|
||||||
////// 战斗结束后触发的事件 //////
|
////// 战斗结束后触发的事件 //////
|
||||||
events.prototype.afterBattle = function (enemyId, x, y) {
|
events.prototype.afterBattle = function (enemyId, x, y) {
|
||||||
return this.eventdata.afterBattle(enemyId, x, y);
|
// Deprecated. See src/plugin/game/battle.ts
|
||||||
};
|
};
|
||||||
|
|
||||||
events.prototype._sys_openDoor = function (data, callback) {
|
events.prototype._sys_openDoor = function (data, callback) {
|
||||||
@ -2140,6 +2113,7 @@ events.prototype._action_disableShop = function (data, x, y, prefix) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
events.prototype._action_battle = function (data, x, y, prefix) {
|
events.prototype._action_battle = function (data, x, y, prefix) {
|
||||||
|
// todo: 修改battle的参数
|
||||||
if (data.id) {
|
if (data.id) {
|
||||||
this.battle(data.id, null, null, true, core.doAction);
|
this.battle(data.id, null, null, true, core.doAction);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3531,7 +3531,7 @@ maps.prototype.resetMap = function (floorId) {
|
|||||||
|
|
||||||
////// 初始化独立的block canvas //////
|
////// 初始化独立的block canvas //////
|
||||||
maps.prototype._initDetachedBlock = function (blockInfo, x, y, displayDamage) {
|
maps.prototype._initDetachedBlock = function (blockInfo, x, y, displayDamage) {
|
||||||
// todo: 不使用 nextCriticals
|
// todo: 不使用 nextCriticals 和 getDamageString
|
||||||
var headCanvas = null,
|
var headCanvas = null,
|
||||||
bodyCanvas = '__body_' + x + '_' + y,
|
bodyCanvas = '__body_' + x + '_' + y,
|
||||||
damageCanvas = null;
|
damageCanvas = null;
|
||||||
|
@ -262,139 +262,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
beforeBattle: function (enemyId, x, y) {
|
|
||||||
// 战斗前触发的事件,可以加上一些战前特效(详见下面支援的例子)
|
|
||||||
// 此函数在“检测能否战斗和自动存档”【之后】执行。如果需要更早的战前事件,请在插件中覆重写 core.events.doSystemEvent 函数。
|
|
||||||
// 返回true则将继续战斗,返回false将不再战斗。
|
|
||||||
// todo: 重写
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
afterBattle: function (enemyId, x, y) {
|
|
||||||
// todo: 重写
|
|
||||||
// 战斗结束后触发的事件
|
|
||||||
const floorId = core.status.floorId;
|
|
||||||
|
|
||||||
var enemy = core.material.enemys[enemyId];
|
|
||||||
var special = enemy.special;
|
|
||||||
|
|
||||||
// 播放战斗音效和动画
|
|
||||||
// 默认播放的动画;你也可以使用
|
|
||||||
var animate = 'hand'; // 默认动画
|
|
||||||
// 检查当前装备是否存在攻击动画
|
|
||||||
var 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');
|
|
||||||
|
|
||||||
// 播放动画;如果不存在坐标(强制战斗)则播放到勇士自身
|
|
||||||
if (x != null && y != null) core.drawAnimate(animate, x, y);
|
|
||||||
else core.drawHeroAnimate(animate);
|
|
||||||
|
|
||||||
// 获得战斗伤害信息
|
|
||||||
// 注意这里勇士坐标要传入当前勇士坐标,不然会默认取伤害最低的地方打怪
|
|
||||||
const damageInfo =
|
|
||||||
core.getDamageInfo(
|
|
||||||
enemyId,
|
|
||||||
{ x: core.status.hero.loc.x, y: core.status.hero.loc.y },
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
) ?? {};
|
|
||||||
// 战斗伤害
|
|
||||||
const damage = damageInfo.damage;
|
|
||||||
// 判定是否致死
|
|
||||||
if (damage == null || 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 (core.hasSpecial(special, 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 (core.hasSpecial(special, 22)) {
|
|
||||||
flags[`night_${floorId}`] ??= 0;
|
|
||||||
flags[`night_${floorId}`] -= enemy.night;
|
|
||||||
}
|
|
||||||
if (core.hasSpecial(special, 23)) {
|
|
||||||
flags[`night_${floorId}`] ??= 0;
|
|
||||||
flags[`night_${floorId}`] += enemy.day;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (core.plugin.skillTree.getSkillLevel(11) > 0) {
|
|
||||||
core.plugin.study.declineStudiedSkill();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是融化怪,需要特殊标记一下
|
|
||||||
if (core.hasSpecial(special, 25) && core.has(x) && core.has(y)) {
|
|
||||||
flags[`melt_${floorId}`] ??= {};
|
|
||||||
flags[`melt_${floorId}`][`${x},${y}`] = enemy.melt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获得金币
|
|
||||||
const money = enemy.money;
|
|
||||||
core.status.hero.money += money;
|
|
||||||
core.status.hero.statistics.money += money;
|
|
||||||
|
|
||||||
// 获得经验
|
|
||||||
const exp = enemy.exp;
|
|
||||||
core.status.hero.exp += exp;
|
|
||||||
core.status.hero.statistics.exp += exp;
|
|
||||||
|
|
||||||
const hint =
|
|
||||||
'打败 ' + 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 事件的处理
|
|
||||||
var todo = [];
|
|
||||||
|
|
||||||
// 战后事件
|
|
||||||
if (core.status.floorId != null) {
|
|
||||||
core.push(
|
|
||||||
todo,
|
|
||||||
core.floors[core.status.floorId].afterBattle[x + ',' + y]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
core.push(todo, enemy.afterBattle);
|
|
||||||
|
|
||||||
// 如果事件不为空,将其插入
|
|
||||||
if (todo.length > 0) core.insertAction(todo, x, y);
|
|
||||||
|
|
||||||
// 因为removeBlock和hideBlock都会刷新状态栏,因此将删除部分移动到这里并保证刷新只执行一次,以提升效率
|
|
||||||
if (core.getBlock(x, y) != null) {
|
|
||||||
core.removeBlock(x, y);
|
|
||||||
} else {
|
|
||||||
core.updateStatusBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果已有事件正在处理中
|
|
||||||
if (core.status.event.id == null) core.continueAutomaticRoute();
|
|
||||||
else core.clearContinueAutomaticRoute();
|
|
||||||
},
|
|
||||||
afterOpenDoor: function (doorId, x, y) {
|
afterOpenDoor: function (doorId, x, y) {
|
||||||
// 开一个门后触发的事件
|
// 开一个门后触发的事件
|
||||||
|
|
||||||
|
232
src/plugin/game/battle.ts
Normal file
232
src/plugin/game/battle.ts
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import { DamageDir, DamageEnemy, getNeedCalDir } from './damage';
|
||||||
|
import { findDir, has } from './utils';
|
||||||
|
|
||||||
|
export function getEnemy(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
floorId: FloorIds = core.status.floorId
|
||||||
|
) {
|
||||||
|
const enemy = core.status.maps[floorId].enemy.list.find(v => {
|
||||||
|
return v.x === x && v.y === y;
|
||||||
|
});
|
||||||
|
if (!enemy) {
|
||||||
|
throw new Error(
|
||||||
|
`Get null when getting enemy on '${x},${y}' in '${floorId}'`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return enemy;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.enemys.canBattle = function (
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
floorId: FloorIds = core.status.floorId,
|
||||||
|
dir: DamageDir | DamageDir[] = getNeedCalDir(x, y, floorId)
|
||||||
|
) {
|
||||||
|
const enemy = getEnemy(x, y, floorId);
|
||||||
|
const damage = enemy.calEnemyDamage(core.status.hero, dir);
|
||||||
|
|
||||||
|
return damage.some(v => {
|
||||||
|
return v.damage < core.status.hero.hp;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
core.events.battle = function (
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
dir: DamageDir,
|
||||||
|
force: boolean = false,
|
||||||
|
callback?: () => void
|
||||||
|
) {
|
||||||
|
core.saveAndStopAutomaticRoute();
|
||||||
|
const enemy = getEnemy(x, y);
|
||||||
|
// 非强制战斗
|
||||||
|
if (
|
||||||
|
!core.enemys.canBattle(x, y, void 0, dir) &&
|
||||||
|
!force &&
|
||||||
|
!core.status.event.id
|
||||||
|
) {
|
||||||
|
core.stopSound();
|
||||||
|
core.playSound('操作失败');
|
||||||
|
core.drawTip('你打不过此怪物!', enemy.id);
|
||||||
|
return core.clearContinueAutomaticRoute(callback);
|
||||||
|
}
|
||||||
|
// 自动存档
|
||||||
|
if (!core.status.event.id) core.autosave(true);
|
||||||
|
// 战前事件
|
||||||
|
if (!this.beforeBattle(enemy, x, y, dir))
|
||||||
|
return core.clearContinueAutomaticRoute(callback);
|
||||||
|
// 战后事件
|
||||||
|
this.afterBattle(enemy, x, y, dir);
|
||||||
|
callback?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
core.events.beforeBattle = function () {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
core.events.afterBattle = function (
|
||||||
|
enemy: DamageEnemy,
|
||||||
|
x?: number,
|
||||||
|
y?: number,
|
||||||
|
dir: DamageDir = 'none'
|
||||||
|
) {
|
||||||
|
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.calEnemyDamage(core.status.hero, dir)[0];
|
||||||
|
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 (core.hasSpecial(special, 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 (core.hasSpecial(special, 22)) {
|
||||||
|
flags[`night_${floorId}`] ??= 0;
|
||||||
|
flags[`night_${floorId}`] -= enemy.enemy.night!;
|
||||||
|
}
|
||||||
|
if (core.hasSpecial(special, 23)) {
|
||||||
|
flags[`night_${floorId}`] ??= 0;
|
||||||
|
flags[`night_${floorId}`] += enemy.enemy.day;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (core.plugin.skillTree.getSkillLevel(11) > 0) {
|
||||||
|
// core.plugin.study.declineStudiedSkill();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 如果是融化怪,需要特殊标记一下
|
||||||
|
if (core.hasSpecial(special, 25) && core.has(x) && core.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.events._sys_battle = function (data: Block, callback?: () => void) {
|
||||||
|
// todo: 重写这个函数的一部分
|
||||||
|
|
||||||
|
// 检查战前事件
|
||||||
|
const floor = core.floors[core.status.floorId];
|
||||||
|
const beforeBattle: MotaEvent = [];
|
||||||
|
const loc = `${data.x},${data.y}` as LocString;
|
||||||
|
const enemy = getEnemy(data.x, data.y);
|
||||||
|
|
||||||
|
beforeBattle.push(...(floor.beforeBattle[loc] ?? []));
|
||||||
|
beforeBattle.push(...(enemy.enemy.beforeBattle ?? []));
|
||||||
|
|
||||||
|
if (beforeBattle.length > 0) {
|
||||||
|
beforeBattle.push({ type: 'battle', x: data.x, y: data.y });
|
||||||
|
core.clearContinueAutomaticRoute();
|
||||||
|
|
||||||
|
// 自动存档
|
||||||
|
var inAction = core.status.event.id == 'action';
|
||||||
|
if (inAction) {
|
||||||
|
core.insertAction(beforeBattle, data.x, data.y);
|
||||||
|
core.doAction();
|
||||||
|
} else {
|
||||||
|
core.autosave(true);
|
||||||
|
core.insertAction(beforeBattle, data.x, data.y, callback);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const dir = findDir(data, core.status.hero.loc) as DamageDir;
|
||||||
|
this.battle(data.x, data.y, dir, false, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Events {
|
||||||
|
/**
|
||||||
|
* 与怪物战斗前
|
||||||
|
* @param x 怪物横坐标
|
||||||
|
* @param y 怪物纵坐标
|
||||||
|
*/
|
||||||
|
beforeBattle(
|
||||||
|
enemy: DamageEnemy,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
dir: DamageDir
|
||||||
|
): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与怪物战斗后
|
||||||
|
*/
|
||||||
|
afterBattle(
|
||||||
|
enemy: DamageEnemy,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
dir: DamageDir
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
}
|
@ -73,7 +73,7 @@ interface CriticalDamageDelta extends Omit<DamageDelta, 'info'> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HaloFn = (info: EnemyInfo, enemy: Enemy) => void;
|
type HaloFn = (info: EnemyInfo, enemy: Enemy) => void;
|
||||||
type DamageDir = Dir | 'none';
|
export type DamageDir = Dir | 'none';
|
||||||
|
|
||||||
/** 光环属性 */
|
/** 光环属性 */
|
||||||
export const haloSpecials: number[] = [8, 21, 25, 26, 27];
|
export const haloSpecials: number[] = [8, 21, 25, 26, 27];
|
||||||
@ -695,7 +695,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
|||||||
damage[loc].type.add(type);
|
damage[loc].type.add(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private calEnemyDamage(
|
calEnemyDamage(
|
||||||
hero: Partial<HeroStatus> = core.status.hero,
|
hero: Partial<HeroStatus> = core.status.hero,
|
||||||
dir: DamageDir | DamageDir[]
|
dir: DamageDir | DamageDir[]
|
||||||
): DamageInfo[] {
|
): DamageInfo[] {
|
||||||
|
@ -18,6 +18,7 @@ import * as towerBoss from './towerBoss';
|
|||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
import * as chase from './chase';
|
import * as chase from './chase';
|
||||||
import * as damage from './damage';
|
import * as damage from './damage';
|
||||||
|
import * as battle from './battle';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
halo,
|
halo,
|
||||||
@ -32,5 +33,6 @@ export {
|
|||||||
towerBoss,
|
towerBoss,
|
||||||
utils,
|
utils,
|
||||||
chase,
|
chase,
|
||||||
damage
|
damage,
|
||||||
|
battle
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
///<reference path="../../../src/types/core.d.ts" />
|
///<reference path="../../../src/types/core.d.ts" />
|
||||||
|
|
||||||
import { studySkill, canStudySkill } from './study.js';
|
import { studySkill, canStudySkill } from './study';
|
||||||
|
|
||||||
const replayableSettings = ['autoSkill'];
|
const replayableSettings = ['autoSkill'];
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
///<reference path="../../../src/types/core.d.ts" />
|
|
||||||
|
|
||||||
// 负责勇士技能:学习
|
// 负责勇士技能:学习
|
||||||
const values = {
|
const values: Record<number, string[]> = {
|
||||||
1: ['crit'],
|
1: ['crit'],
|
||||||
6: ['n'],
|
6: ['n'],
|
||||||
7: ['hungry'],
|
7: ['hungry'],
|
||||||
@ -12,7 +10,7 @@ const values = {
|
|||||||
|
|
||||||
const cannotStudy = [9, 12, 14, 15, 24];
|
const cannotStudy = [9, 12, 14, 15, 24];
|
||||||
|
|
||||||
export function canStudySkill(number) {
|
export function canStudySkill(number: number) {
|
||||||
const s = (core.status.hero.special ??= { num: [], last: [] });
|
const s = (core.status.hero.special ??= { num: [], last: [] });
|
||||||
if (core.plugin.skillTree.getSkillLevel(11) === 0) return false;
|
if (core.plugin.skillTree.getSkillLevel(11) === 0) return false;
|
||||||
if (s.num.length >= 1) return false;
|
if (s.num.length >= 1) return false;
|
||||||
@ -21,7 +19,7 @@ export function canStudySkill(number) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function studySkill(enemy, number) {
|
export function studySkill(enemy: any, number: number) {
|
||||||
core.status.hero.special ??= { num: [], last: [] };
|
core.status.hero.special ??= { num: [], last: [] };
|
||||||
const s = core.status.hero.special;
|
const s = core.status.hero.special;
|
||||||
const specials = core.getSpecials();
|
const specials = core.getSpecials();
|
||||||
@ -41,13 +39,13 @@ export function studySkill(enemy, number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forgetStudiedSkill(num, i) {
|
export function forgetStudiedSkill(num: number, i: number) {
|
||||||
const s = core.status.hero.special;
|
const s = core.status.hero.special;
|
||||||
const index = i !== void 0 && i !== null ? i : s.num.indexOf(num);
|
const index = i !== void 0 && i !== null ? i : s.num.indexOf(num);
|
||||||
if (index === -1) return;
|
if (index === -1) return;
|
||||||
s.num.splice(index, 1);
|
s.num.splice(index, 1);
|
||||||
s.last.splice(index, 1);
|
s.last.splice(index, 1);
|
||||||
const value = values[number] ?? [];
|
const value = values[num] ?? [];
|
||||||
for (const key of value) {
|
for (const key of value) {
|
||||||
delete s[key];
|
delete s[key];
|
||||||
}
|
}
|
||||||
@ -62,16 +60,8 @@ export function checkStudiedSkill() {
|
|||||||
const s = core.status.hero.special;
|
const s = core.status.hero.special;
|
||||||
for (let i = 0; i < s.last.length; i++) {
|
for (let i = 0; i < s.last.length; i++) {
|
||||||
if (s.last[i] <= 0) {
|
if (s.last[i] <= 0) {
|
||||||
this.forgetStudiedSkill(void 0, i);
|
forgetStudiedSkill(1, i);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
core.plugin.study = {
|
|
||||||
canStudySkill,
|
|
||||||
studySkill,
|
|
||||||
forgetStudiedSkill,
|
|
||||||
declineStudiedSkill,
|
|
||||||
checkStudiedSkill
|
|
||||||
};
|
|
@ -155,6 +155,21 @@ export function boundary(arr: any, key?: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取两个坐标的相对方向
|
||||||
|
* @param from 初始坐标
|
||||||
|
* @param to 指向坐标
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
})?.[0] as Dir2) ?? 'none'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface GamePluginUtils {
|
interface GamePluginUtils {
|
||||||
ofDir: typeof ofDir;
|
ofDir: typeof ofDir;
|
||||||
|
13
src/types/enemy.d.ts
vendored
13
src/types/enemy.d.ts
vendored
@ -26,7 +26,10 @@ type PartialNumbericEnemyProperty =
|
|||||||
| 'courage'
|
| 'courage'
|
||||||
| 'charge'
|
| 'charge'
|
||||||
| 'paleShield'
|
| 'paleShield'
|
||||||
| 'iceHalo';
|
| 'iceHalo'
|
||||||
|
| 'day'
|
||||||
|
| 'night'
|
||||||
|
| 'melt';
|
||||||
|
|
||||||
type BooleanEnemyProperty =
|
type BooleanEnemyProperty =
|
||||||
| 'zoneSquare'
|
| 'zoneSquare'
|
||||||
@ -314,10 +317,10 @@ interface Enemys extends EnemyData {
|
|||||||
* @returns true表示可以打败,false表示无法打败
|
* @returns true表示可以打败,false表示无法打败
|
||||||
*/
|
*/
|
||||||
canBattle(
|
canBattle(
|
||||||
enemy: EnemyIds | Enemy,
|
x: number,
|
||||||
x?: number,
|
y: number,
|
||||||
y?: number,
|
floorId?: FloorIds,
|
||||||
floorId?: FloorIds
|
dir?: Dir | 'none' | (Dir | 'none')[]
|
||||||
): boolean;
|
): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
10
src/types/event.d.ts
vendored
10
src/types/event.d.ts
vendored
@ -109,10 +109,10 @@ interface Events extends EventData {
|
|||||||
* @param callback 回调函数
|
* @param callback 回调函数
|
||||||
*/
|
*/
|
||||||
battle(
|
battle(
|
||||||
id: AllIdsOf<'enemys' | 'enemy48'>,
|
x: number,
|
||||||
x?: number,
|
y: number,
|
||||||
y?: number,
|
dir: Dir | 'none',
|
||||||
force?: boolean,
|
force: boolean = false,
|
||||||
callback?: () => void
|
callback?: () => void
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
@ -758,6 +758,8 @@ interface Events extends EventData {
|
|||||||
* @param itemId 道具id,其中敌人手册、传送器和飞行器会被特殊处理
|
* @param itemId 道具id,其中敌人手册、传送器和飞行器会被特殊处理
|
||||||
*/
|
*/
|
||||||
tryUseItem(itemId: ItemIdOf<'tools' | 'constants'>): void;
|
tryUseItem(itemId: ItemIdOf<'tools' | 'constants'>): void;
|
||||||
|
|
||||||
|
_sys_battle(data: Block, callback?: () => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const events: new () => Events;
|
declare const events: new () => Events;
|
||||||
|
12
src/types/function.d.ts
vendored
12
src/types/function.d.ts
vendored
@ -161,18 +161,6 @@ interface EventData {
|
|||||||
*/
|
*/
|
||||||
flyTo(toId: FloorIds, callback?: () => void): boolean;
|
flyTo(toId: FloorIds, callback?: () => void): boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* 与怪物战斗前
|
|
||||||
* @param enemyId 打败的怪物
|
|
||||||
* @param x 怪物横坐标
|
|
||||||
* @param y 怪物纵坐标
|
|
||||||
*/
|
|
||||||
beforeBattle(
|
|
||||||
enemyId: AllIdsOf<'enemys' | 'enemy48'>,
|
|
||||||
x?: number,
|
|
||||||
y?: number
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 与怪物战斗后
|
* 与怪物战斗后
|
||||||
* @param enemyId 打败的怪物
|
* @param enemyId 打败的怪物
|
||||||
|
2
src/types/plugin.d.ts
vendored
2
src/types/plugin.d.ts
vendored
@ -462,8 +462,6 @@ interface Skill {
|
|||||||
effect: string[];
|
effect: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DamageEnemy {}
|
|
||||||
|
|
||||||
type Forward<T> = {
|
type Forward<T> = {
|
||||||
[K in keyof T as T[K] extends Function
|
[K in keyof T as T[K] extends Function
|
||||||
? K extends `_${string}`
|
? K extends `_${string}`
|
||||||
|
4
src/types/status.d.ts
vendored
4
src/types/status.d.ts
vendored
@ -963,8 +963,8 @@ interface HeroStatus {
|
|||||||
* 勇士学习的特技
|
* 勇士学习的特技
|
||||||
*/
|
*/
|
||||||
special: {
|
special: {
|
||||||
num: [];
|
num: number[];
|
||||||
last: [];
|
last: number[];
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user