完善装备栏、怪物手册,更新样板

This commit is contained in:
unanmed 2022-12-27 23:31:14 +08:00
parent bde0a4d1c0
commit 2f88272df8
37 changed files with 10569 additions and 2558 deletions

1
.gitignore vendored
View File

@ -27,3 +27,4 @@ dist-ssr
*.sw? *.sw?
dist.zip dist.zip
story.md

View File

@ -1,2 +1,3 @@
vite.config.ts vite.config.ts
public/project/data.js public/project/data.js
story.md

14
idea.md
View File

@ -10,13 +10,14 @@
- 抢夺:抢夺你的装备并使用(简单难度中,怪物使用时属性降低) - 抢夺:抢夺你的装备并使用(简单难度中,怪物使用时属性降低)
- 抢夺 2.0:抢夺你的道具(怪物手册和楼传除外),并按照左上、右上、右下、左下的顺序依次放到地图的四角(简单无效) - 抢夺 2.0:抢夺你的道具(怪物手册和楼传除外),并按照左上、右上、右下、左下的顺序依次放到地图的四角(简单无效)
- 抢夺 3.0:抢夺装备,但每过 10 回合便按照 2.0 方法扔至地图四角(因为怪物觉得装备不好使),然后重新抢夺你的装备(简单无效) - 抢夺 3.0:抢夺装备,但每过 10 回合便按照 2.0 方法扔至地图四角(因为怪物觉得装备不好使),然后重新抢夺你的装备(简单无效)
- 天气之子:控制天气,包括下雨、下雪、晴天、阴天等,
#### Boss #### Boss
音游,音乐为一个被遗忘的夜晚,可选简单与困难,困难可获得成就冰与火之舞 音游,音乐为一个被遗忘的夜晚,可选简单与困难,困难可获得成就冰与火之舞
玩法一个会转动的圆盘带有一个伸出去的把手boss 从四面八方射子弹,当子弹恰好落到把手前端时,点击按键或屏幕,可以抵挡子弹,简单难度 3 条命,困难 1 条。简单难度音符密度低。困难难度为冰与火之舞的节奏。简单难度判定时间为前后各 150ms困难为 75ms 玩法一个会转动的圆盘带有一个伸出去的把手boss 从四面八方射子弹,当子弹恰好落到把手前端时,点击按键或屏幕,可以抵挡子弹,简单难度 3 条命,困难 1 条。简单难度音符密度低。困难难度为冰与火之舞的节奏。简单难度判定时间为前后各 100ms困难为 50ms
### 第三章 战争
## 主角 ## 主角
@ -32,6 +33,14 @@
- 实时天气 - 实时天气
- 成就系统 - 成就系统
- 装备合成、装备(孔)强化 - 装备合成、装备(孔)强化
- 宝石目标设定
- 自动宝物规划,选中两个或更多宝物后自动在本地图中规划出最优拾取路线,原则是尽量减少其余宝物的捡拾,自动切换主动技能,怪物造成的伤害最低的路线
- 临界显示方式,宝石数还是数值
- 怪物目标设定
- 木牌查看系统
- 宝物目标设定
- 每个怪物加一个怪物说明
- 歌词展示系统
### 第二章 智慧 ### 第二章 智慧
@ -39,6 +48,7 @@
## 成就 ## 成就
- 虚惊一场:打完山洞门口的怪只剩 1 滴血
- 学坏了:学习敌人的电摇嘲讽技能 - 学坏了:学习敌人的电摇嘲讽技能
- 我就是傻子:不学习反抢夺通过第二章 - 我就是傻子:不学习反抢夺通过第二章
- 真能刷:勇气之路的刷血怪刷到 15w 以上的血 - 真能刷:勇气之路的刷血怪刷到 15w 以上的血

View File

@ -8,7 +8,8 @@
"build": "vue-tsc && vite build && ts-node-esm script/compress.ts", "build": "vue-tsc && vite build && ts-node-esm script/compress.ts",
"preview": "vite preview", "preview": "vite preview",
"preview-node": "cd dist && node server.cjs", "preview-node": "cd dist && node server.cjs",
"update": "ts-node-esm script/update.ts" "update": "ts-node-esm script/update.ts",
"declare": "ts-node-esm script/declare.ts"
}, },
"dependencies": { "dependencies": {
"@ant-design/icons-vue": "^6.1.0", "@ant-design/icons-vue": "^6.1.0",

View File

@ -1,7 +1,6 @@
'use strict';
"use strict"; function items() {
function items () {
this._init(); this._init();
} }
@ -11,7 +10,7 @@ items.prototype._init = function () {
for (var itemId in this.items) { for (var itemId in this.items) {
this.items[itemId].id = itemId; this.items[itemId].id = itemId;
} }
} };
////// 获得所有道具 ////// ////// 获得所有道具 //////
items.prototype.getItems = function () { items.prototype.getItems = function () {
@ -23,7 +22,7 @@ items.prototype.getItems = function () {
} }
} }
return items; return items;
} };
////// “即捡即用类”道具的使用效果 ////// ////// “即捡即用类”道具的使用效果 //////
items.prototype.getItemEffect = function (itemId, itemNum) { items.prototype.getItemEffect = function (itemId, itemNum) {
@ -34,10 +33,8 @@ items.prototype.getItemEffect = function (itemId, itemNum) {
var itemEffect = core.material.items[itemId].itemEffect; var itemEffect = core.material.items[itemId].itemEffect;
if (itemEffect) { if (itemEffect) {
try { try {
for (var i = 0; i < itemNum; ++i) for (var i = 0; i < itemNum; ++i) eval(itemEffect);
eval(itemEffect); } catch (e) {
}
catch (e) {
console.error(e); console.error(e);
} }
} }
@ -47,17 +44,15 @@ items.prototype.getItemEffect = function (itemId, itemNum) {
if (useItemEvent) { if (useItemEvent) {
try { try {
core.insertAction(useItemEvent); core.insertAction(useItemEvent);
} } catch (e) {
catch (e) {
console.error(e); console.error(e);
} }
} }
core.updateStatusBar(false, true); core.updateStatusBar(false, true);
} } else {
else {
core.addItem(itemId, itemNum); core.addItem(itemId, itemNum);
} }
} };
////// “即捡即用类”道具的文字提示 ////// ////// “即捡即用类”道具的文字提示 //////
items.prototype.getItemEffectTip = function (itemId) { items.prototype.getItemEffectTip = function (itemId) {
@ -67,15 +62,15 @@ items.prototype.getItemEffectTip = function (itemId) {
var itemEffectTip = core.material.items[itemId].itemEffectTip; var itemEffectTip = core.material.items[itemId].itemEffectTip;
if (itemEffectTip) { if (itemEffectTip) {
try { try {
return core.replaceText(itemEffectTip) || ""; return core.replaceText(itemEffectTip) || '';
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return ""; return '';
} }
} }
} }
return ""; return '';
} };
////// 使用道具 ////// ////// 使用道具 //////
items.prototype.useItem = function (itemId, noRoute, callback) { items.prototype.useItem = function (itemId, noRoute, callback) {
@ -88,17 +83,16 @@ items.prototype.useItem = function (itemId, noRoute, callback) {
// 执行完毕 // 执行完毕
this._afterUseItem(itemId); this._afterUseItem(itemId);
// 记录路线 // 记录路线
if (!noRoute) core.status.route.push("item:" + itemId); if (!noRoute) core.status.route.push('item:' + itemId);
if (callback) callback(); if (callback) callback();
} };
items.prototype._useItemEffect = function (itemId) { items.prototype._useItemEffect = function (itemId) {
var useItemEffect = core.material.items[itemId].useItemEffect; var useItemEffect = core.material.items[itemId].useItemEffect;
if (useItemEffect) { if (useItemEffect) {
try { try {
eval(useItemEffect); eval(useItemEffect);
} } catch (e) {
catch (e) {
console.error(e); console.error(e);
} }
} }
@ -106,22 +100,20 @@ items.prototype._useItemEffect = function (itemId) {
if (useItemEvent) { if (useItemEvent) {
try { try {
core.insertAction(useItemEvent); core.insertAction(useItemEvent);
} } catch (e) {
catch (e) {
console.error(e); console.error(e);
} }
} }
} };
items.prototype._afterUseItem = function (itemId) { items.prototype._afterUseItem = function (itemId) {
// 道具使用完毕:删除 // 道具使用完毕:删除
var itemCls = core.material.items[itemId].cls; var itemCls = core.material.items[itemId].cls;
if (itemCls == 'tools') if (itemCls == 'tools') core.status.hero.items[itemCls][itemId]--;
core.status.hero.items[itemCls][itemId]--;
if (core.status.hero.items[itemCls][itemId] <= 0) if (core.status.hero.items[itemCls][itemId] <= 0)
delete core.status.hero.items[itemCls][itemId]; delete core.status.hero.items[itemCls][itemId];
core.updateStatusBar(false, true); core.updateStatusBar(false, true);
} };
////// 当前能否使用道具 ////// ////// 当前能否使用道具 //////
items.prototype.canUseItem = function (itemId) { items.prototype.canUseItem = function (itemId) {
@ -132,41 +124,40 @@ items.prototype.canUseItem = function (itemId) {
if (canUseItemEffect) { if (canUseItemEffect) {
try { try {
return eval(canUseItemEffect); return eval(canUseItemEffect);
} } catch (e) {
catch (e) {
console.error(e); console.error(e);
return false; return false;
} }
} }
} };
////// 获得某个物品的个数 ////// ////// 获得某个物品的个数 //////
items.prototype.itemCount = function (itemId) { items.prototype.itemCount = function (itemId) {
if (!core.material.items[itemId] || !core.isPlaying()) return 0; if (!core.material.items[itemId] || !core.isPlaying()) return 0;
var itemCls = core.material.items[itemId].cls; var itemCls = core.material.items[itemId].cls;
if (itemCls == "items") return 0; if (itemCls == 'items') return 0;
return core.status.hero.items[itemCls][itemId] || 0; return core.status.hero.items[itemCls][itemId] || 0;
} };
////// 是否存在某个物品 ////// ////// 是否存在某个物品 //////
items.prototype.hasItem = function (itemId) { items.prototype.hasItem = function (itemId) {
return this.itemCount(itemId) > 0; return this.itemCount(itemId) > 0;
} };
////// 是否装备某件装备 ////// ////// 是否装备某件装备 //////
items.prototype.hasEquip = function (itemId) { items.prototype.hasEquip = function (itemId) {
if (!(core.material.items[itemId] || {}).equip || !core.isPlaying()) return null; if (!(core.material.items[itemId] || {}).equip || !core.isPlaying())
return null;
for (var i in core.status.hero.equipment) for (var i in core.status.hero.equipment)
if (core.status.hero.equipment[i] == itemId) if (core.status.hero.equipment[i] == itemId) return true;
return true; return false;
return false };
}
////// 获得某个装备类型的当前装备 ////// ////// 获得某个装备类型的当前装备 //////
items.prototype.getEquip = function (equipType) { items.prototype.getEquip = function (equipType) {
return core.status.hero.equipment[equipType] || null; return core.status.hero.equipment[equipType] || null;
} };
////// 设置某个物品的个数 ////// ////// 设置某个物品的个数 //////
items.prototype.setItem = function (itemId, itemNum) { items.prototype.setItem = function (itemId, itemNum) {
@ -179,7 +170,7 @@ items.prototype.setItem = function (itemId, itemNum) {
delete core.status.hero.items[itemCls][itemId]; delete core.status.hero.items[itemCls][itemId];
} }
core.updateStatusBar(); core.updateStatusBar();
} };
////// 增加某个物品的个数 ////// ////// 增加某个物品的个数 //////
items.prototype.addItem = function (itemId, itemNum) { items.prototype.addItem = function (itemId, itemNum) {
@ -198,7 +189,7 @@ items.prototype.addItem = function (itemId, itemNum) {
if (itemCls == 'constants' && core.status.hero.items[itemCls][itemId] > 1) if (itemCls == 'constants' && core.status.hero.items[itemCls][itemId] > 1)
core.status.hero.items[itemCls][itemId] = 1; core.status.hero.items[itemCls][itemId] = 1;
core.updateStatusBar(); core.updateStatusBar();
} };
////// 删除某个物品 ////// ////// 删除某个物品 //////
items.prototype.removeItem = function (itemId, itemNum) { items.prototype.removeItem = function (itemId, itemNum) {
@ -211,7 +202,7 @@ items.prototype.removeItem = function (itemId, itemNum) {
} }
core.updateStatusBar(); core.updateStatusBar();
return true; return true;
} };
// ---------- 装备相关 ------------ // // ---------- 装备相关 ------------ //
@ -225,14 +216,13 @@ items.prototype.getEquipTypeByName = function (name) {
} }
} }
return types.length == 1 ? types[0] : -1; return types.length == 1 ? types[0] : -1;
} };
items.prototype.getEquipTypeById = function (equipId) { items.prototype.getEquipTypeById = function (equipId) {
var type = core.material.items[equipId].equip.type; var type = core.material.items[equipId].equip.type;
if (typeof type == 'string') if (typeof type == 'string') type = this.getEquipTypeByName(type);
type = this.getEquipTypeByName(type);
return type; return type;
} };
// 当前能否撞上某装备 // 当前能否撞上某装备
items.prototype.canEquip = function (equipId, hint) { items.prototype.canEquip = function (equipId, hint) {
@ -241,7 +231,7 @@ items.prototype.canEquip = function (equipId, hint) {
if (!equip.equip) { if (!equip.equip) {
if (hint) { if (hint) {
core.playSound('操作失败'); core.playSound('操作失败');
core.drawTip("不合法的装备!"); core.drawTip('不合法的装备!');
} }
return false; return false;
} }
@ -250,7 +240,7 @@ items.prototype.canEquip = function (equipId, hint) {
if (!core.hasItem(equipId) && !core.hasEquip(equipId)) { if (!core.hasItem(equipId) && !core.hasEquip(equipId)) {
if (hint) { if (hint) {
core.playSound('操作失败'); core.playSound('操作失败');
core.drawTip("你当前没有" + equip.name + ",无法换装"); core.drawTip('你当前没有' + equip.name + ',无法换装');
} }
return false; return false;
} }
@ -262,18 +252,17 @@ items.prototype.canEquip = function (equipId, hint) {
if (!eval(canUseItemEffect)) { if (!eval(canUseItemEffect)) {
if (hint) { if (hint) {
core.playSound('操作失败'); core.playSound('操作失败');
core.drawTip("当前不可换上" + equip.name); core.drawTip('当前不可换上' + equip.name);
} }
return false; return false;
} }
} } catch (e) {
catch (e) {
console.error(e); console.error(e);
return false; return false;
} }
} }
return true; return true;
} };
////// 换上 ////// ////// 换上 //////
items.prototype.loadEquip = function (equipId, callback) { items.prototype.loadEquip = function (equipId, callback) {
@ -286,13 +275,18 @@ items.prototype.loadEquip = function (equipId, callback) {
var type = this.getEquipTypeById(equipId); var type = this.getEquipTypeById(equipId);
if (type < 0) { if (type < 0) {
core.playSound('操作失败'); core.playSound('操作失败');
core.drawTip("当前没有" + loadEquip.equip.type + "的空位!"); core.drawTip('当前没有' + loadEquip.equip.type + '的空位!');
if (callback) callback(); if (callback) callback();
return; return;
} }
this._realLoadEquip(type, equipId, core.status.hero.equipment[type], callback); this._realLoadEquip(
} type,
equipId,
core.status.hero.equipment[type],
callback
);
};
////// 卸下 ////// ////// 卸下 //////
items.prototype.unloadEquip = function (equipType, callback) { items.prototype.unloadEquip = function (equipType, callback) {
@ -303,11 +297,15 @@ items.prototype.unloadEquip = function (equipType, callback) {
} }
this._realLoadEquip(equipType, null, unloadEquipId, callback); this._realLoadEquip(equipType, null, unloadEquipId, callback);
} };
items.prototype.compareEquipment = function (compareEquipId, beComparedEquipId) { items.prototype.compareEquipment = function (
var result = { "value": {}, "percentage": {} }; compareEquipId,
var first = core.material.items[compareEquipId], second = core.material.items[beComparedEquipId]; beComparedEquipId
) {
var result = { value: {}, percentage: {} };
var first = core.material.items[compareEquipId],
second = core.material.items[beComparedEquipId];
for (var one in result) { for (var one in result) {
for (var name in core.status.hero) { for (var name in core.status.hero) {
if (typeof core.status.hero[name] == 'number') { if (typeof core.status.hero[name] == 'number') {
@ -319,7 +317,7 @@ items.prototype.compareEquipment = function (compareEquipId, beComparedEquipId)
} }
} }
return result; return result;
} };
////// 实际换装的效果 ////// ////// 实际换装的效果 //////
items.prototype._loadEquipEffect = function (equipId, unloadEquipId) { items.prototype._loadEquipEffect = function (equipId, unloadEquipId) {
@ -329,12 +327,12 @@ items.prototype._loadEquipEffect = function (equipId, unloadEquipId) {
for (var name in result.percentage) for (var name in result.percentage)
core.addBuff(name, result.percentage[name] / 100); core.addBuff(name, result.percentage[name] / 100);
for (var name in result.value) for (var name in result.value) core.status.hero[name] += result.value[name];
core.status.hero[name] += result.value[name]; };
}
items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) { items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) {
var loadEquip = core.material.items[loadId] || {}, unloadEquip = core.material.items[unloadId] || {}; var loadEquip = core.material.items[loadId] || {},
unloadEquip = core.material.items[unloadId] || {};
// --- 音效 // --- 音效
this._realLoadEquip_playSound(); this._realLoadEquip_playSound();
@ -348,44 +346,43 @@ items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) {
core.status.hero.equipment[type] = loadId || null; core.status.hero.equipment[type] = loadId || null;
// --- 提示 // --- 提示
if (loadId) core.drawTip("已装备上" + loadEquip.name, loadId); if (loadId) core.drawTip('已装备上' + loadEquip.name, loadId);
else if (unloadId) core.drawTip("已卸下" + unloadEquip.name, unloadId); else if (unloadId) core.drawTip('已卸下' + unloadEquip.name, unloadId);
if (callback) callback(); if (callback) callback();
} };
items.prototype._realLoadEquip_playSound = function () { items.prototype._realLoadEquip_playSound = function () {
if (core.hasFlag("__quickLoadEquip__")) return; if (core.hasFlag('__quickLoadEquip__')) return;
core.stopSound(); core.stopSound();
core.playSound('穿脱装备'); core.playSound('穿脱装备');
} };
////// 保存装备 ////// ////// 保存装备 //////
items.prototype.quickSaveEquip = function (index) { items.prototype.quickSaveEquip = function (index) {
var saveEquips = core.getFlag("saveEquips", []); var saveEquips = core.getFlag('saveEquips', []);
saveEquips[index] = core.clone(core.status.hero.equipment); saveEquips[index] = core.clone(core.status.hero.equipment);
core.setFlag("saveEquips", saveEquips); core.setFlag('saveEquips', saveEquips);
core.status.route.push("saveEquip:" + index); core.status.route.push('saveEquip:' + index);
core.drawTip("已保存" + index + "号套装"); core.drawTip('已保存' + index + '号套装');
} };
////// 读取装备 ////// ////// 读取装备 //////
items.prototype.quickLoadEquip = function (index) { items.prototype.quickLoadEquip = function (index) {
var current = core.getFlag("saveEquips", [])[index]; var current = core.getFlag('saveEquips', [])[index];
if (!current) { if (!current) {
core.playSound('操作失败'); core.playSound('操作失败');
core.drawTip(index + "号套装不存在"); core.drawTip(index + '号套装不存在');
return; return;
} }
// 检查所有的装备 // 检查所有的装备
var equipSize = core.status.globalAttribute.equipName.length; var equipSize = core.status.globalAttribute.equipName.length;
for (var i = 0; i < equipSize; i++) { for (var i = 0; i < equipSize; i++) {
var v = current[i]; var v = current[i];
if (v && !this.canEquip(v, true)) if (v && !this.canEquip(v, true)) return;
return;
} }
core.status.route.push("loadEquip:" + index); core.status.route.push('loadEquip:' + index);
core.setFlag("__quickLoadEquip__", true); core.setFlag('__quickLoadEquip__', true);
// 快速换装 // 快速换装
var toEquip = []; var toEquip = [];
for (var i = 0; i < equipSize; i++) { for (var i = 0; i < equipSize; i++) {
@ -405,25 +402,39 @@ items.prototype.quickLoadEquip = function (index) {
this.loadEquip(to); this.loadEquip(to);
} }
} }
core.removeFlag("__quickLoadEquip__"); core.removeFlag('__quickLoadEquip__');
this._realLoadEquip_playSound(); this._realLoadEquip_playSound();
core.drawTip("成功换上" + index + "号套装"); core.drawTip('成功换上' + index + '号套装');
} };
////// 设置装备属性 ////// ////// 设置装备属性 //////
items.prototype.setEquip = function (equipId, valueType, name, value, operator, prefix) { items.prototype.setEquip = function (
equipId,
valueType,
name,
value,
operator,
prefix
) {
var equip = core.material.items[equipId]; var equip = core.material.items[equipId];
if (!equip || equip.cls != 'equips') return; if (!equip || equip.cls != 'equips') return;
var equipInfo = equip.equip || {}; var equipInfo = equip.equip || {};
if (!equipInfo[valueType]) equipInfo[valueType] = {}; if (!equipInfo[valueType]) equipInfo[valueType] = {};
var toEquipInfo = core.clone(equipInfo); var toEquipInfo = core.clone(equipInfo);
toEquipInfo[valueType][name] = core.events._updateValueByOperator(core.calValue(value, prefix), equipInfo[valueType][name], operator); toEquipInfo[valueType][name] = core.events._updateValueByOperator(
core.calValue(value, prefix),
equipInfo[valueType][name],
operator
);
// 如果是穿上状态,则还需要直接修改当前数值 // 如果是穿上状态,则还需要直接修改当前数值
if (core.hasEquip(equipId)) { if (core.hasEquip(equipId)) {
// 设置一个临时装备,然后模拟换装操作 // 设置一个临时装备,然后模拟换装操作
var tempId = 'temp:' + equipId; var tempId = 'temp:' + equipId;
core.material.items[tempId] = { 'cls': 'equips', 'equip': core.clone(toEquipInfo) }; core.material.items[tempId] = {
cls: 'equips',
equip: core.clone(toEquipInfo)
};
this._loadEquipEffect(tempId, equipId); this._loadEquipEffect(tempId, equipId);
delete core.material.items[tempId]; delete core.material.items[tempId];
core.updateStatusBar(); core.updateStatusBar();
@ -431,4 +442,4 @@ items.prototype.setEquip = function (equipId, valueType, name, value, operator,
equip.equip = core.clone(toEquipInfo); equip.equip = core.clone(toEquipInfo);
flags.equipInfo = flags.equipInfo || {}; flags.equipInfo = flags.equipInfo || {};
flags.equipInfo[equipId] = core.clone(toEquipInfo); flags.equipInfo[equipId] = core.clone(toEquipInfo);
} };

View File

@ -43,6 +43,8 @@ next();
let repStart; let repStart;
const listenedFloors = [];
// ----- GET file // ----- GET file
/** /**
@ -169,6 +171,11 @@ async function writeFile(req, res) {
const type = /^type=(utf8|base64)/.exec(data)[0].slice(5); const type = /^type=(utf8|base64)/.exec(data)[0].slice(5);
const value = /&value=[^]+/.exec(data)[0].slice(7); const value = /&value=[^]+/.exec(data)[0].slice(7);
await fs.writeFile(dir, value, { encoding: type }); await fs.writeFile(dir, value, { encoding: type });
testWatchFloor(name);
if (name.endsWith('project/events.js')) doDeclaration('events', value);
if (name.endsWith('project/items.js')) doDeclaration('items', value);
if (name.endsWith('project/maps.js')) doDeclaration('maps', value);
if (name.endsWith('project/data.js')) doDeclaration('data', value);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
res.end( res.end(
@ -226,9 +233,14 @@ async function writeMultiFiles(req, res) {
const tasks = names.map((v, i) => { const tasks = names.map((v, i) => {
try { try {
return new Promise(res => { return new Promise(res => {
fs.writeFile(path.resolve(__dirname, v), value[i]).then(v => fs.writeFile(
res(`write ${v} success.`) path.resolve(__dirname, v),
); value[i],
'base64' // 多文件是base64写入的
).then(v => {
testWatchFloor(v);
res(`write ${v} success.`);
});
}); });
} catch { } catch {
console.error(e); console.error(e);
@ -313,13 +325,11 @@ async function watch() {
}); });
}); });
// 楼层,热重载 // 楼层 热重载
fss.watch(path.resolve(__dirname, 'project/floors/'), (a, b) => { // 注意这里要逐个监听,并通过创建文件来监听文件改变
if (!/^\w+\.js$/.test(b)) return; const floors = await extract('project/floors/*.js');
const floorId = b.slice(0, -3); floors.forEach(v => {
if (hotReloadData.includes(`@@floor:${floorId}`)) return; watchOneFloor(v.slice(15));
hotReloadData += `@@floor:${floorId}`;
console.log(`floor hot reload: ${floorId}`);
}); });
// 脚本编辑 及 插件 热重载 // 脚本编辑 及 插件 热重载
@ -347,6 +357,35 @@ async function watch() {
}); });
} }
/**
* 检测是否是楼层文件并进行监听
* @param {string} url 要测试的路径
*/
function testWatchFloor(url) {
if (/project(\/|\\)floors(\/|\\).*\.js/.test(url)) {
const f = url.slice(15);
if (!listenedFloors.includes(f.slice(0, -3))) {
watchOneFloor(f);
}
}
}
/**
* 监听一个楼层文件
* @param {string} file 要监听的文件
*/
function watchOneFloor(file) {
if (!/.*\.js/.test(file)) return;
const f = file.slice(0, -3);
listenedFloors.push(file.slice(0, -3));
fss.watchFile(`project/floors/${file}`, { interval: 100 }, () => {
const floorId = f;
if (hotReloadData.includes(`@@floor:${floorId}`)) return;
hotReloadData += `@@floor:${floorId}`;
console.log(`floor hot reload: ${floorId}`);
});
}
/** /**
* 修改部分文件后重新加载及热重载 * 修改部分文件后重新加载及热重载
* @param {http.IncomingMessage} req * @param {http.IncomingMessage} req
@ -555,6 +594,96 @@ async function replaySave(req, res) {
res.end('success'); res.end('success');
} }
// ----- declaration
/**
* 声明某种类型
* @param {string} type 类型
* @param {string} data 信息
*/
async function doDeclaration(type, data) {
const buf = Buffer.from(data, 'base64');
data = buf.toString('utf-8');
if (type === 'events') {
// 事件
const eventData = JSON.parse(data.split('\n').slice(1).join(''));
let eventDec = 'type EventDeclaration = \n';
for (const id in eventData.commonEvent) {
eventDec += ` | '${id}'\n`;
}
await fs.writeFile('../src/source/events.d.ts', eventDec, 'utf-8');
} else if (type === 'items') {
// 道具
const itemData = JSON.parse(data.split('\n').slice(1).join(''));
let itemDec = 'interface ItemDeclaration {\n';
for (const id in itemData) {
itemDec += ` ${id}: '${itemData[id].cls}';\n`;
}
itemDec += '}';
await fs.writeFile('../src/source/items.d.ts', itemDec, 'utf-8');
} else if (type === 'maps') {
// 映射
const d = JSON.parse(data.split('\n').slice(1).join(''));
let id2num = 'interface IdToNumber {\n';
let num2id = 'interface NumberToId {\n';
let id2cls = 'interface IdToCls {\n';
for (const num in d) {
const { id, cls } = d[num];
id2num += ` ${id}: ${num};\n`;
num2id += ` ${num}: '${id}';\n`;
id2cls += ` ${id}: '${cls}';\n`;
}
id2cls += '}';
id2num += '}';
num2id += '}';
await fs.writeFile('../src/source/cls.d.ts', id2cls, 'utf-8');
await fs.writeFile(
'../src/source/maps.d.ts',
`${id2num}\n${num2id}`,
'utf-8'
);
} else if (type === 'data') {
// 全塔属性的注册信息
const d = JSON.parse(data.split('\n').slice(1).join('')).main;
let floorId = 'type FloorIds =\n';
let imgs = 'type ImageIds =\n';
let anis = 'type AnimationIds =\n';
let sounds = 'type SoundIds =\n';
let names = 'interface NameMap {\n';
let bgms = 'type BgmIds =\n';
let fonts = 'type FontIds =\n';
floorId += d.floorIds.map(v => ` | '${v}'\n`).join('');
imgs += d.images.map(v => ` | '${v}'\n`).join('');
anis += d.animates.map(v => ` | '${v}'\n`).join('');
sounds += d.sounds.map(v => ` | '${v}'\n`).join('');
bgms += d.bgms.map(v => ` | '${v}'\n`).join('');
fonts += d.fonts.map(v => ` | '${v}'\n`).join('');
for (const name in d.nameMap) {
names += ` '${name}': '${d.nameMap[name]}';\n`;
}
names += '}';
await fs.writeFile(
'../src/source/data.d.ts',
`
${floorId}
${d.images.length > 0 ? imgs : 'type ImageIds = never\n'}
${d.animates.length > 0 ? anis : 'type AnimationIds = never\n'}
${d.sounds.length > 0 ? sounds : 'type SoundIds = never\n'}
${d.bgms.length > 0 ? bgms : 'type BgmIds = never\n'}
${d.fonts.length > 0 ? fonts : 'type FontIds = never\n'}
${names}
`,
'utf-8'
);
}
}
// ----- server // ----- server
server.on('listening', () => { server.on('listening', () => {

85
script/declare.ts Normal file
View File

@ -0,0 +1,85 @@
import fs from 'fs/promises';
(async function () {
// 1. 声明道具
const item = await fs.readFile('./public/project/items.js', 'utf-8');
const itemData = JSON.parse(item.split('\n').slice(1).join('')) as any;
let itemDec = 'interface ItemDeclaration {\n';
for (const id in itemData) {
itemDec += ` ${id}: '${itemData[id].cls}';\n`;
}
itemDec += '}';
// 2. 声明公共事件
const event = await fs.readFile('./public/project/events.js', 'utf-8');
const eventData = JSON.parse(event.split('\n').slice(1).join('')) as any;
let eventDec = 'type EventDeclaration = \n';
for (const id in eventData.commonEvent) {
eventDec += ` | '${id}'\n`;
}
// 3. 声明数字id映射与cls映射
const map = await fs.readFile('./public/project/maps.js', 'utf-8');
const mapData = JSON.parse(map.split('\n').slice(1).join('')) as any;
let id2num = 'interface IdToNumber {\n';
let num2id = 'interface NumberToId {\n';
let id2cls = 'interface IdToCls {\n';
for (const num in mapData) {
const { id, cls } = mapData[num];
id2num += ` ${id}: ${num};\n`;
num2id += ` ${num}: '${id}';\n`;
id2cls += ` ${id}: '${cls}';\n`;
}
id2cls += '}';
id2num += '}';
num2id += '}';
// 4. 声明楼层id图片动画音效文件别名背景音乐字体
const data = await fs.readFile('./public/project/data.js', 'utf-8');
const d = (JSON.parse(data.split('\n').slice(1).join('')) as any).main;
let floorId = 'type FloorIds =\n';
let imgs = 'type ImageIds =\n';
let anis = 'type AnimationIds =\n';
let sounds = 'type SoundIds =\n';
let names = 'interface NameMap {\n';
let bgms = 'type BgmIds =\n';
let fonts = 'type FontIds =\n';
floorId += d.floorIds.map((v: string) => ` | '${v}'\n`).join('');
imgs += d.images.map((v: string) => ` | '${v}'\n`).join('');
anis += d.animates.map((v: string) => ` | '${v}'\n`).join('');
sounds += d.sounds.map((v: string) => ` | '${v}'\n`).join('');
bgms += d.bgms.map((v: string) => ` | '${v}'\n`).join('');
fonts += d.fonts.map((v: string) => ` | '${v}'\n`).join('');
for (const name in d.nameMap) {
names += ` '${name}': '${d.nameMap[name]}';\n`;
}
names += '}';
// 5. 写入文件
await fs.writeFile('./src/source/cls.d.ts', id2cls, 'utf-8');
await fs.writeFile('./src/source/events.d.ts', eventDec, 'utf-8');
await fs.writeFile('./src/source/items.d.ts', itemDec, 'utf-8');
await fs.writeFile(
'./src/source/maps.d.ts',
`${id2num}\n${num2id}`,
'utf-8'
);
await fs.writeFile(
'./src/source/data.d.ts',
`
${floorId}
${d.images.length > 0 ? imgs : 'type ImageIds = never\n'}
${d.animates.length > 0 ? anis : 'type AnimationIds = never\n'}
${d.sounds.length > 0 ? sounds : 'type SoundIds = never\n'}
${d.bgms.length > 0 ? bgms : 'type BgmIds = never\n'}
${d.fonts.length > 0 ? fonts : 'type FontIds = never\n'}
${names}
`,
'utf-8'
);
})();

View File

@ -75,7 +75,7 @@ export function getDefDamage(
if (!has(dam)) continue; if (!has(dam)) continue;
if (dam.damage === origin) continue; if (dam.damage === origin) continue;
if (dam.damage === res.at(-1)?.[1]) continue; if (dam.damage === res.at(-1)?.[1]) continue;
if (last <= 0) break; if (last === dam.damage) break;
last = dam.damage; last = dam.damage;
res.push([ratio * i + addDef, dam.damage]); res.push([ratio * i + addDef, dam.damage]);
} }
@ -121,7 +121,7 @@ export function getCriticalDamage(
if (!has(dam)) continue; if (!has(dam)) continue;
if (dam.damage === origin) continue; if (dam.damage === origin) continue;
if (dam.damage === res.at(-1)?.[1]) continue; if (dam.damage === res.at(-1)?.[1]) continue;
if (last <= 0) break; if (last === dam.damage) break;
last = dam.damage; last = dam.damage;
res.push([ratio * i + addAtk, dam.damage]); res.push([ratio * i + addAtk, dam.damage]);
} }

View File

@ -3,8 +3,11 @@ import { has } from '../utils';
/** /**
* *
*/ */
export function getEquips() { export function getEquips(): [ItemIdOf<'equips'>, number][] {
return Object.entries(core.status.hero.items.equips); return Object.entries(core.status.hero.items.equips) as [
ItemIdOf<'equips'>,
number
][];
} }
/** /**
@ -13,7 +16,7 @@ export function getEquips() {
*/ */
export function getAddStatus(equip: Equip) { export function getAddStatus(equip: Equip) {
const toGet = Object.assign({}, equip.value, equip.percentage); const toGet = Object.assign({}, equip.value, equip.percentage);
const keys = Object.keys(toGet); const keys = Object.keys(toGet) as (keyof typeof toGet)[];
return ( return (
<div class="equip-add-detail"> <div class="equip-add-detail">
@ -46,15 +49,23 @@ export function getAddStatus(equip: Equip) {
* *
* @param nowEquip * @param nowEquip
*/ */
export function getNowStatus(nowEquip?: Equip) { export function getNowStatus(nowEquip?: Equip, onCol: boolean = false) {
const toShow = ['hp', 'lv', 'atk', 'def', 'mdef', 'mana', 'hpmax', 'money']; const toShow = [
'hp',
'lv',
'atk',
'def',
'mdef',
'mana',
'hpmax',
'money'
] as (keyof SelectType<HeroStatus, number>)[];
return ( return (
<div id="hero-status"> <div id="hero-status">
{toShow.map(v => { {toShow.map(v => {
let status: string; let status: string;
if (v === 'up') status = core.getNextLvUpNeed()?.toString(); if (v === 'lv') status = core.getLvName() ?? '';
else if (v === 'lv') status = core.getLvName() ?? '';
else status = core.getRealStatus(v)?.toString(); else status = core.getRealStatus(v)?.toString();
let add = 0; let add = 0;
@ -63,6 +74,7 @@ export function getNowStatus(nowEquip?: Equip) {
const per = nowEquip.percentage[v] * core.getStatus(v); const per = nowEquip.percentage[v] * core.getStatus(v);
add += isNaN(per) ? 0 : per; add += isNaN(per) ? 0 : per;
} }
if (onCol) add = -add;
return ( return (
<div class="hero-status-one"> <div class="hero-status-one">

View File

@ -7,8 +7,8 @@ interface ClsMap {
} }
interface AllItem { interface AllItem {
tools: [string, number][]; tools: [ItemIdOf<'tools'>, number][];
constants: [string, number][]; constants: [ItemIdOf<'constants'>, number][];
} }
const clsMap: ClsMap = { const clsMap: ClsMap = {

View File

@ -27,7 +27,7 @@ export function has<T>(value: T): value is NonNullable<T> {
* *
* @param damage * @param damage
*/ */
export function getDamageColor(damage: number): string { export function getDamageColor(damage: number): Color {
if (typeof damage !== 'number') return '#f00'; if (typeof damage !== 'number') return '#f00';
if (damage === 0) return '#2f2'; if (damage === 0) return '#2f2';
if (damage < 0) return '#7f7'; if (damage < 0) return '#7f7';

516
src/source/cls.d.ts vendored Normal file
View File

@ -0,0 +1,516 @@
interface IdToCls {
yellowWall: 'animates';
whiteWall: 'animates';
blueWall: 'animates';
star: 'animates';
lava: 'animates';
ice: 'terrains';
blueShopLeft: 'terrains';
blueShopRight: 'terrains';
pinkShopLeft: 'terrains';
pinkShopRight: 'terrains';
lavaNet: 'animates';
poisonNet: 'animates';
weakNet: 'animates';
curseNet: 'animates';
blueWater: 'animates';
water: 'animates';
autotile: 'autotile';
yellowKey: 'items';
blueKey: 'items';
redKey: 'items';
greenKey: 'items';
steelKey: 'items';
bigKey: 'items';
redGem: 'items';
blueGem: 'items';
greenGem: 'items';
yellowGem: 'items';
redPotion: 'items';
bluePotion: 'items';
greenPotion: 'items';
yellowPotion: 'items';
sword1: 'items';
shield1: 'items';
sword2: 'items';
shield2: 'items';
sword3: 'items';
shield3: 'items';
sword4: 'items';
shield4: 'items';
sword5: 'items';
shield5: 'items';
book: 'items';
fly: 'items';
pickaxe: 'items';
icePickaxe: 'items';
bomb: 'items';
centerFly: 'items';
upFly: 'items';
downFly: 'items';
coin: 'items';
freezeBadge: 'items';
cross: 'items';
superPotion: 'items';
earthquake: 'items';
poisonWine: 'items';
weakWine: 'items';
curseWine: 'items';
superWine: 'items';
dagger: 'items';
silverCoin: 'items';
amulet: 'items';
hammer: 'items';
lifeWand: 'items';
jumpShoes: 'items';
sword0: 'items';
shield0: 'items';
skill1: 'items';
wand: 'items';
yellowDoor: 'animates';
blueDoor: 'animates';
redDoor: 'animates';
greenDoor: 'animates';
specialDoor: 'animates';
steelDoor: 'animates';
upFloor: 'terrains';
downFloor: 'terrains';
portal: 'animates';
starPortal: 'animates';
upPortal: 'animates';
leftPortal: 'animates';
downPortal: 'animates';
rightPortal: 'animates';
crystalUp: 'animates';
crystalBottom: 'animates';
fire: 'animates';
switch: 'animates';
steelDoor2: 'animates';
steelDoor3: 'animates';
iceDoor: 'animates';
iceDoor2: 'animates';
magentaWall: 'animates';
man: 'npcs';
trader: 'npcs';
thief: 'npcs';
fairy: 'npcs';
wizard: 'npcs';
recluse: 'npcs';
king: 'npcs';
youngMan: 'npcs';
sign: 'npcs';
expShop: 'npcs';
moneyShop: 'npcs';
princess: 'npcs';
npc0: 'npc48';
npc1: 'npc48';
npc2: 'npc48';
npc3: 'npc48';
greenMan: 'npcs';
blueTrader: 'npcs';
redMSNpc: 'npcs';
blackTrader: 'npcs';
autotile4: 'autotile';
autotile5: 'autotile';
autotile6: 'autotile';
autotile7: 'autotile';
autotile1: 'autotile';
autotile2: 'autotile';
autotile3: 'autotile';
arrowUp: 'terrains';
arrowDown: 'terrains';
arrowLeft: 'terrains';
arrowRight: 'terrains';
light: 'terrains';
darkLight: 'terrains';
ski: 'terrains';
flower: 'terrains';
box: 'terrains';
boxed: 'terrains';
octopusLeftTop: 'npcs';
octopusTop: 'npcs';
octopusRightTop: 'npcs';
octopusLeft: 'npcs';
octopusCenter: 'npcs';
octopusRight: 'npcs';
octopusLeftBottom: 'npcs';
octopusRightBottom: 'npcs';
dragonLeftTop: 'npcs';
dragonTop: 'npcs';
dragonRightTop: 'npcs';
dragonLeft: 'npcs';
dragonCenter: 'npcs';
dragonRight: 'npcs';
dragonLeftBottom: 'npcs';
dragonRightBottom: 'npcs';
greenSlime: 'enemys';
redSlime: 'enemys';
blackSlime: 'enemys';
slimelord: 'enemys';
bat: 'enemys';
bigBat: 'enemys';
redBat: 'enemys';
vampire: 'enemys';
skeleton: 'enemys';
skeletonWarrior: 'enemys';
skeletonCaptain: 'enemys';
ghostSoldier: 'enemys';
zombie: 'enemys';
zombieKnight: 'enemys';
rock: 'enemys';
slimeman: 'enemys';
bluePriest: 'enemys';
redPriest: 'enemys';
brownWizard: 'enemys';
redWizard: 'enemys';
yellowGateKeeper: 'enemys';
blueGateKeeper: 'enemys';
redGateKeeper: 'enemys';
swordsman: 'enemys';
soldier: 'enemys';
yellowKnight: 'enemys';
redKnight: 'enemys';
darkKnight: 'enemys';
blackKing: 'enemys';
yellowKing: 'enemys';
greenKing: 'enemys';
blueKnight: 'enemys';
goldSlime: 'enemys';
poisonSkeleton: 'enemys';
poisonBat: 'enemys';
ironRock: 'enemys';
skeletonPriest: 'enemys';
skeletonKing: 'enemys';
skeletonPresbyter: 'enemys';
skeletonKnight: 'enemys';
evilHero: 'enemys';
devilWarrior: 'enemys';
demonPriest: 'enemys';
goldHornSlime: 'enemys';
redKing: 'enemys';
blueKing: 'enemys';
magicMaster: 'enemys';
silverSlime: 'enemys';
blademaster: 'enemys';
whiteHornSlime: 'enemys';
evilPrincess: 'enemys';
evilFairy: 'enemys';
yellowPriest: 'enemys';
redSwordsman: 'enemys';
whiteSlimeman: 'enemys';
poisonZombie: 'enemys';
dragon: 'enemys';
octopus: 'enemys';
fairyEnemy: 'enemys';
princessEnemy: 'enemys';
angel: 'enemy48';
elemental: 'enemy48';
steelGuard: 'enemy48';
evilBat: 'enemy48';
silverSlimelord: 'enemys';
goldSlimelord: 'enemys';
grayRock: 'enemys';
blueRock: 'enemys';
skeletonLite: 'enemys';
greenKnight: 'enemys';
bowman: 'enemys';
liteBowman: 'enemys';
crimsonZombie: 'enemys';
frozenSkeleton: 'enemys';
watcherSlime: 'enemys';
mutantSlimeman: 'enemys';
frostBat: 'enemys';
devilKnight: 'enemys';
grayPriest: 'enemys';
greenGateKeeper: 'enemys';
ground: 'terrains';
sWallT: 'terrains';
sWallL: 'terrains';
sWallR: 'terrains';
sWallB: 'terrains';
grass: 'terrains';
sWallTL: 'terrains';
sWallBR: 'terrains';
grass2: 'terrains';
sWallTR: 'terrains';
sWallBL: 'terrains';
ground2: 'terrains';
sWallTB: 'terrains';
ground3: 'terrains';
sWallLR: 'terrains';
sWallBLR: 'terrains';
sWallTLR: 'terrains';
sWallTBR: 'terrains';
sWallTBL: 'terrains';
I319: 'items';
I320: 'items';
I321: 'items';
I322: 'items';
I323: 'items';
I324: 'items';
I325: 'items';
I326: 'items';
I327: 'items';
I328: 'items';
I329: 'items';
I330: 'items';
T331: 'terrains';
T332: 'terrains';
T333: 'terrains';
T334: 'terrains';
T335: 'terrains';
T336: 'terrains';
T337: 'terrains';
T338: 'terrains';
T339: 'terrains';
T340: 'terrains';
T341: 'terrains';
T342: 'terrains';
T343: 'terrains';
T344: 'terrains';
T345: 'terrains';
T346: 'terrains';
T347: 'terrains';
T348: 'terrains';
T349: 'terrains';
T350: 'terrains';
T351: 'terrains';
T352: 'terrains';
T353: 'terrains';
T354: 'terrains';
T355: 'terrains';
T356: 'terrains';
T357: 'terrains';
T358: 'terrains';
T359: 'terrains';
T360: 'terrains';
T361: 'terrains';
T362: 'terrains';
T363: 'terrains';
T364: 'terrains';
T365: 'terrains';
T366: 'terrains';
N367: 'npc48';
E368: 'enemys';
E369: 'enemys';
E370: 'enemys';
E371: 'enemys';
E372: 'enemys';
E373: 'enemys';
E374: 'enemys';
E375: 'enemys';
I376: 'items';
I377: 'items';
I378: 'items';
I379: 'items';
I380: 'items';
I381: 'items';
I382: 'items';
I383: 'items';
I384: 'items';
I385: 'items';
I386: 'items';
I387: 'items';
I388: 'items';
I389: 'items';
I390: 'items';
I391: 'items';
I392: 'items';
I393: 'items';
I394: 'items';
I395: 'items';
I396: 'items';
I397: 'items';
I398: 'items';
I399: 'items';
I400: 'items';
I401: 'items';
I402: 'items';
I403: 'items';
I404: 'items';
I405: 'items';
I406: 'items';
I407: 'items';
I408: 'items';
I409: 'items';
I410: 'items';
I411: 'items';
I412: 'items';
I413: 'items';
I414: 'items';
I415: 'items';
I416: 'items';
I417: 'items';
I418: 'items';
I419: 'items';
I420: 'items';
I421: 'items';
I422: 'items';
I423: 'items';
I424: 'items';
I425: 'items';
I426: 'items';
I427: 'items';
I428: 'items';
I429: 'items';
I430: 'items';
I431: 'items';
I432: 'items';
I433: 'items';
I434: 'items';
I435: 'items';
I436: 'items';
I437: 'items';
I438: 'items';
I439: 'items';
I440: 'items';
I441: 'items';
I442: 'items';
I443: 'items';
I444: 'items';
I445: 'items';
I446: 'items';
I447: 'items';
I448: 'items';
I449: 'items';
I450: 'items';
I451: 'items';
I452: 'items';
I453: 'items';
I454: 'items';
I455: 'items';
I456: 'items';
I457: 'items';
I458: 'items';
I459: 'items';
I460: 'items';
I461: 'items';
I462: 'items';
I463: 'items';
I464: 'items';
I465: 'items';
I466: 'items';
I467: 'items';
I468: 'items';
I469: 'items';
I470: 'items';
I471: 'items';
I472: 'items';
I473: 'items';
I474: 'items';
I475: 'items';
I476: 'items';
I477: 'items';
I478: 'items';
I479: 'items';
I480: 'items';
I481: 'items';
I482: 'items';
I483: 'items';
I484: 'items';
I485: 'items';
I486: 'items';
I487: 'items';
I488: 'items';
I489: 'items';
I490: 'items';
I491: 'items';
A492: 'animates';
A493: 'animates';
A494: 'animates';
A495: 'animates';
A496: 'animates';
A497: 'animates';
E498: 'enemys';
E499: 'enemys';
E500: 'enemys';
E501: 'enemys';
E502: 'enemys';
E503: 'enemys';
E504: 'enemys';
E505: 'enemys';
A506: 'animates';
A507: 'animates';
A508: 'animates';
A509: 'animates';
A510: 'animates';
E511: 'enemys';
E512: 'enemys';
E513: 'enemys';
E514: 'enemys';
E515: 'enemys';
T516: 'terrains';
E517: 'enemys';
E518: 'enemys';
E519: 'enemys';
E520: 'enemys';
E521: 'enemys';
E522: 'enemys';
E523: 'enemys';
E524: 'enemys';
E525: 'enemys';
T526: 'terrains';
T527: 'terrains';
T528: 'terrains';
T529: 'terrains';
T530: 'terrains';
T531: 'terrains';
N532: 'npcs';
N533: 'npcs';
N534: 'npcs';
N535: 'npcs';
E536: 'enemys';
E537: 'enemys';
E538: 'enemys';
E539: 'enemys';
A540: 'animates';
A541: 'animates';
A542: 'animates';
A543: 'animates';
E544: 'enemys';
E545: 'enemys';
E546: 'enemys';
E547: 'enemys';
E548: 'enemys';
E549: 'enemys';
E550: 'enemys';
T551: 'terrains';
T552: 'terrains';
T553: 'terrains';
T554: 'terrains';
T555: 'terrains';
E556: 'enemys';
E557: 'enemys';
I558: 'items';
X20037: 'tileset';
X20038: 'tileset';
X20039: 'tileset';
X20045: 'tileset';
X20047: 'tileset';
X20053: 'tileset';
X20054: 'tileset';
X20055: 'tileset';
X20056: 'tileset';
X20057: 'tileset';
X20058: 'tileset';
X20064: 'tileset';
X20065: 'tileset';
X20066: 'tileset';
X20074: 'tileset';
X20152: 'tileset';
X20153: 'tileset';
X30040: 'tileset';
X30041: 'tileset';
X30042: 'tileset';
X30048: 'tileset';
X30050: 'tileset';
X30056: 'tileset';
X30057: 'tileset';
X30058: 'tileset';
X30105: 'tileset';
X30112: 'tileset';
X30113: 'tileset';
X30121: 'tileset';
X30196: 'tileset';
X30204: 'tileset';
}

178
src/source/data.d.ts vendored Normal file
View File

@ -0,0 +1,178 @@
type FloorIds =
| 'MT0'
| 'MT1'
| 'MT2'
| 'MT3'
| 'MT4'
| 'MT5'
| 'MT6'
| 'MT7'
| 'MT8'
| 'MT9'
| 'MT10'
| 'MT11'
| 'MT12'
| 'MT13'
| 'MT14'
| 'MT15'
| 'MT16'
| 'MT17'
| 'MT18'
| 'MT19'
| 'MT20'
| 'MT21'
| 'tower1'
| 'tower2'
| 'tower3'
| 'tower4'
| 'tower5'
| 'tower6'
| 'tower7'
type ImageIds =
| 'IQ.png'
| 'arrow.png'
| 'atk.png'
| 'beforeBoss.jpg'
| 'bg.jpg'
| 'boom.png'
| 'botton.png'
| 'cave.jpg'
| 'cave1.jpg'
| 'cave2.jpg'
| 'def.png'
| 'escape.jpg'
| 'exp.png'
| 'grass.jpg'
| 'hero1.png'
| 'hero2.png'
| 'hp.png'
| 'money.png'
| 'mount.jpg'
| 'plot1.jpg'
| 'skill0.png'
| 'skill1.png'
| 'skill2.png'
| 'skill3.png'
| 'skill4.png'
| 'skill5.png'
| 'skill6.png'
| 'skill7.png'
| 'title.jpg'
| 'tower.jpg'
| 'tower6.jpeg'
| 'tower7.jpeg'
| 'towerBoss.jpg'
| 'towerBoss2.jpg'
| 'towerBoss3.jpg'
| 'winskin.png'
| 'winskin2.png'
| 'winskin3.png'
type AnimationIds =
| 'amazed'
| 'angry'
| 'angry2'
| 'bulb'
| 'emm'
| 'explosion1'
| 'explosion2'
| 'explosion3'
| 'explosion4'
| 'fire'
| 'focus'
| 'fret'
| 'hand'
| 'ice'
| 'jianji'
| 'luv'
| 'magicAtk'
| 'stone'
| 'sweat'
| 'sweat2'
| 'sword'
| 'zone'
type SoundIds =
| '008-System08.ogg'
| '015-Jump01.ogg'
| '050-Explosion03.ogg'
| '051-Explosion04.ogg'
| '087-Action02.ogg'
| '094-Attack06.ogg'
| '118-Fire02.ogg'
| '119-Fire03.ogg'
| '120-Ice01.ogg'
| 'arrow.mp3'
| 'attack.mp3'
| 'bomb.mp3'
| 'cancel.mp3'
| 'centerFly.mp3'
| 'chapter.mp3'
| 'confirm.mp3'
| 'cursor.mp3'
| 'danger.mp3'
| 'door.mp3'
| 'electron.mp3'
| 'equip.mp3'
| 'error.mp3'
| 'floor.mp3'
| 'item.mp3'
| 'jump.mp3'
| 'load.mp3'
| 'open_ui.mp3'
| 'paper.mp3'
| 'pickaxe.mp3'
| 'quake.mp3'
| 'recovery.mp3'
| 'save.mp3'
| 'shake.mp3'
| 'shop.mp3'
| 'thunder.mp3'
| 'tree.mp3'
| 'zone.mp3'
type BgmIds =
| 'beforeBoss.mp3'
| 'cave.mp3'
| 'escape.mp3'
| 'grass.mp3'
| 'mount.mp3'
| 'plot1.mp3'
| 'title.mp3'
| 'tower.mp3'
| 'towerBoss.mp3'
| 'towerBoss2.mp3'
| 'towerBoss3.mp3'
type FontIds =
| 'normal'
| 'scroll'
interface NameMap {
'确定': 'confirm.mp3';
'取消': 'cancel.mp3';
'操作失败': 'error.mp3';
'光标移动': 'cursor.mp3';
'打开界面': 'open_ui.mp3';
'读档': 'load.mp3';
'存档': 'save.mp3';
'获得道具': 'item.mp3';
'回血': 'recovery.mp3';
'炸弹': 'bomb.mp3';
'飞行器': 'centerFly.mp3';
'开关门': 'door.mp3';
'上下楼': 'floor.mp3';
'跳跃': 'jump.mp3';
'破墙镐': 'pickaxe.mp3';
'破冰镐': 'icePickaxe.mp3';
'宝石': 'gem.mp3';
'阻激夹域': 'zone.mp3';
'穿脱装备': 'equip.mp3';
'背景音乐': 'bgm.mp3';
'攻击': 'attack.mp3';
'背景图': 'bg.jpg';
'商店': 'shop.mp3';
'领域': 'zone';
}

3
src/source/events.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
type EventDeclaration =
| '加点事件'
| '回收钥匙商店'

182
src/source/items.d.ts vendored Normal file
View File

@ -0,0 +1,182 @@
interface ItemDeclaration {
yellowKey: 'tools';
blueKey: 'tools';
redKey: 'tools';
redGem: 'items';
blueGem: 'items';
greenGem: 'items';
yellowGem: 'items';
redPotion: 'items';
bluePotion: 'items';
yellowPotion: 'items';
greenPotion: 'items';
sword0: 'items';
sword1: 'equips';
sword2: 'items';
sword3: 'items';
sword4: 'items';
sword5: 'items';
shield0: 'items';
shield1: 'equips';
shield2: 'items';
shield3: 'items';
shield4: 'items';
shield5: 'items';
superPotion: 'items';
silverCoin: 'items';
book: 'constants';
fly: 'constants';
coin: 'constants';
freezeBadge: 'constants';
cross: 'constants';
dagger: 'constants';
amulet: 'constants';
bigKey: 'tools';
greenKey: 'tools';
steelKey: 'tools';
pickaxe: 'tools';
icePickaxe: 'tools';
bomb: 'tools';
centerFly: 'constants';
upFly: 'tools';
downFly: 'tools';
earthquake: 'tools';
poisonWine: 'tools';
weakWine: 'tools';
curseWine: 'tools';
superWine: 'tools';
hammer: 'tools';
lifeWand: 'tools';
jumpShoes: 'tools';
skill1: 'constants';
wand: 'constants';
I319: 'items';
I320: 'items';
I321: 'items';
I322: 'constants';
I323: 'items';
I324: 'items';
I325: 'items';
I326: 'items';
I327: 'items';
I328: 'items';
I329: 'items';
I330: 'constants';
I376: 'items';
I377: 'items';
I378: 'items';
I379: 'items';
I380: 'items';
I381: 'items';
I382: 'items';
I383: 'items';
I384: 'items';
I385: 'items';
I386: 'items';
I387: 'items';
I388: 'items';
I389: 'items';
I390: 'items';
I391: 'items';
I392: 'items';
I393: 'items';
I394: 'items';
I395: 'items';
I396: 'items';
I397: 'items';
I398: 'items';
I399: 'items';
I400: 'items';
I401: 'items';
I402: 'items';
I403: 'items';
I404: 'items';
I405: 'items';
I406: 'items';
I407: 'items';
I408: 'items';
I409: 'items';
I410: 'items';
I411: 'items';
I412: 'items';
I413: 'items';
I414: 'items';
I415: 'items';
I416: 'items';
I417: 'items';
I418: 'items';
I419: 'items';
I420: 'items';
I421: 'items';
I422: 'items';
I423: 'items';
I424: 'items';
I425: 'items';
I426: 'items';
I427: 'items';
I428: 'items';
I429: 'items';
I430: 'items';
I431: 'items';
I432: 'items';
I433: 'items';
I434: 'items';
I435: 'items';
I436: 'items';
I437: 'items';
I438: 'items';
I439: 'items';
I440: 'items';
I441: 'items';
I442: 'items';
I443: 'items';
I444: 'items';
I445: 'items';
I446: 'items';
I447: 'items';
I448: 'items';
I449: 'items';
I450: 'items';
I451: 'items';
I452: 'items';
I453: 'items';
I454: 'items';
I455: 'items';
I456: 'items';
I457: 'items';
I458: 'items';
I459: 'items';
I460: 'items';
I461: 'items';
I462: 'items';
I463: 'items';
I464: 'items';
I465: 'items';
I466: 'items';
I467: 'items';
I468: 'items';
I469: 'items';
I470: 'items';
I471: 'items';
I472: 'items';
I473: 'items';
I474: 'items';
I475: 'items';
I476: 'items';
I477: 'items';
I478: 'items';
I479: 'items';
I480: 'items';
I481: 'items';
I482: 'items';
I483: 'items';
I484: 'items';
I485: 'items';
I486: 'items';
I487: 'items';
I488: 'items';
I489: 'items';
I490: 'items';
I491: 'items';
I558: 'constants';
}

1032
src/source/maps.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
font-size: 1.6em; font-size: 1.6em;
} }
// 文字按钮
.button-text { .button-text {
cursor: pointer; cursor: pointer;
transition: color 0.2s linear; transition: color 0.2s linear;
@ -32,6 +33,10 @@
color: aquamarine; color: aquamarine;
} }
.selectable {
border: #0000 0.5px solid;
}
.selectable[selected='true'] { .selectable[selected='true'] {
animation: selected alternate 5s infinite ease-in-out; animation: selected alternate 5s infinite ease-in-out;
} }
@ -50,3 +55,17 @@
background-color: rgba(39, 251, 209, 0.143); background-color: rgba(39, 251, 209, 0.143);
} }
} }
// 拖拽类
.draginable {
transition: background-color 0.2s linear;
}
.draginable[draged='true'][access='true']:hover {
background-color: rgba(0, 255, 187, 0.31);
}
.draginable[draged='true'][access='false']:hover {
background-color: rgba(255, 0, 0, 0.319);
cursor: not-allowed;
}

189
src/types/action.d.ts vendored
View File

@ -1,74 +1,137 @@
/** @file actions.js 定义了玩家的操作控制 */ /**
declare class actions { *
*/
type MotaMouseFunc = (x: number, y: number, px: number, py: number) => boolean;
/**
*
*/
type MotaKeyboardFunc = (e: KeyboardEvent) => boolean;
/**
*
*/
interface RegisteredActionMap {
keyDown: (keyCode: number) => boolean;
keyDownCtrl: () => boolean;
keyUp: (keyCode: number, altKey: boolean, fromReplay: boolean) => boolean;
longClick: MotaMouseFunc;
onStatusBarClick: (px: number, py: number, vertical: boolean) => boolean;
ondown: MotaMouseFunc;
onkeyDown: MotaKeyboardFunc;
onkeyUp: MotaKeyboardFunc;
onmousewheel: (direct: 1 | -1) => boolean;
onmove: MotaMouseFunc;
onup: MotaMouseFunc;
pressKey: (keyCode: number) => boolean;
}
type ActionKey = keyof RegisteredActionMap;
/**
* void就变成了actions上的函数...
*/
type VoidedActionFuncs = {
[P in ActionKey]: (...params: Parameters<RegisteredActionMap[P]>) => void;
};
/**
*
*/
interface ClickLoc extends Loc {
/**
* 32
*/
size: 32;
}
interface RegisteredActionOf<K extends ActionKey> {
/**
*
*/
action: K;
/**
*
*/
name: string;
/**
*
*/
priority: number;
/**
*
*/
func: RegisteredActionMap[K];
}
/**
*
*/
interface Actions extends VoidedActionFuncs {
/**
*
*/
readonly LAST: number;
/**
*
*/
readonly _HX_: number;
/**
*
*/
readonly _HY_: number;
/**
*
*/
readonly actionsdata: ActionData;
/**
*
*/
readonly actions: {
[P in ActionKey]: RegisteredActionOf<P>[];
};
/** /**
* *
* @param action ondown, onclick, keyDown * @param action
* @param name 使 * @param name 使
* @param func * @param func func返回true
* func返回true * @param priority 0
* @param priority 0
*/ */
registerAction( registerAction<K extends ActionKey>(
action: string, action: K,
name: string, name: string,
func: string | ((...params: any) => void), func: RegisteredActionMap[K],
priority?: number priority?: number
): void; ): void;
/** 注销一个用户交互行为 */ /**
unregisterAction(action: string, name: string): void; *
* @param action
* @param name
*/
unregisterAction(action: ActionKey, name: string): void;
/** 执行一个用户交互行为 */ /**
doRegisteredAction(action: string, ...params: any): void; *
*/
/** 按下某个键时 */ doRegisteredAction<K extends ActionKey>(
onkeyDown(e: KeyboardEvent): void; action: K,
...params: Parameters<RegisteredActionMap[K]>
/** 放开某个键时 */
onkeyUp(e: KeyboardEvent): void;
/** 按住某个键时 */
pressKey(keyCode: number): void;
/** 根据按下键的code来执行一系列操作 */
keyDown(keyCode: number): void;
/** 根据放开键的code来执行一系列操作 */
keyUp(keyCode: number, altKey?: boolean, fromReplay?: boolean): void;
/** 点击(触摸)事件按下时 */
ondown(loc: number[]): void;
/** 当在触摸屏上滑动时 */
onmove(loc: number[]): void;
/** 当点击(触摸)事件放开时 */
onup(loc: number[]): void;
/** 具体点击屏幕上(x,y)点时,执行的操作 */
onclick(
x: number,
y: number,
px: number,
py: number,
stepPostfix?: any
): void; ): void;
/** 滑动鼠标滚轮时的操作 */ /**
onmousewheel(direct: 1 | -1): void; * (_HX_ - 2, _HX_ + 2)
* @param x
/** 长按Ctrl键时 */ */
keyDownCtrl(): void; _out(x: number): boolean;
/** 长按 */
longClick(
x: number,
y: number,
px: number,
py: number,
fromEvent?: boolean
): void;
/** 点击自绘状态栏时 */
onStatusBarClick(e?: MouseEvent): void;
} }
declare const actions: new () => Actions;

1179
src/types/control.d.ts vendored

File diff suppressed because it is too large Load Diff

1715
src/types/core.d.ts vendored

File diff suppressed because it is too large Load Diff

140
src/types/data.d.ts vendored Normal file
View File

@ -0,0 +1,140 @@
interface MainData {
/**
* id
*/
readonly floorIds: FloorIds[];
/**
*
*/
readonly floorPatitions: [FloorIds, FloorIds?][];
/**
*
*/
readonly tilesets: string[];
/**
*
*/
readonly animates: AnimationIds[];
/**
* bgm
*/
readonly bgms: BgmIds[];
/**
*
*/
readonly sounds: SoundIds[];
/**
*
*/
readonly fonts: FontIds[];
/**
*
*/
readonly nameMap: NameMap;
/**
*
*/
readonly levelChoose: LevelChooseEvent[];
/**
*
*/
readonly equipName: string[];
/**
* bgm
*/
readonly startBgm: BgmIds;
/**
*
*/
readonly styles: MainStyle;
/**
*
*/
readonly splitImages: SplitImageData;
}
interface FirstData {
/**
*
*/
title: string;
/**
* mota.config.ts中的一致
*/
name: string;
/**
*
*/
version: string;
/**
*
*/
floorId: FloorIds;
/**
*
*/
hero: HeroStatus;
/**
*
*/
startCanvas: MotaEvent;
/**
*
*/
startText: MotaEvent;
/**
*
*/
shops: ShopEventOf<keyof ShopEventMap>[];
/**
*
*/
levelUp: LevelUpEvent;
}
/**
*
*/
interface DataCore {
/**
* main信息
*/
readonly main: MainData;
/**
*
*/
readonly firstData: FirstData;
/**
*
*/
readonly values: CoreValues;
/**
*
*/
readonly flags: CoreFlags;
}
declare const data: new () => Omit<DataCore, 'main'>;

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

@ -1,16 +1,242 @@
/** @file enemys.js 定义了一系列和敌人相关的API函数。 */ type PartialNumbericEnemyProperty =
declare class enemys { | 'value'
| 'zone'
| 'repulse'
| 'laser'
| 'breakArmor'
| 'counterAttack'
| 'vampire'
| 'hpBuff'
| 'atkBuff'
| 'defBuff'
| 'range'
| 'haloRange'
| 'n'
| 'purify'
| 'atkValue'
| 'defValue'
| 'damage';
type BooleanEnemyProperty =
| 'zoneSquare'
| 'haloSquare'
| 'notBomb'
| 'add'
| 'haloAdd';
type DetailedEnemy<I extends EnemyIds = EnemyIds> = {
specialText: string[];
toShowSpecial: string[];
toShowColor: Color[];
specialColor: Color[];
damageColor: Color;
} & Enemy<I>;
type Enemy<I extends EnemyIds = EnemyIds> = {
/** /**
* * id
* @example core.hasSpecial('greenSlime', 1) // 判定绿头怪有无先攻属性
* @param special id或敌人对象或正整数数组或自然数
* @param test
* @returns special为数组或数且含有test或相等special为敌人id或对象且具有此属性true
*/ */
hasSpecial( id: I;
special: number | number[] | string | Enemy,
test: number /**
): boolean; *
*/
name: string;
/**
* IDnullID来替换该怪物原本的ID
*
*/
displayIdInBook: EnemyIds;
/**
*
*/
beforeBattle: MotaEvent;
/**
*
*/
afterBattle: MotaEvent;
} & {
[P in PartialNumbericEnemyProperty]?: number;
} & {
[P in BooleanEnemyProperty]: boolean;
} & EnemyInfoBase;
/**
*
*/
type EnemySpecialDeclaration = [
id: number,
name: string | ((enemy: Enemy) => string),
desc: string | ((enemy: Enemy) => string),
color: Color,
extra?: number
];
interface DamageString {
/**
*
*/
damage: string;
/**
*
*/
color: Color;
}
interface EnemyInfoBase {
/**
*
*/
hp: number;
/**
*
*/
atk: number;
/**
*
*/
def: number;
/**
*
*/
money: number;
/**
*
*/
exp: number;
/**
*
*/
point: number;
/**
*
*/
special: number[];
}
interface EnemyInfo extends EnemyInfoBase {
/**
*
*/
guards: [x: number, y: number, id: EnemyIds];
}
interface DamageInfo {
/**
*
*/
mon_hp: number;
/**
*
*/
mon_atk: number;
/**
*
*/
mon_def: number;
/**
*
*/
init_damage: number;
/**
*
*/
per_damage: number;
/**
*
*/
hero_per_damage: number;
/**
*
*/
turn: number;
/**
*
*/
damage: number;
}
interface BookEnemyInfo extends Enemy, EnemyInfo {
/**
*
*/
locs?: [x: number, y: number][];
/**
*
*/
name: string;
/**
*
*/
specialText: string[];
/**
*
*/
specialColor: Color[];
/**
*
*/
damage: number;
/**
*
*/
critical: number;
/**
*
*/
criticalDamage: number;
/**
* ratio防减伤
*/
defDamage: number;
}
/**
*
*/
interface Enemys extends EnemyData {
/**
*
*/
readonly enemys: {
[P in EnemyIds]: Enemy<P>;
};
/**
*
*/
readonly enemydata: EnemyData;
/**
*
*/
getEnemys(): {
[P in EnemyIds]: Enemy<P>;
};
/** /**
* *
@ -18,120 +244,135 @@ declare class enemys {
* @param enemy id或敌人对象core.material.enemys.greenSlime * @param enemy id或敌人对象core.material.enemys.greenSlime
* @returns * @returns
*/ */
getSpecialText(enemy: string | Enemy): string[]; getSpecialText(enemy: EnemyIds | Enemy): string[];
/**
*
* @param enemy id或敌人对象core.material.enemys.greenSlime
*/
getSpecialColor(enemy: EnemyIds | Enemy): Color[];
/**
*
* @param enemy id或敌人对象core.material.enemys.greenSlime
*/
getSpecialFlag(enemy: EnemyIds | Enemy): number[];
/** /**
* *
* @example core.getSpecialHint('bat', 1) // '先攻:怪物首先攻击' * @example core.getSpecialHint('bat', 1) // '先攻:怪物首先攻击'
* @param enemy id或敌人对象 * @param enemy id或敌人对象
* @param special * @param special
* @returns * @returns
*/ */
getSpecialHint(enemy: string | Enemy, special: number): string; getSpecialHint(enemy: EnemyIds | Enemy, special: number): string;
/** 获得某个敌人的某项属性值 */ /**
getEnemyValue( *
enemy: string | Enemy, * @param enemy id或敌人对象
name: string, * @param name
* @param x
* @param y
* @param floorId
*/
getEnemyValue<K extends keyof Enemy>(
enemy: EnemyIds | Enemy,
name: K,
x?: number, x?: number,
y?: number, y?: number,
floorId?: string floorId?: FloorIds
): any; ): Enemy[K];
/** /**
* *
* @example core.canBattle('greenSlime',0,0,'MT0') // 能否打败主塔0层左上角的绿头怪假设有 * @example core.canBattle('greenSlime',0,0,'MT0') // 能否打败主塔0层左上角的绿头怪假设有
* @param enemy id或敌人对象 * @param enemy id或敌人对象
* @param x * @param x
* @param y * @param y
* @param floorId * @param floorId
* @returns true表示可以打败false表示无法打败 * @returns true表示可以打败false表示无法打败
*/ */
canBattle( canBattle(
enemy: string | Enemy, enemy: EnemyIds | Enemy,
x?: number, x?: number,
y?: number, y?: number,
floorId?: string floorId?: FloorIds
): boolean; ): boolean;
/**
*
* @example core.getDamage('greenSlime',0,0,'MT0') // 绿头怪的总伤害
* @param enemy id或敌人对象
* @param x
* @param y
* @param floorId
* @returns null
*/
getDamage(
enemy: string | Enemy,
x?: number,
y?: number,
floorId?: string
): number;
/** /**
* *
* @example core.getDamageString('greenSlime', 0, 0, 'MT0') // 绿头怪的地图显伤 * @example core.getDamageString('greenSlime', 0, 0, 'MT0') // 绿头怪的地图显伤
* @param enemy id或敌人对象 * @param enemy id或敌人对象
* @param x * @param x
* @param y * @param y
* @param floorId * @param floorId
* @returns damage: 表示伤害值或为'???'color: 形如'#RrGgBb'
*/ */
getDamageString( getDamageString(
enemy: string | Enemy, enemy: EnemyIds | Enemy,
x?: number, x?: number,
y?: number, y?: number,
floorId?: string floorId?: FloorIds
): { ): DamageString;
damage: string;
color: string;
};
/** /**
* useLoop开关选择回合法或二分法 * useLoop开关选择回合法或二分法
* @example core.nextCriticals('greenSlime', 9, 0, 0, 'MT0') // 绿头怪接下来的9个临界 * @example core.nextCriticals('greenSlime', 9, 0, 0, 'MT0') // 绿头怪接下来的9个临界
* @param enemy id或敌人对象 * @param enemy id或敌人对象
* @param number 1 * @param number 1
* @param x * @param x
* @param y * @param y
* @param floorId * @param floorId
* @returns * @returns
*/ */
nextCriticals( nextCriticals(
enemy: string | Enemy, enemy: EnemyIds | Enemy,
number?: number, number?: number,
x?: number, x?: number,
y?: number, y?: number,
floorId?: string floorId?: FloorIds
): [atk: number, dam: number][]; ): [critical: number, damage: number][];
/** /**
* 使 * 使
* @example core.nextCriticals('greenSlime', 10, 0, 0, 'MT0') // 再加10点防御能使绿头怪的伤害降低多少 * @example core.nextCriticals('greenSlime', 10, 0, 0, 'MT0') // 再加10点防御能使绿头怪的伤害降低多少
* @param enemy id或敌人对象 * @param enemy id或敌人对象
* @param k 1 * @param k 1
* @param x * @param x
* @param y * @param y
* @param floorId * @param floorId
* @returns * @returns
*/ */
getDefDamage( getDefDamage(
enemy: string | Enemy, enemy: EnemyIds | Enemy,
k?: number, k?: number,
x?: number, x?: number,
y?: number, y?: number,
floorId?: string floorId?: FloorIds
): number;
/**
*
* @example core.getDamage('greenSlime',0,0,'MT0') // 绿头怪的总伤害
* @param enemy id或敌人对象
* @param x
* @param y
* @param floorId
* @returns null
*/
getDamage(
enemy: EnemyIds | Enemy,
x?: number,
y?: number,
floorId?: FloorIds
): number; ): number;
/** /**
* *
* @example core.getCurrentEnemys('MT0') // 主塔0层的敌人集合 * @example core.getCurrentEnemys('MT0') // 主塔0层的敌人集合
* @param floorId id * @param floorId id
* @returns * @returns
*/ */
getCurrentEnemys(floorId?: string): (Enemy & DetailedEnemy)[]; getCurrentEnemys(floorId?: FloorIds): DetailedEnemy[];
/** /**
* *
@ -140,63 +381,10 @@ declare class enemys {
* @param floorId id或其数组 * @param floorId id或其数组
* @returns true表示有敌人被漏打false表示敌人已死光 * @returns true表示有敌人被漏打false表示敌人已死光
*/ */
hasEnemyLeft(enemyId?: string, floorId?: string | string[]): boolean; hasEnemyLeft(
enemyId?: EnemyIds | EnemyIds[],
/** 获得所有怪物原始数据的一个副本 */ floorId?: FloorIds | FloorIds[]
getEnemys(): any; ): boolean;
/** 获得所有特殊属性定义 */
getSpecials(): [
number,
string | ((enemy: Enemy) => string),
string | ((enemy: Enemy) => string),
string | [number, number, number, number?],
number?
][];
/** 获得所有特殊属性的颜色 */
getSpecialColor(
enemy: string | Enemy
): Array<string | [number, number, number, number?]>;
/** 获得所有特殊属性的额外标记 */
getSpecialFlag(enemy: string | Enemy): Array<number>;
/** 获得怪物真实属性 */
getEnemyInfo(
enemy: string | Enemy,
hero?: any,
x?: number,
y?: number,
floorId?: string
): {
hp: number;
def: number;
atk: number;
money: number;
exp: number;
point: number;
special: number | number[];
guards: Array<[number, number, string]>;
[x: string]: any;
};
/** 获得战斗伤害信息(实际伤害计算函数) */
getDamageInfo(
enemy: string | Enemy,
hero?: any,
x?: number,
y?: number,
floorId?: string
): {
mon_hp: number;
mon_atk: number;
mon_def: number;
init_damage: number;
per_damage: number;
hero_per_damage: number;
turn: number;
damage: number;
[x: string]: any;
} | null;
} }
declare const enemys: new () => Enemys;

803
src/types/event.d.ts vendored

File diff suppressed because it is too large Load Diff

313
src/types/eventDec.d.ts vendored Normal file
View File

@ -0,0 +1,313 @@
type MotaAction = any;
type MotaEvent = any[];
/**
*
*/
type ShopEventOf<T extends keyof ShopEventMap> = ShopEventMap[T];
interface ShopEventMap {
/**
*
*/
common: CommonShopEvent;
/**
*
*/
item: ItemShopEvent;
/**
*
*/
event: CommonEventShopEvent;
}
interface ShopEventBase {
/**
* id
*/
id: string;
/**
*
*/
textInList: string;
/**
*
*/
mustEnable: boolean;
/**
*
*/
disablePreview: boolean;
}
/**
*
*/
interface CommonShopChoice {
/**
*
*/
text: string;
/**
*
*/
need: string;
/**
*
*/
icon: AllIds;
/**
*
*/
color: Color;
/**
*
*/
action: MotaEvent;
}
/**
*
*/
interface CommonShopEvent extends ShopEventBase {
/**
*
*/
text: string;
/**
*
*/
choices: CommonShopChoice[];
}
/**
*
*/
interface ItemShopChoice {
/**
* id
*/
id: AllIdsOf<'items'>;
/**
*
*/
number: number;
/**
* ${}
*/
money: string;
/**
*
*/
sell: string;
/**
*
*/
condition: string;
}
/**
*
*/
interface ItemShopEvent extends ShopEventBase {
/**
*
*/
item: true;
/**
* 西
*/
use: 'money' | 'exp';
/**
*
*/
choices: ItemShopChoice[];
}
interface CommonEventShopEvent {
/**
* 使
*/
commonEvent: EventDeclaration;
}
interface AutoEventBase {
/**
*
*/
condition: string;
/**
*
*/
currentFloor: boolean;
/**
*
*/
priority: number;
/**
*
*/
delayExecute: boolean;
/**
*
*/
multiExecute: boolean;
/**
*
*/
data: MotaEvent;
}
interface AutoEvent extends AutoEventBase {
/**
* id
*/
floorId: FloorIds;
/**
*
*/
index: string;
/**
*
*/
x: number;
/**
*
*/
y: number;
/**
*
*/
symbol: string;
}
interface LevelChooseEvent {
/**
*
*/
title: string;
/**
*
*/
name: string;
/**
* hard值
*/
hard: number;
/**
*
*/
color: RGBArray;
/**
*
*/
action: MotaEvent;
}
interface LevelUpEvent {
/**
*
*/
need: number;
/**
*
*/
title: string;
/**
*
*/
action: MotaEvent;
}
/**
*
*/
interface DoorInfo {
/**
*
*/
time: number;
/**
*
*/
openSound: SoundIds;
/**
*
*/
closeSound: SoundIds;
/**
*
*/
keys: Partial<Record<ItemIdOf<'tools'> | `${ItemIdOf<'tools'>}:o`, number>>;
/**
*
*/
afterOpenDoor?: MotaEvent;
}
interface ChangeFloorEvent {
/**
*
*/
floorId: ':before' | ':after' | ':now' | FloorIds;
/**
* stair就无效了
*/
loc?: LocArr;
/**
*
*/
stair?: FloorChangeStair;
/**
*
*/
direction?: HeroTurnDir;
/**
*
*/
time?: number;
/**
* 穿
*/
ignoreChangeFloor?: boolean;
}

456
src/types/eventStatus.d.ts vendored Normal file
View File

@ -0,0 +1,456 @@
interface EventStatusDataMap {
/**
*
*/
action: ActionStatusData;
/**
*
*/
book: number;
/**
*
*/
fly: number;
/**
*
*/
viewMaps: ViewMapStatusData;
/**
*
*/
equipbox: EquipboxStatusData;
/**
*
*/
toolbox: ToolboxStatusData;
/**
*
*/
save: SaveStatusData;
load: SaveStatusData;
replayLoad: SaveStatusData;
replayRemain: SaveStatusData;
replaySince: SaveStatusData;
/**
*
*/
text: TextStatusData;
/**
*
*/
confirmBox: ConfirmStatusData;
/**
*
* 西
*/
about: null;
help: null;
'book-detail': null;
keyBoard: null;
switchs: null;
'switchs-sounds': null;
'switchs-display': null;
'switchs-action': null;
settings: null;
selectShop: null;
notes: null;
syncSave: null;
syncSelect: null;
localSaveSelect: null;
storageRemove: null;
cursor: null;
replay: null;
gameInfo: null;
}
interface _EventStatusSelectionMap {
/**
*
*/
action: number;
/**
*
*/
equipbox: number;
/**
*
*/
toolbox: number;
/**
*
*/
save: boolean;
load: boolean;
/**
* (0)(1)
*/
confirmBox: 0 | 1;
/**
*
*/
switchs: number;
'switchs-sounds': number;
'switchs-display': number;
'switchs-action': number;
settings: number;
notes: number;
syncSave: number;
syncSelect: number;
localSaveSelect: number;
storageRemove: number;
replay: number;
gameInfo: number;
}
interface _EventStatusUiMap {
/**
*
*/
action: ActionStatusUi;
/**
* id
*/
book: number;
/**
*
*/
confirmBox: string;
/**
*
*/
'switchs-display': SwitchsStatusData;
/**
*
*/
settings: SwitchsStatusData;
/**
*
*/
selectShop: SelectShopStatusUi;
notes: SelectShopStatusUi;
syncSave: SelectShopStatusUi;
syncSelect: SelectShopStatusUi;
localSaveSelect: SelectShopStatusUi;
storageRemove: SelectShopStatusUi;
gameInfo: SelectShopStatusUi;
}
interface _EventStatusIntervalMap {
/**
* (?
*/
action: ActionStatusData;
/**
* 退
*/
book: ActionStatusData;
/**
* 退
*/
save: ActionStatusData;
load: ActionStatusData;
}
interface _EventStatusTimeoutMap {
/**
*
*/
action: number;
}
interface _EventStatusAnimateUiMap {
/**
*
*/
action: number;
}
type EventStatus = keyof EventStatusDataMap;
type _FillEventStatus<T> = {
[P in EventStatus]: P extends keyof T ? T[P] : null;
};
type EventStatusSelectionMap = _FillEventStatus<_EventStatusSelectionMap>;
type EventStatusUiMap = _FillEventStatus<_EventStatusUiMap>;
type EventStatusIntervalMap = _FillEventStatus<_EventStatusIntervalMap>;
type EventStatusTimeoutMap = _FillEventStatus<_EventStatusTimeoutMap>;
type EventStatusAnimateUiMap = _FillEventStatus<_EventStatusAnimateUiMap>;
/**
*
*/
interface EventStatusOf<T extends EventStatus = EventStatus> {
/**
*
*/
id: T;
/**
*
*/
data: EventStatusDataMap[T];
/**
*
*/
selection: EventStatusSelectionMap[T];
/**
* ui信息
*/
ui: EventStatusUiMap[T];
/**
*
*/
interval: EventStatusIntervalMap[T];
/**
*
*/
timeout: EventStatusTimeoutMap[T];
/**
*
*/
animateUi: EventStatusAnimateUiMap[T];
}
interface ActionStatusEventList {
/**
*
*/
todo: MotaEvent;
/**
*
*/
total: MotaEvent;
/**
*
*/
contidion: string;
}
interface ActionLocStackInfo {
/**
*
*/
x: number;
/**
*
*/
y: number;
/**
* id
*/
floorId: FloorIds;
}
/**
*
*/
interface ActionStatusData {
/**
*
*/
list: DeepReadonly<ActionStatusEventList>;
/**
*
*/
x?: number;
/**
*
*/
y?: number;
/**
*
*/
callback?: () => void;
/**
*
*/
appendingEvents: MotaEvent[];
/**
*
*/
locStack: any[];
/**
*
*/
type: EventType;
/**
*
*/
current: MotaAction;
}
interface ActionStatusUi {
/**
*
*/
text: string;
/**
*
*/
yes?: MotaEvent;
/**
*
*/
no?: MotaEvent;
/**
*
*/
choices?: string[];
/**
*
*/
width?: number;
}
interface ViewMapStatusData {
/**
*
*/
index: number;
/**
*
*/
damage: boolean;
/**
*
*/
all: boolean;
/**
*
*/
x: number;
/**
*
*/
y: number;
}
interface EquipboxStatusData {
/**
*
*/
page: number;
/**
*
*/
selectId: ItemIdOf<'equips'>;
}
interface ToolboxStatusData {
/**
*
*/
toolsPage: number;
/**
*
*/
constantsPage: number;
/**
* id
*/
selectId: ItemIdOf<'constants' | 'tools'>;
}
interface SaveStatusData {
/**
*
*/
page: number;
/**
*
*/
offset: number;
/**
* fav表示收藏all表示所有存档
*/
mode: 'fav' | 'all';
}
interface TextStatusData {
/**
*
*/
list: string[];
/**
*
*/
callback: () => void;
}
interface ConfirmStatusData {
/**
*
*/
yes: () => void;
/**
*
*/
no: () => void;
}
interface SwitchsStatusData {
/**
*
*/
choices: string[];
}
interface SelectShopStatusUi {
/**
*
*/
offset: number;
}

282
src/types/function.d.ts vendored Normal file
View File

@ -0,0 +1,282 @@
interface ActionData {
/**
*
* @param keyCode keyCode
* @param altKey alt键
*/
onKeyUp(keyCode: number, altKey: boolean): boolean;
/**
*
* @param px
* @param py
* @param vertical
*/
onClickStatusBar(px: number, py: number, vertical: boolean): boolean;
}
interface ControlData {
/**
*
*/
saveData(): Save;
/**
*
* @param data
* @param callback
*/
loadData(data: Save, callback?: () => void): void;
/**
*
* @param name
*/
getStatusLabel(name: string): string;
/**
* debuff
* @param action get表示获得debuffremove表示移除debuff
* @param type debuff列表
*/
triggerDebuff(action: 'get' | 'remove', type: string | string[]): void;
/**
*
*/
updateStatusBar(): void;
/**
*
* @param floorId id
*/
updateCheckBlock(floorId: FloorIds): void;
/**
*
* @param callback
*/
moveOneStep(callback?: () => void): void;
/**
*
* @param x
* @param y
* @param ignoreSteps
*/
moveDirectly(x: number, y: number, ignoreSteps?: number): boolean;
/**
*
* @param time
*/
parallelDo(time: number): void;
}
interface EnemyData {
/**
*
*/
getSpecials(): EnemySpecialDeclaration[];
/**
*
* @param enemy id或敌人对象
* @param hero core.status.hero获取
* @param x
* @param y
* @param floorId
*/
getEnemyInfo(
enemy: EnemyIds | Enemy,
hero?: HeroStatus,
x?: number,
y?: number,
floorId?: FloorIds
): EnemyInfo;
/**
*
* @param enemy id或敌人对象
* @param hero core.status.hero获取
* @param x
* @param y
* @param floorId
*/
getDamageInfo(
enemy: EnemyIds | Enemy,
hero?: Partial<HeroStatus>,
x?: number,
y?: number,
floorId?: FloorIds
): DamageInfo;
/**
*
* @example core.hasSpecial('greenSlime', 1) // 判定绿头怪有无先攻属性
* @param special id或敌人对象或正整数数组或自然数
* @param test
* @returns special为数组或数且含有test或相等special为敌人id或对象且具有此属性true
*/
hasSpecial(
special: number | number[] | EnemyIds | Enemy,
test: number
): boolean;
}
interface UiData {
/**
*
* @param cls
*/
getToolboxItems<T extends Exclude<ItemCls, 'items'>>(cls: T): ItemIdOf<T>[];
/**
*
*/
drawStatusBar(): void;
/**
*
*/
drawStatistics(): AllIdsOf<'items'>[];
/**
*
*/
drawAbout(): void;
}
interface EventData {
/**
*
* @param hero
* @param hard
* @param floorId
* @param maps
* @param values
*/
resetGame(
hero: HeroStatus,
hard: string,
floorId: FloorIds,
maps: GameStatus['maps'],
values: Partial<CoreValues>
): void;
/**
*
* @param reason
* @param norank
* @param noexit 退
*/
win(reason: string, norank?: boolean, noexit?: boolean): void;
/**
*
* @param reason
*/
lose(reason: string): void;
/**
*
* @param floorId
* @param heroLoc
*/
changingFloor(floorId: FloorIds, heroLoc: Loc): void;
/**
*
* @param floorId
*/
afterChangeFloor(floorId: FloorIds): void;
/**
*
* @param toId
* @param callback
*/
flyTo(toId: FloorIds, callback: () => void): void;
/**
*
* @param enemyId
* @param x
* @param y
*/
beforeBattle(
enemyId: AllIdsOf<'enemys' | 'enemy48'>,
x?: number,
y?: number
): void;
/**
*
* @param enemyId
* @param x
* @param y
*/
afterBattle(
enemyId: AllIdsOf<'enemys' | 'enemy48'>,
x?: number,
y?: number
): void;
/**
*
* @param doorId id
* @param x
* @param y
*/
afterOpenDoor(
doorId: AllIdsOf<Exclude<Cls, 'enemys' | 'enemy48'>>,
x: number,
y: number
): void;
/**
*
* @param itemId id
* @param x
* @param y
* @param isGentleClick
*/
afterGetItem(
itemId: AllIdsOf<'items'>,
x: number,
y: number,
isGentleClick?: boolean
): void;
/**
*
*/
afterPushBox(): void;
}
interface FunctionsData {
/**
*
*/
actions: ActionData;
/**
*
*/
control: ControlData;
/**
*
*/
enemys: EnemyData;
/**
* ui信息
*/
ui: UiData;
/**
*
*/
events: EventData;
}

81
src/types/icon.d.ts vendored
View File

@ -1,20 +1,71 @@
/** 和图标相关的函数 */ type IconIds =
declare class icons { | keyof MaterialIcon['animates']
/** 获得所有图标类型 */ | keyof MaterialIcon['autotile']
getIcons(): void; | keyof MaterialIcon['enemy48']
| keyof MaterialIcon['enemys']
| keyof MaterialIcon['hero']
| keyof MaterialIcon['items']
| keyof MaterialIcon['items']
| keyof MaterialIcon['npc48']
| keyof MaterialIcon['npcs']
| keyof MaterialIcon['terrains'];
/** 根据ID获得其类型 */ interface IconOffsetInfo {
getClsFromId(id?: string): BlockCls; /**
* id
/** 获得所有图标的ID */ */
getAllIconIds(): void; image: string;
/** 根据图块数字或ID获得所在的tileset和坐标信息 */
getTilesetOffset(id?: string): void;
/** /**
* *
* @param cls
*/ */
getAnimateFrames(cls: BlockCls): 1 | 2 | 4; x: number;
/**
*
*/
y: number;
} }
/**
*
*/
interface Icons {
/**
*
*/
readonly icons: MaterialIcon;
/**
*
*/
readonly tilesetStartOffset: 10000;
/**
* id
*/
readonly allIconIds: IconIds;
/**
*
*/
getIcons(): MaterialIcon;
/**
* ID获得图块类型
*/
getClsFromId<T extends AllIds>(id: T): ClsOf<T>;
/**
* ID
*/
getAllIconIds(): IconIds;
/**
* ID获得所在的tileset和坐标信息
* @param id id
*/
getTilesetOffset(id: string | number): IconOffsetInfo | null;
}
declare const icons: new () => Icons;

206
src/types/item.d.ts vendored
View File

@ -1,12 +1,117 @@
/** @file items.js 主要负责一切和道具相关的内容。 */ interface Item<I extends AllIdsOf<'items'>> {
declare class items {
/** /**
* * id
*/
id: I;
/**
*
*/
cls: ItemClsOf<I>;
/**
*
*/
name: string;
/**
*
*/
text?: string;
/**
*
*/
hideInToolBox: boolean;
/**
*
*/
equip: ItemClsOf<I> extends 'equips' ? Equip : never;
/**
* 使使
*/
hideInReplay: boolean;
/**
*
*/
itemEffect?: string;
/**
*
*/
itemEffectTip?: string;
/**
* 使
*/
useItemEvent?: MotaEvent;
/**
* 使
*/
useItemEffect?: string;
/**
* 使
*/
canUseItemEffect?: string | boolean;
}
interface EquipBase {
/**
*
*/
value: Record<keyof SelectType<HeroStatus, number>, number>;
/**
*
*/
percentage: Record<keyof SelectType<HeroStatus, number>, number>;
}
interface Equip extends EquipBase {
/**
*
*/
type: number | string;
/**
*
*/
animate: AnimationIds;
/**
* 穿
*/
equipEvent?: MotaEvent;
/**
*
*/
unequipEvent?: MotaEvent;
}
/**
*
*/
interface Items {
/**
*
*/
getItems(): {
[P in AllIdsOf<'items'>]: Item<P>;
};
/**
*
* @example core.getItemEffect('redPotion', 10) // 执行获得10瓶红血的效果 * @example core.getItemEffect('redPotion', 10) // 执行获得10瓶红血的效果
* @param itemId id * @param itemId id
* @param itemNum 1 * @param itemNum 1
*/ */
getItemEffect(itemId: string, itemNum?: number): void; getItemEffect(itemId: AllIdsOf<'items'>, itemNum?: number): void;
/** /**
* *
@ -14,16 +119,20 @@ declare class items {
* @param itemId id * @param itemId id
* @returns itemEffectTip的内容 * @returns itemEffectTip的内容
*/ */
getItemEffectTip(itemId: string): string; getItemEffectTip(itemId: AllIdsOf<'items'>): string;
/** /**
* 使 * 使
* @example core.useItem('pickaxe', true) // 使用破墙镐,不计入录像,无回调 * @example core.useItem('pickaxe', true) // 使用破墙镐,不计入录像,无回调
* @param itemId id * @param itemId id
* @param noRoute 使true * @param noRoute 使true
* @param callback 使使 * @param callback 使使
*/ */
useItem(itemId: string, noRoute?: boolean, callback?: () => void): void; useItem(
itemId: ItemIdOf<'tools' | 'constants'>,
noRoute?: boolean,
callback?: () => void
): void;
/** /**
* 使 * 使
@ -31,7 +140,7 @@ declare class items {
* @param itemId id * @param itemId id
* @returns true表示可以使用 * @returns true表示可以使用
*/ */
canUseItem(itemId: string): boolean; canUseItem(itemId: AllIdsOf<'items'>): boolean;
/** /**
* *
@ -39,7 +148,7 @@ declare class items {
* @param itemId id * @param itemId id
* @returns 穿 * @returns 穿
*/ */
itemCount(itemId: string): number; itemCount(itemId: AllIdsOf<'items'>): number;
/** /**
* (穿) * (穿)
@ -47,7 +156,7 @@ declare class items {
* @param itemId id * @param itemId id
* @returns true表示持有 * @returns true表示持有
*/ */
hasItem(itemId: string): boolean; hasItem(itemId: AllIdsOf<'items'>): boolean;
/** /**
* 穿 * 穿
@ -55,7 +164,7 @@ declare class items {
* @param itemId id * @param itemId id
* @returns true表示已装备 * @returns true表示已装备
*/ */
hasEquip(itemId: string): boolean; hasEquip(itemId: ItemIdOf<'equips'>): boolean;
/** /**
* *
@ -63,7 +172,7 @@ declare class items {
* @param equipType * @param equipType
* @returns idnull表示未穿戴 * @returns idnull表示未穿戴
*/ */
getEquip(equipType: number): string; getEquip(equipType: number): ItemIdOf<'equips'> | null;
/** /**
* *
@ -71,15 +180,29 @@ declare class items {
* @param itemId id * @param itemId id
* @param itemNum 0 * @param itemNum 0
*/ */
setItem(itemId: string, itemNum?: number): void; setItem(itemId: AllIdsOf<'items'>, itemNum?: number): void;
/** /**
* *
* @example core.addItem('yellowKey', -2) // 没收两把黄钥匙 * @example core.addItem('yellowKey', -2) // 没收两把黄钥匙
* @param itemId id * @param itemId id
* @param itemNum * @param itemNum
*/ */
addItem(itemId: string, itemNum?: number): void; addItem(itemId: AllIdsOf<'items'>, itemNum?: number): void;
/**
* @deprecated 使addItem代替
* addItem(itemId, -n);
* @param itemId id
* @param itemNum
*/
removeItem(itemId?: AllIdsOf<'items'>, itemNum?: number): void;
/**
*
* @param equipId
*/
getEquipTypeByName(name?: ItemIdOf<'equips'>): number;
/** /**
* *
@ -87,7 +210,7 @@ declare class items {
* @param equipId id * @param equipId id
* @returns * @returns
*/ */
getEquipTypeById(equipId: string): number | string; getEquipTypeById(equipId: ItemIdOf<'equips'>): number;
/** /**
* 穿 * 穿
@ -96,7 +219,7 @@ declare class items {
* @param hint 穿 * @param hint 穿
* @returns true表示可以穿上false表示无法穿上 * @returns true表示可以穿上false表示无法穿上
*/ */
canEquip(equipId: string, hint: boolean): boolean; canEquip(equipId: ItemIdOf<'equips'>, hint?: boolean): boolean;
/** /**
* 穿 * 穿
@ -104,7 +227,7 @@ declare class items {
* @param equipId id * @param equipId id
* @param callback 穿 * @param callback 穿
*/ */
loadEquip(equipId: string, callback?: () => void): void; loadEquip(equipId: ItemIdOf<'equips'>, callback?: () => void): void;
/** /**
* *
@ -121,10 +244,10 @@ declare class items {
* @param beComparedEquipId id * @param beComparedEquipId id
* @returns 0 * @returns 0
*/ */
compareEquipment( compareEquipment<F extends ItemIdOf<'equips'>>(
compareEquipId: string, compareEquipId: F,
beComparedEquipId: string beComparedEquipId: Exclude<ItemIdOf<'equips'>, F>
): { [key: string]: number }; ): EquipBase;
/** /**
* *
@ -140,15 +263,6 @@ declare class items {
*/ */
quickLoadEquip(index: number): void; quickLoadEquip(index: number): void;
/** 获得所有道具 */
getItems(): void;
/** 删除某个物品 */
removeItem(itemId?: string, itemNum?: number): void;
/** 根据类型获得一个可用的装备孔 */
getEquipTypeByName(name?: string): void;
/** /**
* *
* @example core.setEquip('sword1', 'value', 'atk', 300, '+='); // 设置铁剑的攻击力数值再加300 * @example core.setEquip('sword1', 'value', 'atk', 300, '+='); // 设置铁剑的攻击力数值再加300
@ -156,15 +270,31 @@ declare class items {
* @param valueType valuepercentage * @param valueType valuepercentage
* @param name atk * @param name atk
* @param value * @param value
* @param operator += * @param operator +=
* @param prefix * @param prefix
*/ */
setEquip( setEquip(
equipId: string, equipId: ItemIdOf<'equips'>,
valueType: string, valueType: 'value' | 'percentage',
name: string, name: keyof SelectType<HeroStatus, number>,
value: any, value: number,
operator?: string, operator?: MotaOperator,
prefix?: string prefix?: string
): void; ): void;
/**
* 穿
* @param type
* @param loadId
* @param unloadId
* @param callback
*/
_realLoadEquip(
type: number,
loadId?: ItemIdOf<'equips'>,
unloadId?: ItemIdOf<'equips'>,
callback?: () => void
): void;
} }
declare const items: new () => Items;

78
src/types/loader.d.ts vendored
View File

@ -1,29 +1,65 @@
/** @file loader.js 主要负责资源的加载 */ /**
declare class loader { *
/** 加载一系列图片 */ */
loadImages(dir: any, names: any, toSave: any, callback?: () => any): any; interface Loader {
/**
*
* @param dir
* @param names
* @param toSave
* @param callback
*/
loadImages(
dir: string,
names: string[],
toSave: Record<string, HTMLImageElement>,
callback?: () => void
): void;
/** 加载某一张图片 */ /**
loadImage(dir: any, imgName?: any, callback?: () => any): any; *
* @param dir
* @param imgName
* @param callback
*/
loadImage(dir: string, imgName: string, callback?: () => void): void;
/** 从zip中加载一系列图片 */ /**
* zip中加载一系列图片
* @param url
* @param names
*/
loadImagesFromZip( loadImagesFromZip(
url?: any, url: string,
names?: any, names: string,
toSave?: any, toSave: Record<string, HTMLImageElement>,
onprogress?: any, onprogress?: (loaded: number, total: number) => void,
onfinished?: any onfinished?: () => void
): any; ): void;
/** 加载一个音乐 */ /**
loadOneMusic(name?: string): any; *
* @param name
*/
loadOneMusic(name: BgmIds): void;
/** 加载一个音效 */ /**
loadOneSound(name?: string): any; *
* @param name
*/
loadOneSound(name: SoundIds): void;
/** 加载一个bgm */ /**
loadBgm(name?: string): any; * bgm
* @param name bgm的id或名称
*/
loadBgm(name: BgmIds | NameMapIn<BgmIds>): void;
/** 释放一个bgm的缓存 */ /**
freeBgm(name?: string): any; * bgm的缓存
* @param name bgm的id或名称
*/
freeBgm(name: BgmIds | NameMapIn<BgmIds>): void;
} }
declare const loader: new () => Loader;

1358
src/types/map.d.ts vendored

File diff suppressed because it is too large Load Diff

98
src/types/source.d.ts vendored Normal file
View File

@ -0,0 +1,98 @@
/**
*
*/
type Cls =
| 'autotile'
| 'animates'
| 'enemys'
| 'items'
| 'npcs'
| 'terrains'
| 'enemy48'
| 'npc48'
| 'tilesets';
/**
*
*/
type AnimatableCls = Exclude<Cls, 'items' | 'terrains' | 'tilesets'>;
/**
*
*/
type ItemCls = 'tools' | 'items' | 'equips' | 'constants';
/**
* id
*/
type AllIds = keyof IdToNumber;
/**
*
*/
type AllNumbers = keyof NumberToId | 0;
/**
* id
*/
type AllIdsOf<T extends Cls> = keyof {
[P in keyof IdToCls as IdToCls[P] extends T ? P : never]: P;
};
/**
* id
*/
type ItemIdOf<T extends ItemCls> = keyof {
[P in keyof ItemDeclaration as ItemDeclaration[P] extends T ? P : never]: P;
};
/**
*
*/
type ItemClsOf<T extends AllIdsOf<'items'>> = ItemDeclaration[T];
/**
*
*/
type ClsOf<T extends AllIds> = IdToCls[T];
/**
*
*/
type AllNumbersOf<T extends Cls> = IdToNumber[AllIdsOf<T>];
/**
*
*/
type NameMapIn<T extends string> = keyof {
[P in keyof NameMap as NameMap[P] extends T ? P : never]: NameMap[P];
};
/**
* id
*/
type EnemyIds = AllIdsOf<'enemys' | 'enemy48'>;
/**
*
*/
interface FrameNumbers {
autotile: 4;
animates: 4;
enemys: 2;
items: 1;
npcs: 2;
terrains: 1;
enemy48: 4;
npc48: 4;
}
/**
*
*/
type FrameOf<T extends Cls> = FrameNumbers[T];
/**
*
*/
type SourceIds = ImageIds | AnimationIds | SoundIds | BgmIds | FontIds;

1104
src/types/status.d.ts vendored

File diff suppressed because it is too large Load Diff

1108
src/types/ui.d.ts vendored

File diff suppressed because it is too large Load Diff

924
src/types/util.d.ts vendored

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,8 @@ import BookDetail from './bookDetail.vue';
import { LeftOutlined } from '@ant-design/icons-vue'; import { LeftOutlined } from '@ant-design/icons-vue';
import { KeyCode } from '../plugin/keyCodes'; import { KeyCode } from '../plugin/keyCodes';
const floorId = core.floorIds[core.status.event?.ui] ?? core.status.floorId; const floorId =
core.floorIds[core.status.event?.ui as number] ?? core.status.floorId;
const enemy = core.getCurrentEnemys(floorId); const enemy = core.getCurrentEnemys(floorId);
const scroll = ref(0); const scroll = ref(0);
@ -65,7 +66,7 @@ enemy.forEach(v => {
if (typeof v === 'string') return v; if (typeof v === 'string') return v;
else return core.arrayToRGBA(v); else return core.arrayToRGBA(v);
}); });
v.damageColor = getDamageColor(v.damage); v.damageColor = getDamageColor(v.damage!);
}); });
/** /**

View File

@ -41,9 +41,10 @@
><div ><div
class="equip selectable" class="equip selectable"
v-for="([id, num], i) of toShow" v-for="([id, num], i) of toShow"
:selected="selected === i" :selected="selected === i && !isCol"
@mousedown="selected = i" @mousedown="select(i)"
@touchstart="selected = i" @touchstart="select(i)"
@click="clickList(i)"
> >
<div class="equip-icon"> <div class="equip-icon">
<BoxAnimate <BoxAnimate
@ -71,10 +72,17 @@
style="width: 100%; height: 30vh" style="width: 100%; height: 30vh"
:type="isMobile ? 'horizontal' : 'vertical'" :type="isMobile ? 'horizontal' : 'vertical'"
> >
<div id="equip-now-div"> <div id="equip-now-div" @touchmove="dragout">
<div <div
v-for="(name, i) of equipCol" v-for="(name, i) of equipCol"
class="equip-now-one" class="equip-now-one draginable selectable"
:draged="draged"
:access="canDragin(i)"
:selected="isCol && selected === i"
@mouseenter="dragin($event, i)"
@touchmove="dragin($event, i)"
@mouseleave="dragout"
@click="select(i, true)"
> >
<BoxAnimate <BoxAnimate
:id="equiped[i] ?? 'none'" :id="equiped[i] ?? 'none'"
@ -112,7 +120,13 @@
</div> </div>
<div id="equip-desc"> <div id="equip-desc">
<div id="equip-icon"> <div id="equip-icon">
<BoxAnimate :id="toShow[selected]?.[0]"></BoxAnimate> <BoxAnimate
:id="
isCol
? equiped[selected] ?? 'none'
: toShow[selected]?.[0] ?? 'none'
"
></BoxAnimate>
<span>{{ equip.name }}</span> <span>{{ equip.name }}</span>
</div> </div>
<div id="equip-type"> <div id="equip-type">
@ -135,15 +149,25 @@
<div id="equip-desc-text"> <div id="equip-desc-text">
<span style="font-size: 3vh" id="title">装备介绍</span> <span style="font-size: 3vh" id="title">装备介绍</span>
<Scroll id="desc-text" style="height: 100%; width: 100%"> <Scroll id="desc-text" style="height: 100%; width: 100%">
<div v-if="!descText.value.startsWith('!!html')"> <div v-if="!descText.value!.startsWith('!!html')">
{{ descText.value }} {{ descText.value }}
</div> </div>
<div v-else v-html="descText.value.slice(6)"></div> <div v-else v-html="descText.value!.slice(6)"></div>
</Scroll> </Scroll>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div id="icon-drag">
<BoxAnimate
class="drag-icon"
v-if="draged"
:id="toShow[selected]?.[0] ?? 'none'"
:width="48"
:height="48"
noborder
></BoxAnimate>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -153,26 +177,30 @@ import {
SortAscendingOutlined, SortAscendingOutlined,
SortDescendingOutlined SortDescendingOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'; import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import { getAddStatus, getEquips, getNowStatus } from '../plugin/ui/equipbox'; import { getAddStatus, getEquips, getNowStatus } from '../plugin/ui/equipbox';
import BoxAnimate from '../components/boxAnimate.vue'; import BoxAnimate from '../components/boxAnimate.vue';
import { has, type } from '../plugin/utils'; import { has, type } from '../plugin/utils';
import { cancelGlobalDrag, isMobile, useDrag } from '../plugin/use'; import { cancelGlobalDrag, isMobile, useDrag } from '../plugin/use';
import { hyper } from 'mutate-animate'; import { hyper } from 'mutate-animate';
import { message } from 'ant-design-vue';
const equips = getEquips(); const equips = ref(getEquips());
const col = ref('all'); const col = ref('all');
const all = core.material.items; const all = core.material.items;
const selected = ref(0); const selected = ref(0);
const isCol = ref(false); //
const equipCol = core.status.globalAttribute.equipName; const equipCol = core.status.globalAttribute.equipName;
const equiped = core.status.hero.equipment; const equiped = ref(core.status.hero.equipment);
const listClicked = ref(false);
const draged = ref(false); const draged = ref(false);
const toEquipType = ref(-1); // 穿-1穿
/** 排序方式down表示下面大上面小 */ /** 排序方式down表示下面大上面小 */
const sort = ref<'up' | 'down'>('down'); const sort = ref<'up' | 'down'>('down');
const norm = ref('none'); const norm = ref<keyof SelectType<HeroStatus, number> | 'none'>('none');
const sType = ref<'value' | 'percentage'>('value'); const sType = ref<'value' | 'percentage'>('value');
// //
@ -197,38 +225,46 @@ watch(sType, n => {
*/ */
const equip = computed(() => { const equip = computed(() => {
const index = toShow.value[selected.value]; const index = toShow.value[selected.value];
if (!has(index)) const none = {
return { name: '没有选择装备',
name: '没有选择装备', cls: 'equip',
cls: 'equip', text: '没有选择装备',
text: '没有选择装备', equip: { type: '无', value: {}, percentage: {}, animate: '' }
equip: { type: '无', value: {}, percentage: {}, animate: '' } };
}; if (isCol.value) {
const id = equiped.value[selected.value];
const e = core.material.items[id];
if (!has(e)) return none;
return e;
}
if (!has(index)) return none;
return all[index[0]]; return all[index[0]];
}); });
const addStatus = computed(() => { const addStatus = computed(() => {
return getAddStatus(equip.value.equip!); // @ts-ignore
return getAddStatus(equip.value.equip!, isCol.value);
}); });
const descText = computed(() => { const descText = computed(() => {
if (equip.value.text.startsWith('!!html')) return ref(equip.value.text); if (equip.value.text!.startsWith('!!html')) return ref(equip.value.text);
return type(equip.value.text, 25, hyper('sin', 'out'), true); return type(equip.value.text!, 25, hyper('sin', 'out'), true);
}); });
const nowStatus = computed(() => { const nowStatus = computed(() => {
return getNowStatus(equip.value.equip); // @ts-ignore
return getNowStatus(equip.value.equip, isCol.value);
}); });
/** /**
* 需要展示的装备需要排序等操作 * 需要展示的装备已进行排序等操作
*/ */
const toShow = computed(() => { const toShow = computed(() => {
const sortType = sort.value; const sortType = sort.value;
const sortNorm = norm.value; const sortNorm = norm.value;
const sortBy = sType.value; const sortBy = sType.value;
const res = equips.filter(v => { const res = equips.value.filter(v => {
const e = all[v[0]].equip!; const e = all[v[0]].equip!;
const t = e.type; const t = e.type;
if (sortNorm !== 'none') { if (sortNorm !== 'none') {
@ -268,15 +304,78 @@ function exit() {
core.plugin.equipOpened.value = false; core.plugin.equipOpened.value = false;
} }
// ----- function clickList(i: number) {
let [fx, fy] = [0, 0]; if (i === selected.value && listClicked.value) {
function dragEquip(x: number, y: number) { const id = toShow.value[selected.value]?.[0];
if ((x - fx) ** 2 + (y - fy) ** 2 > 10 ** 2) { if (!core.canEquip(id)) {
draged.value = true; message.warn({
content: '无法装备!',
class: 'antdv-message'
});
return;
}
core.loadEquip(id);
update();
listClicked.value = false;
} }
listClicked.value = true;
} }
onMounted(() => { /**
* 选择某个装备
*/
function select(i: number, col: boolean = false) {
if (i !== selected.value && !col) {
listClicked.value = false;
}
if (col) listClicked.value = false;
if (col && isCol.value === col && selected.value === i) {
core.unloadEquip(i);
update();
}
isCol.value = col;
selected.value = i;
}
/**
* 是否可以将当前装备拖入该栏
* @param type 装备栏
*/
function canDragin(type: number) {
if (type < 0) return false;
const et = equip.value.equip?.type;
if (!core.canEquip(toShow.value[selected.value]?.[0])) return false;
if (!has(et)) return false;
if (typeof et === 'number') return type === et;
return equipCol[type] === et;
}
/**
* 穿上某个装备
*/
function loadEquip() {
const num = toEquipType.value;
if (num < 0) return;
if (!canDragin(num)) {
message.warn({
content: '无法装备!',
class: 'antdv-message'
});
return;
}
const now = equiped.value[num];
const to = toShow.value[selected.value]?.[0];
core.items._realLoadEquip(num, to, now);
update();
}
function update() {
equiped.value = core.status.hero.equipment;
equips.value = getEquips();
}
// -----
function bind() {
const equips = Array.from( const equips = Array.from(
document.querySelectorAll('.equip') document.querySelectorAll('.equip')
) as HTMLDivElement[]; ) as HTMLDivElement[];
@ -289,9 +388,46 @@ onMounted(() => {
fx = x; fx = x;
fy = y; fy = y;
}, },
void 0, () => {
if (draged.value) {
draged.value = false;
loadEquip();
console.log(toEquipType.value);
}
},
true true
); );
}
// -----
let [fx, fy] = [0, 0];
function dragEquip(x: number, y: number, e: TouchEvent | MouseEvent) {
if ((x - fx) ** 2 + (y - fy) ** 2 > 10 ** 2 && !draged.value) {
draged.value = true;
}
if (draged.value) {
const target = document.getElementById('icon-drag') as HTMLDivElement;
target.style.left = `${x - 24}px`;
target.style.top = `${y - 24}px`;
}
}
function dragin(e: Event, type: number) {
e.stopPropagation();
toEquipType.value = type;
}
function dragout(e: Event) {
e.stopPropagation();
toEquipType.value = -1;
}
watch(toShow, n => {
bind();
});
onMounted(() => {
bind();
}); });
onUnmounted(() => { onUnmounted(() => {
@ -442,11 +578,12 @@ onUnmounted(() => {
padding-left: 5%; padding-left: 5%;
.equip-now-one { .equip-now-one {
flex-basis: 33.3%; flex-basis: 30%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
margin: 3% 0 3% 0; margin: 3% 3.3% 3% 0;
padding-left: 0.5%;
span { span {
margin-left: 10%; margin-left: 10%;
@ -520,6 +657,15 @@ onUnmounted(() => {
} }
} }
#icon-drag {
position: fixed;
width: 32px;
height: 32px;
margin: 0;
padding: 0;
pointer-events: none;
}
@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {
#equipbox { #equipbox {
padding: 5%; padding: 5%;

View File

@ -78,11 +78,15 @@
></BoxAnimate> ></BoxAnimate>
<div id="basic-info"> <div id="basic-info">
<span style="border-bottom: 1px solid #ddd4">{{ <span style="border-bottom: 1px solid #ddd4">{{
all[selected]?.name ?? '没有道具' selected === 'none'
? '没有道具'
: all[selected].name
}}</span> }}</span>
<span>{{ <span>{{
getClsName(all[selected]?.cls as ItemMode) ?? selected === 'none'
'永久道具' ? '永久道具'
: getClsName(all[selected].cls as ItemMode) ??
'永久道具'
}}</span> }}</span>
</div> </div>
</div> </div>
@ -91,10 +95,10 @@
<div id="desc"> <div id="desc">
<span style="border-bottom: 1px solid #ddd4">道具描述</span> <span style="border-bottom: 1px solid #ddd4">道具描述</span>
<Scroll id="desc-text"> <Scroll id="desc-text">
<div v-if="!descText.value.startsWith('!!html')"> <div v-if="!descText.value!.startsWith('!!html')">
{{ descText.value }} {{ descText.value }}
</div> </div>
<div v-else v-html="descText.value.slice(6)"></div> <div v-else v-html="descText.value!.slice(6)"></div>
</Scroll> </Scroll>
</div> </div>
</div> </div>
@ -115,6 +119,7 @@ import { message } from 'ant-design-vue';
import { KeyCode } from '../plugin/keyCodes'; import { KeyCode } from '../plugin/keyCodes';
type ItemMode = 'tools' | 'constants'; type ItemMode = 'tools' | 'constants';
type ShowItemIds = ItemIdOf<'constants' | 'tools'> | 'none';
const mode = ref<ItemMode>('tools'); const mode = ref<ItemMode>('tools');
@ -126,7 +131,7 @@ const toShow = computed<ItemMode[]>(() => {
const all = core.material.items; const all = core.material.items;
const selected = ref(items[toShow.value[0]][0]?.[0] ?? 'none'); const selected = ref<ShowItemIds>(items[toShow.value[0]][0]?.[0] ?? 'none');
const index = ref(0); const index = ref(0);
watch(index, n => { watch(index, n => {
@ -144,15 +149,15 @@ watch(mode, n => {
const descText = computed(() => { const descText = computed(() => {
const s = selected.value; const s = selected.value;
if (s === 'none') return ref('没有选择道具'); if (s === 'none') return ref('没有选择道具');
if (all[s].text.startsWith('!!html')) return ref(all[s].text); if (all[s].text!.startsWith('!!html')) return ref(all[s].text);
return type(all[s].text, 25, hyper('sin', 'out'), true); return type(all[s].text!, 25, hyper('sin', 'out'), true);
}); });
/** /**
* 选择一个道具时 * 选择一个道具时
* @param id 道具id * @param id 道具id
*/ */
async function select(id: string, nouse: boolean = false) { async function select(id: ShowItemIds, nouse: boolean = false) {
if (selected.value === id && !nouse) { if (selected.value === id && !nouse) {
use(id); use(id);
} }
@ -164,7 +169,8 @@ function exit() {
core.plugin.toolOpened.value = false; core.plugin.toolOpened.value = false;
} }
async function use(id: string) { async function use(id: ShowItemIds) {
if (id === 'none') return;
if (core.canUseItem(id)) { if (core.canUseItem(id)) {
// //
core.plugin.transition.value = false; core.plugin.transition.value = false;