0.13地图回合构筑中

This commit is contained in:
salt 2026-04-23 11:05:03 +08:00
parent 36cfe11dc2
commit b30cd88fd6
9 changed files with 1085 additions and 93 deletions

File diff suppressed because one or more lines are too long

View File

@ -99,10 +99,17 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"_lint": true,
"_docs": "能否使用或装备",
"_data": "当前能否使用或装备该道具仅对cls不为items有效。null表示始终不可使用但可装备"
},
"timeCost": {
"_leaf": true,
"_type": "textarea",
"_string": true,
"_docs": "地图回合时间消耗",
"_data": "使用本道具推进的地图时间null 不推进。见 docs/map-turn-spec.md"
}
}
},
"items_template": { 'cls': 'items', 'name': '新物品', 'canUseItemEffect': 'true' },
"items_template": { 'cls': 'items', 'name': '新物品', 'timeCost': null, 'canUseItemEffect': 'true' },
// --------------------------- 【怪物】相关的表格配置 --------------------------- //
@ -187,6 +194,13 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"_type": "textarea",
"_data": "加点"
},
"timeCost": {
"_leaf": true,
"_type": "textarea",
"_string": true,
"_docs": "地图回合",
"_data": "参与地图时间调度的阈值null/0 不参与。置于加点与特殊属性之间。见 docs/map-turn-spec.md"
},
"special": {
"_leaf": true,
"_type": "popCheckboxSet",
@ -362,7 +376,7 @@ var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
}
}
},
"enemys_template": { 'name': '新敌人', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'exp': 0, 'point': 0, 'special': [] },
"enemys_template": { 'name': '新敌人', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'exp': 0, 'point': 0, 'timeCost': null, 'special': [] },
// --------------------------- 【图块属性】相关的表格配置 --------------------------- //

View File

@ -0,0 +1,502 @@
# Map turn integration in tower game
_Exported on 2026/4/22 at GMT+8 21:53:10 from Cursor (3.1.17)_
---
**User**
目标是做一个魔塔游戏的变体魔塔类型游戏其特征为固定地图、仅玩家进行行动。为了令操作更有交互考虑引入地图回合行动这个概念。将角色每移动1格或者进行单场战斗或者其他的待追加视为1次行动。包括玩家操作的角色、敌方每只怪物共用地图回合这个概念。例如玩家使用某技能其消耗了3时间3时间对应3行动那么随这段时间的推进中只消耗1时间就可以1行动的怪物会行动3次。现在该如何在这个项目框架中进行插入地图回合行动这个概念
---
**Cursor**
我先快速梳理你这个工程里“移动、战斗、技能耗时”分别是在哪些钩子里触发的,然后基于现有插件机制给你一个可插入的“地图回合系统”设计方案,尽量做到少改核心、以插件为主。
我看到 `plugins.js` 里已经有自定义角色系统和 `heroMoving` 帧回调,这很适合挂“每步计行动点”。我继续读这一段和战斗相关函数,确认最稳妥的接入点。
我已经定位到引擎层 `libs/events.js`下一步会确认“每走1格”真正完成的时机函数以及战斗触发链路这样能给你一个不破坏现有事件系统的接入点。
我已经把关键接入点确认完了这个框架里“每走1格”就在 `moveOneStep`,战斗结算在 `afterBattle`,道具/技能使用走 `core.items.useItem`。下面我给你一个可直接落地的“地图回合调度器”方案(先最小可用,再扩展怪物 AI。可以而且你这个项目结构非常适合做“地图回合行动”。
你要的核心是:**把“玩家行动消耗时间”转换为“全体怪物获得行动机会”**。
建议做成一个独立的 `MapTurnScheduler`(地图回合调度器),挂在 `core.plugin` 下,不改引擎底层逻辑。
## 推荐接入点(你这个工程里已存在)
`每走1格` 的钩子就在 `project/functions.js``moveOneStep`
```1464:1515:project/functions.js
"moveOneStep": function (callback) {
// ... 省略 ...
// 增加步数
core.status.hero.steps++;
// ... 省略 ...
// 执行目标点的阻激夹域事件
core.checkBlock();
// 执行目标点的script和事件
if (!hasTrigger)
core.trigger(nowx, nowy, callback);
}
```
`单场战斗` 的结算点在 `project/functions.js``afterBattle`
```258:267:project/functions.js
"afterBattle": function (enemyId, x, y) {
// 战斗结束后触发的事件
var enemy = core.material.enemys[enemyId];
var special = enemy.special;
// ...
}
```
`道具/技能使用` 的统一入口在 `libs/items.js`
```81:92:libs/items.js
items.prototype.useItem = function (itemId, noRoute, callback) {
if (!this.canUseItem(itemId)) {
if (callback) callback();
return;
}
this._useItemEffect(itemId);
this._afterUseItem(itemId);
if (!noRoute) core.status.route.push("item:" + itemId);
if (callback) callback();
}
```
---
## 架构方案(建议)
### 1) 定义统一“时间消耗 -> 推进地图回合”
在插件里做一个接口(示意):
```js
core.plugin.mapTurn.consume(cost, source)
```
- `cost`: 本次行动消耗时间(比如移动=1普通战斗=1技能=3
- `source`: 可选记录来源move / battle / skill
### 2) 调度器内部做两层循环
- 外层:消费 `cost` 次“时间单位”
- 内层每个时间单位让所有怪物累积行动值AP够了就行动
示意规则:
- 每只怪有 `actSpeed`每时间单位获得多少AP默认 1
- 每次行动门槛 `actCost`,默认 1
- 若玩家一次消耗 3 时间,`actSpeed=1` 的怪可行动 3 次,`actSpeed=0.5` 的怪行动 1 次(余 0.5
---
## 怪物数据扩展(最稳)
`project/enemys.js` 给需要行动的怪加自定义字段(不会破坏原系统):
- `actSpeed`速度默认1
- `actCost`每次行动消耗默认1
- `aiType`:行为类型(`chase` / `patrol` / `caster` 等)
- `aiArgs`:行为参数
这套字段引擎不会管,但你的调度器能读取。
---
## 如何在你的工程里“插入”而不炸
### A. 玩家移动后推进时间
`moveOneStep` 末尾追加:
- `core.plugin.mapTurn.consume(1, { type: 'move' })`
### B. 战斗结算后推进时间
`afterBattle` 末尾追加:
- `core.plugin.mapTurn.consume(1, { type: 'battle', enemyId, x, y })`
### C. 技能/道具推进时间
两种做法(二选一):
1. **精细控制**:在具体技能 `useItemEffect` 里手动调用(例如 `skill1``consume(3)`
2. **统一控制**:包裹 `core.items.useItem`,按 `itemId -> timeCost` 表自动扣时间
你提到“技能消耗3时间”建议先对技能单独写最清晰。
---
## 怪物行动执行建议(从简单到复杂)
1) **第一阶段(最小可用)**
怪物行动只做“释放地图效果/远程伤害/召唤”,不移动坐标。
优点:几乎不碰路径和碰撞,稳定。
2) **第二阶段移动AI**
使用 `core.moveBlock(x,y,steps,time,true,cb)` 进行怪物移动(引擎已支持)。
- 靠近勇士时攻击(自定义伤害/触发战斗)
- 否则按 `chase` / `patrol` 移动一格
---
## 必须注意的坑
- **事件重入**`consume()` 内可能触发 `insertAction`,要有 `isResolving` 锁,避免递归调用。
- **自动寻路**:怪物行动时可能打断自动路由,建议敌方行动期间临时停自动寻路。
- **楼层切换**建议按楼层存AP`flags.enemyAP[floorId][x,y,id]`),换层后恢复。
- **存读档一致性**:把时间轴状态都放 `flags`,这样天然随存档走。
- **录像一致性**若有随机AI需把随机结果写入route你项目已支持自定义route项
---
## 你现在最适合的落地顺序
1. 先做 `mapTurn.consume()` + `moveOneStep/afterBattle` 两个接入
2. 先实现 1 个怪物 `aiType`(例如只会每回合对勇士造成固定伤害)
3. 再接入技能时间(`skill1`=3
4. 最后再上移动AI`moveBlock`
---
如果你愿意,我下一步可以直接给你一版“贴合你当前文件结构”的**完整伪代码模板**(按 `project/plugins.js + project/functions.js + project/items.js` 分段),你照抄改就能跑。
---
**User**
技能不一定非得消耗3回合仅举例。考虑在item-图块属性-canUseItemEffect的下方注册一个地图回合默认null以skill1二倍斩为例其暂时设置为3。而怪物方面考虑enemys和enemy48这两块在point加点下方也注册一个地图回合如果为0在时间中不考虑其变化以减少检索当前地图所有怪潜在可能导致的卡顿。如果怪物有技能假设为净化花费时间为2那么描述为净化2...(具体效果)这样。这样如何?
---
**Cursor**
这个思路**整体是对的**,而且很适合你现在这套工程。
结论先说:**可以这样做,但建议不要把“地图回合”直接挂在 `canUseItemEffect` 下面复用其语义,最好新增独立字段**,避免后续维护混乱。
## 你这套设计的可行性判断
- **道具/技能侧**:在 `item` 里加一个“耗时字段”很合理(例如 `skill1` = 3
- **怪物侧**:在敌人定义里加“行动耗时/速度字段”也合理;`0` 表示静态怪、忽略地图回合,能明显减少计算。
- **描述展示**:像“净化(2): ...”这种写法非常好,玩家可读性强。
## 建议的字段命名(比“放在 canUseItemEffect 下方”更稳)
### 道具(`project/items.js`
建议新增:
- `timeCost`: 使用该道具/技能消耗多少地图时间(默认 `null``0`
原因:`canUseItemEffect` 在引擎里是“可否使用”的布尔脚本,不是配置容器。
你可以看到它只被当条件执行:
```81:92:libs/items.js
items.prototype.useItem = function (itemId, noRoute, callback) {
if (!this.canUseItem(itemId)) {
if (callback) callback();
return;
}
this._useItemEffect(itemId);
this._afterUseItem(itemId);
if (!noRoute) core.status.route.push("item:" + itemId);
if (callback) callback();
}
```
```127:135:libs/items.js
items.prototype.canUseItem = function (itemId) {
if (!core.hasItem(itemId)) return false;
var canUseItemEffect = core.material.items[itemId].canUseItemEffect;
if (canUseItemEffect) {
try {
return eval(canUseItemEffect);
```
---
### 怪物(`project/enemys.js`
建议新增:
- `timeCost``actCost`(每次行动所需时间,默认 `0` 表示不参与)
- 可选 `actType`(追击/施法/巡逻)
- 可选 `actValue`(技能强度)
你提的“`point` 下方加字段”完全可以,兼容性没问题。
## 关于性能你的“0 不参与”是关键优化点
你担心每回合扫全图卡顿,这个判断非常专业。
建议实现时再加一层优化:
- 进入楼层时,建立一次 `activeEnemies` 列表(只收 `timeCost > 0` 的怪)
- 地图变化(怪死亡/召唤/移动)时局部更新这份列表
- 每次时间推进只遍历 `activeEnemies`,不扫全图
这样比“每次都 `extractBlocks + filter`”快很多。
## “净化(2): ...”显示位置建议
你现在怪物特殊属性文案在 `getSpecials` 里定义,可直接拼接耗时展示:
```493:506:project/functions.js
return [
[1, "先攻", "怪物首先攻击", "#ffcc33"],
...
[9, "净化", function (enemy) { return "战斗前,怪物附加角色护盾的" + (enemy.purify || core.values.purify) + "倍作为伤害"; }, "#80eed6"],
```
可以改成类似:
- 名称:`净化(2)`(静态)
- 或描述函数里动态拼:`净化(${enemy.timeCost||0})...`
---
你的方案方向是正确的;我只建议做一个小修正:
**“地图回合耗时”单独字段化(`timeCost`),不要借 `canUseItemEffect` 的语义。**
这样你后面做 UI 展示、战斗日志、AI 调度都会更顺。
如果你要,我下一条可以直接给你一份“字段规范 + 调度规则表”(含默认值和边界行为),你可以直接当项目约定文档用。
---
**User**
统一使用timecost。追击、巡逻、等怪物行动统一为玩家行动消耗这个后相应进行行动。对消耗时间的操作进一步确认玩家移动一格含事件中强制移动、拾取将面前道具获得不视为移动、使用tools类道具例如bigKey、单场战斗。特别的部分boss需要特别设置为单次接触就是一次战斗回合不连续推进回合也视为1地图回合来玩家有更多操作空间。而玩家的技能根据类型可否取消来推进时间例如远程技能扔出去就打在怪身上无法撤回的直接消耗地图回合而战斗技能或者状态技能由于战斗前玩家需要考虑和模拟是否打开会更好则不直接消耗时间直到进行战斗才结算并且如果是二倍斩花费3时间跟战斗时间默认1之间不累加为4而是取最大值3。这样如何
---
**Cursor**
这版规则很成熟了,已经接近“可直接实现”的设计稿。
结论:**整体可行,逻辑自洽,且可玩性会明显提升**。下面我帮你把关键点“定死”,避免实现时歧义。
## 核心结论
你这套应统一成一句话:
- **地图时间只由“玩家消耗 timeCost 的行为”推进;怪物按这段时间统一行动。**
你列的行为里,大部分都应该推进时间,且用 `timeCost` 统一配置完全正确。
## 建议定版的时间结算规则
### 1) 玩家行为触发地图时间(建议)
- 移动1格含事件强制移动`timeCost = 1`
- 轻按拾取(面前道具,不移动)→ 默认 `timeCost = 1`(建议可配)
- 使用 `tools` 类道具(如 `bigKey`)→ 道具自身 `timeCost`(默认 1 或 0看你设计
- 单场战斗 → 默认 `timeCost = 1`可被技能覆盖见第4条
这部分定义非常清晰,建议再补一句:
**纯UI操作开菜单、开手册、查看模拟不推进时间。**
---
### 2) 怪物行动规则(追击/巡逻统一)
你说的“玩家消耗多少怪物就相应行动”完全OK。
推荐实现成:
- 怪物字段 `timeCost`
- `0`:静态,不参与地图回合(性能优化)
- `>0`:参与地图回合,每累计到门槛可行动一次
- 怪物字段 `actType`
- `chase`、`patrol`、`skill` 等
并且保留你说的描述显示:`净化(2)...`。
---
### 3) Boss“单次接触只打一回合”机制
这个需求很好,能显著提升策略空间。
建议加怪物字段:
- `contactBattleOnly: true`
语义定为:
- 每次接触该 Boss仅结算“一次战斗接触”
- 本次接触后立即结束,不触发连续回合推进
- 该次接触仍记为 `1` 地图回合(或技能覆盖值)
这样你就能实现“打一下、走位、再打一下”的 Boss 节奏。
---
### 4) 技能耗时与战斗耗时合并(你的 max 规则)
你提出的规则非常好,建议正式定为:
- `finalBattleTimeCost = max(baseBattleTimeCost, pendingSkillTimeCost)`
也就是你说的:
- 二倍斩 `timeCost=3`
- 战斗默认 `1`
- 最终取 `3`,不叠加成 `4`
这个比相加更平衡,也避免“状态技能 + 战斗”被双重惩罚。
---
### 5) 可取消/不可取消技能的时间点(关键)
你这条很专业,建议直接固化:
- **不可取消技能**(如抛射物已发出)
- 释放瞬间立即推进地图时间(消耗 `timeCost`
- **可取消技能**(战斗前开关类/状态类)
- 开启时不推进时间,只记录 `pendingSkillTimeCost`
- 真正触发战斗时按第4条结算
- 取消则清空 `pendingSkillTimeCost`
这会让“预判、模拟、开关”体验很好,不会被操作惩罚。
---
## 你现在还需要补的两个小约束防Bug
- **多技能并存时的规则**
建议 `pendingSkillTimeCost` 取最大,不叠加(和战斗规则一致,最稳)。
- **战斗未发生时清理时机**
可取消技能若切楼/剧情中断/死亡,记得清空 pending避免脏状态。
---
## 最终评价
这套方案可以直接作为你的“地图回合制规范 v1”
- 字段统一:`timeCost`
- 行为统一:玩家消耗时间 -> 怪物响应行动
- 性能可控:`timeCost=0` 怪不参与
- Boss可特判单次接触一回合
- 技能结算合理:可取消延后结算,且与战斗取 `max`
如果你愿意,我下一步可以给你一份“**精确到变量名和状态机的伪代码规范**”(例如 `pendingSkillTimeCost`, `consumeTime(cost, reason)`, `resolveEnemyActions(cost)` 的完整流程图式文本),你可以直接照着实现。
---
**User**
补充:分为四种技能,战斗技能(战斗中)、状态技能(战斗前例如加攻击)、恢复技能(非战斗,例如回血回蓝)、远程技能(非战斗,对敌人)。多技能共存而言,战斗中技能不允许同时启用多个战斗技能。状态类技能,默认不消耗时间。恢复技能视为远程技能,均消耗时间。考虑对敌人的技能也准备按这样后续修改。这样的话,应该也不存在脏状态问题了。
---
**Cursor**
这个补充很到位,规则已经很完整了。
结论:**按你这四类技能划分后,系统会更稳定,也更容易避免脏状态**。你现在这版可以作为正式规范了。
建议你把规则最终固定成下面这份(可直接当实现约束):
- `战斗技能`仅战斗中生效同一时刻最多启用1个互斥
- `状态技能`:战斗前开关类;默认不推进地图时间;在战斗结算时参与 `max(战斗耗时, 状态技能耗时)`
- `恢复技能`:非战斗;按你定义归入“即时消耗时间”类(和远程一致)。
- `远程技能`:非战斗对敌;释放即生效,不可撤回,立即消耗 `timeCost`
你说“这样基本不存在脏状态”——**基本成立**,但我建议再加两条保险,彻底封死边角:
- 在以下时机统一清理临时态:`战斗结束`、`切楼`、`死亡`、`读档`、`中断事件链`。
(比如 `pendingSkillTimeCost`、`pendingSkillId`、`inBattleSkillId`
- 若状态技能是“可开关”,关闭时立即清空挂起耗时,避免下一场战斗误继承。
再加一个小建议(体验向):
- 给技能增加统一字段:`skillType: battle|status|recover|ranged``timeCost``exclusiveGroup`(战斗技能用)。
后续你改“对敌技能”时就不需要额外写分支了,直接走同一调度器。
整体上,这个设计已经可以进实现阶段了。
---
**User**
状态技能可以多回合例如强化3次战斗这样每次战斗后-1。所以要考虑战斗结束切楼、读档则不影响死亡则会引入后续游戏机制直接清除所有状态即可事件不打断当前状态。其他还有问题吗
---
**Cursor**
这版已经很接近可落地了。你这个补充下,主要再补 6 个边界定义就够了:
- **战斗计数何时扣减**:建议统一在 `afterBattle` 成功结算后 `-1`;如果战斗前判定不可战斗、取消、或角色死亡前未完成结算,不扣。
- **哪些战斗算“1次”**:要明确包含 `普通接触战斗`、`强制战斗`、`伏击/捕捉触发战斗`、`多怪支援战` 是否算 1 次(建议支援仍算 1 次“单场”)。
- **多状态技能并存规则**:同类是否叠加?建议先定为“同 ID 刷新层数/回合,不叠加多个实例”,避免状态爆炸。
- **状态与时间的关系**:状态技能本身不耗时,但若它提供 `battleTimeCost`,战斗时用你之前定的 `max(1, 所有生效状态技能耗时)`,不要相加。
- **读档一致性**:你说切楼/读档不影响,这没问题;前提是把状态剩余次数、来源技能、附加参数都存进 `flags`,否则会“看起来不影响、实际丢状态”。
- **死亡清空范围**:你说“清除所有状态”很好,建议明确只清“临时战斗状态与挂起状态”,不清永久被动/装备效果。
你当前设定里“事件不打断当前状态”也可行,但再加一句更稳:
**事件里如果显式调用了清状态事件,优先按事件脚本执行。**
整体判断:没有大问题,剩下就是把“什么算一场战斗、何时扣层数、是否叠加”这三件事写死成规则文档即可。
---
**User**
按序进行1、战斗成功结算后如果失败按死亡计算。2、这些都视为1次战斗非boss战都是如此如果被不同位置的多怪捕捉进行了多次战斗则逐一结算。3、部分技能可以部分不行后续会另行设计。4、状态技能恒定为timecost为0。5、就按读档一致性来。6、可以。
---
**Cursor**
可以,这 6 条现在已经是**可实现且不冲突**的一版规范了。按你给的顺序,我帮你固化成“最终判定口径”:
- `1` 只在**战斗成功结算后**扣状态次数;战斗失败直接走死亡流程,不做后续扣减流程。
- `2` 普通战统一按“1次战斗”计若由不同位置多怪捕捉触发了多场战斗就逐场独立结算每场都可触发状态-1
- `3` 技能是否可叠加/共存留作后续分技能设计,当前框架兼容。
- `4` 状态技能 `timeCost` 恒为 `0`(开启/维持不推进地图时间)。
- `5` 状态完整进入存档体系,读档后保持一致。
- `6` 死亡时允许清状态(按你后续机制扩展)。
你这版下,系统核心就很清楚了:
**地图时间推进只由非状态技能/移动/拾取/道具/战斗触发,状态技能只影响战斗效果与战后计数,不影响时间轴。**
如果你愿意下一步我可以给你一份“实现时的最小数据结构清单”字段名级别10行内方便你直接落到 `items/enemys/flags`
---
**User**
之前提到一个给我一份“精确到变量名和状态机的伪代码规范”,生成相应的计划和文本。
---
**Cursor**
我会给你一份可直接执行的计划:先把“地图时间/技能状态”的状态机与字段名规范化再产出一份伪代码文本按你已确认的6条规则固化并附上实现落点清单。接下来我先生成计划供你确认。
---
**User**
Implement the plan as specified, it is attached for your reference. Do NOT edit the plan file itself.
To-do's from the plan have already been created. Do not create them again. Mark them as in_progress as you work, starting with the first one. Don't stop until you have completed all the to-dos.
---
**Cursor**
我将按计划逐项落地,先完成规则冻结与优先级表,然后依次写数据模型、状态机伪代码、接入点映射和验收清单,最终产出 `docs/map-turn-spec.md`。先把第一个待办设为进行中并开始写文档。
我开始写规范文档正文,先固化规则优先级并连同变量名、状态机伪代码一次性写入,随后回填待办状态。
已按计划完整实现,并且所有待办都已依序完成。
已新增规范文档:
- `docs/map-turn-spec.md`
文档内容已覆盖你要求的全部部分:
- 规则优先级冻结(按你确认的 1-6 条与补充口径固化)
- 统一字段命名(全部使用 `timeCost`)与运行时状态容器变量名
- 地图时间推进 + 技能状态的状态机(含 mermaid
- 精确到函数名的伪代码(`consumeTime`、`resolveEnemyActions`、`settleBattleTimeCost`、`applyStatusAfterBattle`、`clearOnDeath`
- 多怪捕捉逐场结算、Boss 单次接触特判
- 与现有工程函数的接入点映射(`moveOneStep` / `afterBattle` / `useItem` / `plugins`
- 存读档一致性要求
- 最小验收清单8 条)
如果你希望,我下一步可以基于这份规范再给一版“实施顺序最小改动清单”(先改哪 3 个函数最稳)。

303
docs/map-turn-spec.md Normal file
View File

@ -0,0 +1,303 @@
# 地图回合与技能状态伪代码规范
## 1. 目标与范围
本规范用于在当前项目中引入“地图时间Map Turn”和“四类技能状态”机制确保
- 规则口径唯一,避免实现歧义。
- 变量命名可直接映射到代码实现。
- 与现有流程兼容:移动、战斗、道具、捕捉、存读档。
- 先支持玩家侧,后续可平滑扩展到“敌方技能”。
## 2. 规则优先级(冻结版)
当规则冲突时,按以下优先级从高到低执行:
1. 死亡流程优先:战斗失败或致死效果触发后,立即进入死亡清理。
2. 战斗成功后扣层:只有成功结算的战斗才扣除“按战斗次数持续”的状态层数。
3. 时间推进来源:仅由推进时间的行为触发(移动/拾取/tools/战斗/远程/恢复)。
4. 状态技能时间固定:状态技能 `timeCost = 0`,开启/关闭不直接推进地图时间。
5. 战斗耗时结算:`battleFinalTimeCost = max(baseBattleTimeCost, statusBattleTimeCostMax)`。
6. 多战逐场结算:同一帧内多场战斗(如多怪捕捉)逐场独立结算。
7. 存读档一致:状态容器完整写入 `flags`,读档恢复后行为一致。
8. 切楼不清状态:切楼、读档不影响状态持续;死亡按机制清临时状态。
9. 事件默认不打断状态:除非事件显式调用清理接口。
## 2.1 时间增量(`timeCost` / `deltaTime`)与地图回合(`mapTurn`
| 概念 | 含义 | 典型变化 |
|------|------|----------|
| **时间增量** | 一次玩家行为产生的「要结算的时间单位」总数,记为 `deltaTime`(与道具/战斗等配置的 `timeCost``battleFinalTimeCost` 一致)。 | 可一次从 `0` 跳到 `3`(例如 `consumeTime(3, ...)`)。 |
| **`mapTurn`(离散 tick** | 每完成 **1** 个时间单位的完整地图回合结算,记数 `+1`。 | 必须 **逐一** 经历 `… → k → k+1 → k+2 → …`,不可把 3 个 tick 合并成一次模糊批处理。 |
**结算顺序(写死)**:对外入口 `consumeTime(deltaTime, reason)` 在单帧内收到 `deltaTime = n` 时:
1. 先将累计量一次性加上:`flags.mapTurnState.clock += n`(表示「时间池」或总经过时间,允许与 `mapTurn` 不同步增长策略见下)。
2. 再 **循环 `n` 次** 调用 `advanceMapTurnOne(reason)`:每次只做 **一个** tick——`mapTurn += 1`,并在该 tick 内完成敌人槽位/行动等(见 §6
**推荐关系**:若采用「整数时间模型」,也可令 `clock` 仅在循环内每 tick `+1`,与 `mapTurn` 同步递增;**无论 `clock` 是一次 `+=n` 还是分 `n``+=1``advanceMapTurnOne` 必须被调用恰好 `n` 次**以保证「0→3 的时间变化」对应「mapTurn 连续三步」的语义。
**实现落点**:调度函数挂在 [project/plugins.js](project/plugins.js) 的 `core.plugin.mapTurn`(或等价模块名);`items.js` / `enemys.js` 仅提供数值字段 `timeCost` 等,不在图块脚本里写复杂循环。
## 3. 统一字段与运行时数据模型
### 3.1 技能定义items 扩展)
```js
// project/items.js 每个可用技能道具tools类建议字段
{
skillType: "battle" | "status" | "recover" | "ranged",
timeCost: number, // 统一时间消耗状态技能固定为0
canCancel: boolean, // 可否取消主要用于status
exclusiveGroup: string | null, // 战斗技能互斥组,例如 "battleSkill"
durationBattles: number | null,// 状态技能持续战斗次数null表示非按战斗计数
stackPolicy: "refresh" | "stack" | "replace" // 后续扩展
}
```
### 3.2 怪物定义enemys/enemy48 扩展)
```js
// project/enemys.js + enemy48 对应定义建议字段
{
timeCost: number, // 0=不参与地图时间调度;>0=参与
actType: "chase" | "patrol" | "skill" | "idle",
contactBattleOnly: boolean, // Boss可设true单次接触仅一场战斗
actArgs: object | null
}
```
### 3.3 运行时状态flags
```js
flags.mapTurnState = {
// 累计时间单位(可与 mapTurn 同步策略二选一,见 §2.1
clock: 0,
// 已完成的离散地图回合 tick 数;仅能通过 advanceMapTurnOne 每次 +1
mapTurn: 0,
enemyActionGauge: {
// [floorId]: { [enemyRuntimeId]: gaugeValue }
},
// 仅收录「怪物定义 timeCost > 0」的实例按楼层缓存供每 tick 遍历,避免全图 extractBlocks
activeEnemiesByFloor: {
// [floorId]: [
// { runtimeId, x, y, enemyId, blockIndex?, ... } // runtimeId 实现阶段约定,如 "floorId:x:y:enemyId"
// ]
},
// 可选:每次 rebuild / patch 后自增,便于调试与断言缓存有效
activeEnemiesVersion: 0
}
flags.skillState = {
activeStatusSkills: {
// [skillId]: {
// remainBattles: number,
// battleTimeCost: number, // 对战斗耗时影响值状态技能可为0或设计值
// sourceItemId: string
// }
},
activeBattleSkillId: null, // 当前战斗技能(互斥)
temp: {
// 临时运行态,死亡可清空
inBattle: false
}
}
```
### 3.4 插件模块职责(`project/plugins.js`
- 实现 `core.plugin.mapTurn`(名称可调整,全文保持一致),至少包含:
- `consumeTime(deltaTime, reason)`
- `advanceMapTurnOne(reason)`
- `rebuildActiveEnemies(floorId)`、`patchActiveEnemiesForBlockChange(...)`(见 §6.6
- `resolveEnemyActionsForSingleTick(reason)`(仅遍历当前层 `activeEnemies`
- 可选:`__enable` 总开关,关闭时上述函数为空操作,便于分步接入游戏。
## 4. 行为触发矩阵(是否推进地图时间)
- 移动一格(含事件强制移动):推进,默认 `+1`
- 轻按拾取(不移动):推进,默认 `+1`
- 使用 tools 类道具(如 bigKey按道具 `timeCost` 推进。
- 单场战斗:推进,按 `battleFinalTimeCost`
- 远程技能(不可撤回):立即按技能 `timeCost` 推进。
- 恢复技能:视同远程技能,立即按 `timeCost` 推进。
- 状态技能:`timeCost=0`,不推进地图时间。
- 纯 UI 操作(菜单、手册、预览模拟):不推进。
**说明**:矩阵中的「推进 `+n`」均指调用 `consumeTime(n, ...)`;引擎内部必须把 `n` 拆成 `n``advanceMapTurnOne`§2.1)。
## 5. 核心状态机
```mermaid
flowchart TD
playerAction[PlayerAction] --> classifyAction[ClassifyActionType]
classifyAction -->|move/getItem/tool/ranged/recover| consumeTime[consumeTime_delta]
classifyAction -->|statusSkillToggle| updateStatus[upsertStatusSkillState]
classifyAction -->|battleTrigger| settleBattleTime[settleBattleTimeCost]
settleBattleTime --> consumeTime
consumeTime --> loopTicks["loop_delta_times"]
loopTicks --> advanceOne[advanceMapTurnOne]
advanceOne --> resolveEnemy[resolveEnemyActionsForSingleTick]
resolveEnemy --> maybeBattle[maybeTriggerBattles]
maybeBattle -->|battleSuccess| afterBattle[applyStatusAfterBattle]
maybeBattle -->|battleFail| deathClear[clearOnDeath]
afterBattle --> persist[saveStateToFlags]
updateStatus --> persist
deathClear --> persist
```
## 6. 伪代码规范(变量名级别)
### 6.1 时间推进入口一次可变多格tick 逐一结算)
```js
function consumeTime(deltaTime, reason) {
if (!deltaTime || deltaTime <= 0) return;
// 累计时间可一次加上 deltaTime与 §2.1 一致若采用「clock 与 mapTurn 同步每 tick +1」则改为在 advanceMapTurnOne 内 +1
flags.mapTurnState.clock += deltaTime;
for (let i = 0; i < deltaTime; i++) {
advanceMapTurnOne(reason); // 内部 mapTurn += 1且只处理单 tick
}
}
function advanceMapTurnOne(reason) {
flags.mapTurnState.mapTurn = (flags.mapTurnState.mapTurn || 0) + 1;
resolveEnemyActionsForSingleTick(reason);
}
```
### 6.2 怪物行动调度(单 tick只遍历 `activeEnemies`
```js
function resolveEnemyActionsForSingleTick(reason) {
const floorId = core.status.floorId;
const list = flags.mapTurnState.activeEnemiesByFloor[floorId] || [];
for (const enemyRef of list) {
const runtimeId = enemyRef.runtimeId;
const enemyDef = enemyRef.def;
// 每 mapTurn tick行动槽 +1达到怪物 timeCost怪物参与调度阈值则行动一次
addEnemyGauge(floorId, runtimeId, 1);
while (getEnemyGauge(floorId, runtimeId) >= enemyDef.timeCost) {
subEnemyGauge(floorId, runtimeId, enemyDef.timeCost);
performEnemyAction(enemyRef); // chase/patrol/skill/idle
}
}
}
```
**注意**:列表中的怪物已保证 `enemyDef.timeCost > 0`(见 §6.3);此处 **禁止** 每 tick 调用 `extractBlocks` 全图扫描。
### 6.3 `activeEnemies` 维护契约(性能)
1. **建表**:进入某楼层且地图块就绪后,调用 `rebuildActiveEnemies(floorId)`,扫描该层 `blocks`(或等价 API仅加入「怪物图块且对应 `core.material.enemys[id].timeCost > 0`」的项,写入 `flags.mapTurnState.activeEnemiesByFloor[floorId]`,并 `activeEnemiesVersion++`
2. **局部更新**:当 **地图上的怪物集合或坐标** 发生变化时(如 `removeBlock`、`setBlock`、怪物 `moveBlock` 结束、`hideBlock` 等),调用 `patchActiveEnemiesForBlockChange(...)` 增删或更新对应条目,**不** 全量重建(除非实现成本过高,可退化为对该层 `rebuildActiveEnemies`)。
3. **遍历**`resolveEnemyActionsForSingleTick` **仅** 遍历当前层缓存列表;`deltaTime > 1` 时,重复 `deltaTime` 次单 tick 调度,等价于 mapTurn 从 `k` 逐步走到 `k+deltaTime`
4. **换层**`core.status.floorId` 切换后,下一 tick 使用新层的 `activeEnemiesByFloor[floorId]`;未访问过的楼层可无列表,首次结算前 `rebuild`
5. **读档**`activeEnemiesByFloor` 为派生缓存,**允许** 在读档结束、当前楼层已 `drawMap` 就绪后执行一次 `rebuildActiveEnemies(currentFloorId)``enemyActionGauge` 等需持久化的数据仍放在 `flags.mapTurnState` 内随存档走。
### 6.4 战斗耗时结算
```js
function settleBattleTimeCost() {
const baseBattleTimeCost = 1;
// 仅统计当前生效状态技能中的“战斗耗时影响”
let statusBattleTimeCostMax = 0;
for (const skillId in flags.skillState.activeStatusSkills) {
const state = flags.skillState.activeStatusSkills[skillId];
statusBattleTimeCostMax = Math.max(statusBattleTimeCostMax, state.battleTimeCost || 0);
}
return Math.max(baseBattleTimeCost, statusBattleTimeCostMax);
}
```
### 6.5 战斗后状态扣减
```js
function applyStatusAfterBattle(battleResult) {
if (battleResult !== "success") return; // 失败走死亡流程
for (const skillId in flags.skillState.activeStatusSkills) {
const state = flags.skillState.activeStatusSkills[skillId];
if (typeof state.remainBattles === "number") {
state.remainBattles -= 1;
if (state.remainBattles <= 0) {
delete flags.skillState.activeStatusSkills[skillId];
}
}
}
}
```
### 6.6 死亡清理
```js
function clearOnDeath() {
// 预留:后续可细分清理范围
flags.skillState.temp = {};
flags.skillState.activeBattleSkillId = null;
flags.skillState.activeStatusSkills = {};
}
```
## 7. 关键业务分支
### 7.1 多怪捕捉导致多场战斗
- 逐场调用战斗结算。
- 每场成功后都执行一次 `applyStatusAfterBattle("success")`
- 任一场失败则进入死亡流程并终止后续结算。
### 7.2 Boss 单次接触
- `contactBattleOnly=true` 时,每次接触仅触发一场战斗。
- 该场战斗仍视作 1 场独立结算,按 `settleBattleTimeCost()` 推进时间。
- 不做“连战自动推进”,给玩家留操作窗口。
### 7.3 战斗技能互斥
```js
function activateBattleSkill(skillId) {
const skill = getSkillDef(skillId);
if (skill.exclusiveGroup === "battleSkill") {
flags.skillState.activeBattleSkillId = skillId;
}
}
```
## 8. 工程接入点映射
- `project/plugins.js`:实现 `core.plugin.mapTurn`§3.4),包含 `consumeTime`、`advanceMapTurnOne`、`resolveEnemyActionsForSingleTick`、`rebuildActiveEnemies`、`patchActiveEnemiesForBlockChange`、`clearOnDeath` 等;**所有「推进多少时间就结算多少次 tick」的逻辑集中于此**。
- `project/functions.js` -> `moveOneStep`:移动完成后调用 `core.plugin.mapTurn.consumeTime(1, "move")`(或封装名一致即可)。
- `project/functions.js` -> `afterBattle`:成功结算后先 `consumeTime(settleBattleTimeCost(), "battle")`(内部按 §2.1 拆 tick`applyStatusAfterBattle("success")`
- `project/functions.js` -> `afterChangeFloor`(或 `changingFloor` 结束、楼层已绘制后):**一行** `core.plugin.mapTurn.rebuildActiveEnemies(core.status.floorId)`,建立当前层 `activeEnemies`§6.3)。
- `libs/items.js` -> `useItem`(或道具 `useItemEffect` 末尾):恢复/远程/tools 按 `item.timeCost` 调用 `consumeTime(item.timeCost, ...)`;状态技能只更新 `flags.skillState`,不调用 `consumeTime`
- `project/enemys.js`(及 enemy48按需为参与地图回合的怪物设置 `timeCost > 0``actType``timeCost === 0` 的怪不得进入 `activeEnemies`
- **地图变更挂钩(实现时择优)**:在会改变怪物位置/存亡的 API 之后调用 `patchActiveEnemiesForBlockChange`(若短期无法统一挂钩,可退化为每次变更后对该层 `rebuildActiveEnemies`)。
## 9. 存读档一致性要求
- 所有运行态必须在 `flags.mapTurnState``flags.skillState` 下可序列化。
- 读档后不重置状态层数、不重置时间计量(`clock` / `mapTurn` / `enemyActionGauge`)、不重置技能状态。
- 切楼不触发状态清空,仅切换当前楼层;换层后须使用对应 `activeEnemiesByFloor[floorId]`
- `activeEnemiesByFloor` 为派生缓存:读档后若列表缺失或与地图不一致,在楼层就绪时 **重建一次**§6.3),不依赖旧缓存跨版本兼容。
## 10. 最小验收清单
- 使用状态技能后不立即推进时间,战斗成功后层数 `-1`
- 远程/恢复技能释放立即推进时间,怪物按时间响应。
- 战斗耗时按 `max` 规则而不是累加。
- 多怪捕捉触发多场战斗时,状态层数逐场扣减。
- Boss `contactBattleOnly=true` 时单次接触仅结算一场。
- 死亡后状态与临时态清理符合预期。
- 存档读档后状态层数与地图时间连续。
- 设置怪物 `timeCost=0` 时,该怪不参与调度,性能无明显回退。
- **`consumeTime(3, ...)` 触发恰好 3 次 `advanceMapTurnOne`**`mapTurn` 连续 `+3`,且敌人行动槽/行为按 **3 个独立 tick** 结算(可用日志或计数器断言)。
- **`activeEnemies` 路径**:每 tick 不调用全图 `extractBlocks`;进楼 `rebuild` 后,增删怪仅 `patch` 或单次 `rebuild``timeCost=0` 的怪不在列表中。

View File

@ -11,6 +11,10 @@ enemys.prototype._init = function () {
this.enemydata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys;
for (var enemyId in this.enemys) {
this.enemys[enemyId].id = enemyId;
// 地图回合:数据表约定为 point 与 special 之间的 timeCost缺失时补 null含编辑器新注册怪物
var _e = this.enemys[enemyId];
if (_e.timeCost === undefined)
_e.timeCost = null;
}
if (main.mode == 'play') {
this.enemydata.hasSpecial = function (a, b) {

View File

@ -10,6 +10,10 @@ items.prototype._init = function () {
this.items = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a;
for (var itemId in this.items) {
this.items[itemId].id = itemId;
// 地图回合:与 project/items.js 一致,缺省 timeCost 时补 null含编辑器新注册道具
var _it0 = this.items[itemId];
if (_it0.timeCost === undefined)
_it0.timeCost = null;
}
}
@ -85,6 +89,13 @@ items.prototype.useItem = function (itemId, noRoute, callback) {
}
// 执行道具效果
this._useItemEffect(itemId);
// 地图回合:按 project/items.js 中 timeCostnull 不推进);见 docs/map-turn-spec.md
try {
var _item = core.material.items[itemId];
var _tc = _item && _item.timeCost;
if (typeof _tc === "number" && _tc > 0 && core.plugin && core.plugin.mapTurn && typeof core.plugin.mapTurn.consumeTime === "function")
core.plugin.mapTurn.consumeTime(_tc, "item:" + itemId);
} catch (e) { }
// 执行完毕
this._afterUseItem(itemId);
// 记录路线

View File

@ -1,84 +1,84 @@
var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
{
"greenSlime": {"name":"绿头怪","hp":40,"atk":7,"def":1,"money":0,"exp":0,"point":0,"special":0},
"redSlime": {"name":"红头怪","hp":60,"atk":10,"def":1,"money":0,"exp":0,"point":0,"special":[],"value":0},
"blackSlime": {"name":"青头怪","hp":80,"atk":18,"def":1,"money":0,"exp":0,"point":0,"special":0},
"slimelord": {"name":"怪王","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[]},
"bat": {"name":"小蝙蝠","hp":30,"atk":12,"def":3,"money":2,"exp":0,"point":0,"special":[4]},
"bigBat": {"name":"大蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"redBat": {"name":"红蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"vampire": {"name":"冥灵魔王","hp":888,"atk":888,"def":888,"money":888,"exp":888,"point":0,"special":[],"n":8},
"skeleton": {"name":"骷髅人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"skeletonCaptain": {"name":"骷髅队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"zombie": {"name":"兽人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"zombieKnight": {"name":"兽人武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"rock": {"name":"石头人","hp":50,"atk":50,"def":0,"money":3,"exp":0,"point":0,"special":3},
"bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"special":[9]},
"redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"brownWizard": {"name":"初级巫师","hp":100,"atk":120,"def":0,"money":16,"exp":0,"point":0,"special":15,"value":100,"range":2},
"redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"special":15,"value":200,"zoneSquare":true},
"swordsman": {"name":"双手剑士","hp":100,"atk":120,"def":0,"money":6,"exp":0,"point":0,"special":4},
"soldier": {"name":"冥战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"yellowKnight": {"name":"金骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"redKnight": {"name":"红骑士","hp":500,"atk":200,"def":50,"money":0,"exp":0,"point":0,"special":[7]},
"darkKnight": {"name":"黑骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"blueKnight": {"name":"蓝骑士","hp":100,"atk":120,"def":0,"money":9,"exp":0,"point":0,"special":8},
"goldSlime": {"name":"黄头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"poisonSkeleton": {"name":"紫骷髅","hp":50,"atk":60,"def":70,"money":80,"exp":0,"point":0,"special":13},
"poisonBat": {"name":"紫蝙蝠","hp":100,"atk":120,"def":0,"money":14,"exp":0,"point":0,"special":13},
"skeletonPriest": {"name":"骷髅法师","hp":100,"atk":100,"def":0,"money":0,"exp":0,"point":0,"special":18,"value":20},
"skeletonKing": {"name":"骷髅王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"evilHero": {"name":"迷失勇者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"demonPriest": {"name":"魔神法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"goldHornSlime": {"name":"金角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"silverSlime": {"name":"银头怪","hp":100,"atk":120,"def":0,"money":15,"exp":0,"point":0,"special":14},
"whiteHornSlime": {"name":"尖角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"redSwordsman": {"name":"剑王","hp":300,"atk":300,"def":2,"money":0,"exp":0,"point":0,"special":[28]},
"poisonZombie": {"name":"绿兽人","hp":100,"atk":120,"def":0,"money":13,"exp":0,"point":0,"special":[12]},
"octopus": {"name":"血影","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0,"bigImage":"dragon_1.png"},
"princessEnemy": {"name":"假公主","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"angel": {"name":"天使","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"steelGuard": {"name":"铁守卫","hp":50,"atk":50,"def":50,"money":0,"exp":0,"point":0,"special":[18],"value":20},
"evilBat": {"name":"邪恶蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"silverSlimelord": {"name":"银怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"goldSlimelord": {"name":"金怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"skeletonWarrior": {"name":"骷髅士兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"whiteSlimeman": {"name":"水银战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"slimeman": {"name":"影子战士","hp":100,"atk":0,"def":0,"money":11,"exp":0,"point":0,"special":[9],"atkValue":2,"defValue":3},
"yellowGateKeeper": {"name":"初级卫兵","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":0},
"blueGateKeeper": {"name":"中级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"redGateKeeper": {"name":"高级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"magicMaster": {"name":"黑暗大法师","hp":100,"atk":120,"def":0,"money":12,"exp":0,"point":0,"special":11,"value":0.3333333333333333,"add":true,"notBomb":true},
"devilWarrior": {"name":"魔神武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"fairyEnemy": {"name":"仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"dragon": {"name":"魔龙","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0,"bigImage":"dragon_0.png"},
"skeletonKnight": {"name":"骷髅武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"skeletonPresbyter": {"name":"骷髅巫师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"ironRock": {"name":"铁面人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"grayRock": {"name":"灰色石头人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"yellowPriest": {"name":"中级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"evilPrincess": {"name":"痛苦魔女","hp":1000,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[10]},
"blademaster": {"name":"剑圣","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"evilFairy": {"name":"黑暗仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"greenKnight": {"name":"强盾骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"bowman": {"name":"初级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"watcherSlime": {"name":"邪眼怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"devilKnight": {"name":"恶灵骑士","hp":150,"atk":100,"def":50,"money":0,"exp":0,"point":0,"special":[1,5,7,8]},
"grayPriest": {"name":"混沌法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"greenGateKeeper": {"name":"卫兵队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"ghostSoldier": {"name":"冥队长","hp":200,"atk":100,"def":50,"money":0,"exp":0,"point":0,"special":8},
"frostBat": {"name":"寒蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"blackKing": {"name":"黑衣魔王","hp":1000,"atk":500,"def":0,"money":1000,"exp":1000,"point":0,"special":0,"notBomb":true},
"yellowKing": {"name":"黄衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"greenKing": {"name":"青衣武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"redKing": {"name":"红衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"blueKing": {"name":"白衣武士","hp":100,"atk":120,"def":0,"money":17,"exp":0,"point":0,"special":16},
"keiskeiFairy": {"name":"铃兰花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"tulipFairy": {"name":"郁金香花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"purpleBowman": {"name":"高级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":0},
"bearDown": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"},"bigImage":"bear.png"},
"bearLeft": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}},
"bearRight": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}},
"bearUp": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}}
"greenSlime": {"name":"绿头怪","hp":40,"atk":7,"def":1,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"redSlime": {"name":"红头怪","hp":60,"atk":10,"def":1,"money":0,"exp":0,"point":0,"timeCost":null,"special":[],"value":0},
"blackSlime": {"name":"青头怪","hp":80,"atk":18,"def":1,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"slimelord": {"name":"怪王","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"timeCost":null,"special":[]},
"bat": {"name":"小蝙蝠","hp":30,"atk":12,"def":3,"money":2,"exp":0,"point":0,"timeCost":null,"special":[4]},
"bigBat": {"name":"大蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"redBat": {"name":"红蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":[]},
"vampire": {"name":"冥灵魔王","hp":888,"atk":888,"def":888,"money":888,"exp":888,"point":0,"timeCost":null,"special":[],"n":8},
"skeleton": {"name":"骷髅人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"skeletonCaptain": {"name":"骷髅队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"zombie": {"name":"兽人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"zombieKnight": {"name":"兽人武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"rock": {"name":"石头人","hp":50,"atk":50,"def":0,"money":3,"exp":0,"point":0,"timeCost":null,"special":3},
"bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"timeCost":null,"special":[9]},
"redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"brownWizard": {"name":"初级巫师","hp":100,"atk":120,"def":0,"money":16,"exp":0,"point":0,"timeCost":null,"special":15,"value":100,"range":2},
"redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"timeCost":null,"special":15,"value":200,"zoneSquare":true},
"swordsman": {"name":"双手剑士","hp":100,"atk":120,"def":0,"money":6,"exp":0,"point":0,"timeCost":null,"special":4},
"soldier": {"name":"冥战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"yellowKnight": {"name":"金骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"redKnight": {"name":"红骑士","hp":500,"atk":200,"def":50,"money":0,"exp":0,"point":0,"timeCost":null,"special":[7]},
"darkKnight": {"name":"黑骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"blueKnight": {"name":"蓝骑士","hp":100,"atk":120,"def":0,"money":9,"exp":0,"point":0,"timeCost":null,"special":8},
"goldSlime": {"name":"黄头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"poisonSkeleton": {"name":"紫骷髅","hp":50,"atk":60,"def":70,"money":80,"exp":0,"point":0,"timeCost":null,"special":13},
"poisonBat": {"name":"紫蝙蝠","hp":100,"atk":120,"def":0,"money":14,"exp":0,"point":0,"timeCost":null,"special":13},
"skeletonPriest": {"name":"骷髅法师","hp":100,"atk":100,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":18,"value":20},
"skeletonKing": {"name":"骷髅王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"evilHero": {"name":"迷失勇者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"demonPriest": {"name":"魔神法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"goldHornSlime": {"name":"金角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"silverSlime": {"name":"银头怪","hp":100,"atk":120,"def":0,"money":15,"exp":0,"point":0,"timeCost":null,"special":14},
"whiteHornSlime": {"name":"尖角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"redSwordsman": {"name":"剑王","hp":300,"atk":300,"def":2,"money":0,"exp":0,"point":0,"timeCost":null,"special":[28]},
"poisonZombie": {"name":"绿兽人","hp":100,"atk":120,"def":0,"money":13,"exp":0,"point":0,"timeCost":null,"special":[12]},
"octopus": {"name":"血影","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0,"bigImage":"dragon_1.png"},
"princessEnemy": {"name":"假公主","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"angel": {"name":"天使","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"steelGuard": {"name":"铁守卫","hp":50,"atk":50,"def":50,"money":0,"exp":0,"point":0,"timeCost":null,"special":[18],"value":20},
"evilBat": {"name":"邪恶蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"silverSlimelord": {"name":"银怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"goldSlimelord": {"name":"金怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"skeletonWarrior": {"name":"骷髅士兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"whiteSlimeman": {"name":"水银战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"slimeman": {"name":"影子战士","hp":100,"atk":0,"def":0,"money":11,"exp":0,"point":0,"timeCost":null,"special":[9],"atkValue":2,"defValue":3},
"yellowGateKeeper": {"name":"初级卫兵","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"timeCost":null,"special":0},
"blueGateKeeper": {"name":"中级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"redGateKeeper": {"name":"高级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"magicMaster": {"name":"黑暗大法师","hp":100,"atk":120,"def":0,"money":12,"exp":0,"point":0,"timeCost":null,"special":11,"value":0.3333333333333333,"add":true,"notBomb":true},
"devilWarrior": {"name":"魔神武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"fairyEnemy": {"name":"仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"dragon": {"name":"魔龙","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0,"bigImage":"dragon_0.png"},
"skeletonKnight": {"name":"骷髅武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"skeletonPresbyter": {"name":"骷髅巫师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"ironRock": {"name":"铁面人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"grayRock": {"name":"灰色石头人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"yellowPriest": {"name":"中级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"evilPrincess": {"name":"痛苦魔女","hp":1000,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":[10]},
"blademaster": {"name":"剑圣","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"evilFairy": {"name":"黑暗仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"greenKnight": {"name":"强盾骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"bowman": {"name":"初级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"watcherSlime": {"name":"邪眼怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"devilKnight": {"name":"恶灵骑士","hp":150,"atk":100,"def":50,"money":0,"exp":0,"point":0,"timeCost":null,"special":[1,5,7,8]},
"grayPriest": {"name":"混沌法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"greenGateKeeper": {"name":"卫兵队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"ghostSoldier": {"name":"冥队长","hp":200,"atk":100,"def":50,"money":0,"exp":0,"point":0,"timeCost":null,"special":8},
"frostBat": {"name":"寒蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"blackKing": {"name":"黑衣魔王","hp":1000,"atk":500,"def":0,"money":1000,"exp":1000,"point":0,"timeCost":null,"special":0,"notBomb":true},
"yellowKing": {"name":"黄衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"greenKing": {"name":"青衣武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"redKing": {"name":"红衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"blueKing": {"name":"白衣武士","hp":100,"atk":120,"def":0,"money":17,"exp":0,"point":0,"timeCost":null,"special":16},
"keiskeiFairy": {"name":"铃兰花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"tulipFairy": {"name":"郁金香花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"purpleBowman": {"name":"高级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":0},
"bearDown": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"},"bigImage":"bear.png"},
"bearLeft": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}},
"bearRight": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}},
"bearUp": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"timeCost":null,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}}
}

View File

@ -1,31 +1,35 @@
var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
{
"yellowKey": {
"cls": "tools",
"cls": "items",
"timeCost": null,
"name": "黄钥匙",
"text": "可以打开一扇黄门",
"hideInToolbox": true
},
"blueKey": {
"cls": "tools",
"timeCost": null,
"name": "蓝钥匙",
"text": "可以打开一扇蓝门",
"hideInToolbox": true
},
"redKey": {
"cls": "tools",
"timeCost": null,
"name": "红钥匙",
"text": "可以打开一扇红门",
"hideInToolbox": true
},
"greenKey": {
"cls": "tools",
"timeCost": null,
"name": "绿钥匙",
"text": "可以打开一扇绿门",
"hideInToolbox": true
"text": "可以打开一扇绿门"
},
"yellowKey2": {
"cls": "items",
"timeCost": null,
"name": "黄钥匙×2",
"text": "获得两把黄钥匙",
"itemEffect": "core.addItem('yellowKey', 2)",
@ -35,6 +39,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"blueKey2": {
"cls": "items",
"timeCost": null,
"name": "蓝钥匙×2",
"text": "获得两把蓝钥匙",
"itemEffect": "core.addItem('blueKey', 2)",
@ -44,6 +49,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"redKey2": {
"cls": "items",
"timeCost": null,
"name": "红钥匙×2",
"text": "获得两把红钥匙",
"itemEffect": "core.addItem('redKey', 2)",
@ -53,6 +59,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"greenKey2": {
"cls": "items",
"timeCost": null,
"name": "绿钥匙×2",
"text": "获得两把绿钥匙",
"itemEffect": "core.addItem('greenKey', 2)",
@ -62,6 +69,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"yellowKey3": {
"cls": "items",
"timeCost": null,
"name": "黄钥匙×3",
"text": "获得三把黄钥匙",
"itemEffect": "core.addItem('yellowKey', 3)",
@ -71,6 +79,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"blueKey3": {
"cls": "items",
"timeCost": null,
"name": "蓝钥匙×3",
"text": "获得三把蓝钥匙",
"itemEffect": "core.addItem('blueKey', 3)",
@ -80,6 +89,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"redKey3": {
"cls": "items",
"timeCost": null,
"name": "红钥匙×3",
"text": "获得三把红钥匙",
"itemEffect": "core.addItem('redKey', 3)",
@ -89,6 +99,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"greenKey3": {
"cls": "items",
"timeCost": null,
"name": "绿钥匙×3",
"text": "获得三把绿钥匙",
"itemEffect": "core.addItem('greenKey', 3)",
@ -98,6 +109,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"redGem": {
"cls": "items",
"timeCost": null,
"name": "红宝石",
"text": "攻击+${core.values.redGem}",
"itemEffect": "core.status.hero.atk += core.values.redGem * core.status.thisMap.ratio",
@ -107,6 +119,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"blueGem": {
"cls": "items",
"timeCost": null,
"name": "蓝宝石",
"text": ",防御+${core.values.blueGem}",
"itemEffect": "core.status.hero.def += core.values.blueGem * core.status.thisMap.ratio",
@ -116,6 +129,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"greenGem": {
"cls": "items",
"timeCost": null,
"name": "绿宝石",
"text": ",护盾+${core.values.greenGem}",
"itemEffect": "core.status.hero.mdef += core.values.greenGem * core.status.thisMap.ratio",
@ -125,6 +139,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"yellowGem": {
"cls": "items",
"timeCost": null,
"name": "黄宝石",
"text": ",生命+${core.values.yellowGem},生命上限+${core.values.yellowGem}",
"itemEffect": "core.status.hero.hp += core.values.yellowGem * core.status.thisMap.ratio; core.status.hero.hpmax += core.values.yellowGem * core.status.thisMap.ratio",
@ -134,6 +149,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"redPotion": {
"cls": "items",
"timeCost": null,
"name": "红血瓶",
"text": ",生命+${core.values.redPotion}",
"itemEffect": "core.status.hero.hp += core.values.redPotion * core.status.thisMap.ratio",
@ -143,6 +159,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"bluePotion": {
"cls": "items",
"timeCost": null,
"name": "蓝血瓶",
"text": ",生命+${core.values.bluePotion}",
"itemEffect": "core.status.hero.hp += core.values.bluePotion * core.status.thisMap.ratio",
@ -152,6 +169,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"yellowPotion": {
"cls": "items",
"timeCost": null,
"name": "黄血瓶",
"text": ",生命+${core.values.yellowPotion}",
"itemEffect": "core.status.hero.hp += core.values.yellowPotion * core.status.thisMap.ratio",
@ -161,6 +179,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"greenPotion": {
"cls": "items",
"timeCost": null,
"name": "绿血瓶",
"text": ",生命+${core.values.greenPotion}",
"itemEffect": "core.status.hero.hp += core.values.greenPotion * core.status.thisMap.ratio",
@ -170,6 +189,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"redPotion2": {
"cls": "items",
"timeCost": null,
"name": "大红血瓶",
"text": ",生命+${core.values.redPotion * 20}",
"itemEffect": "core.status.hero.hp += core.values.redPotion * 20 * core.status.thisMap.ratio",
@ -179,6 +199,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"bluePotion2": {
"cls": "items",
"timeCost": null,
"name": "大蓝血瓶",
"text": ",生命+${core.values.bluePotion * 20}",
"itemEffect": "core.status.hero.hp += core.values.bluePotion * 20 * core.status.thisMap.ratio",
@ -188,6 +209,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"yellowPotion2": {
"cls": "items",
"timeCost": null,
"name": "大黄血瓶",
"text": ",生命+${core.values.yellowPotion * 20}",
"itemEffect": "core.status.hero.hp += core.values.yellowPotion * 20 * core.status.thisMap.ratio",
@ -197,6 +219,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"greenPotion2": {
"cls": "items",
"timeCost": null,
"name": "大绿血瓶",
"text": ",生命+${core.values.greenPotion * 20}",
"itemEffect": "core.status.hero.hp += core.values.greenPotion * 20 * core.status.thisMap.ratio",
@ -206,6 +229,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"sword0": {
"cls": "items",
"timeCost": null,
"name": "破旧的剑",
"text": "一把已经生锈的剑",
"equip": {
@ -220,6 +244,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"sword1": {
"cls": "items",
"timeCost": null,
"name": "铁剑",
"text": "一把很普通的铁剑",
"equip": {
@ -234,6 +259,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"sword2": {
"cls": "items",
"timeCost": null,
"name": "银剑",
"text": "一把很普通的银剑",
"equip": {
@ -248,6 +274,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"sword3": {
"cls": "items",
"timeCost": null,
"name": "骑士剑",
"text": "一把很普通的骑士剑",
"equip": {
@ -262,6 +289,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"sword4": {
"cls": "items",
"timeCost": null,
"name": "圣剑",
"text": "一把很普通的圣剑",
"equip": {
@ -276,6 +304,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"sword5": {
"cls": "items",
"timeCost": null,
"name": "神圣剑",
"text": "一把很普通的神圣剑",
"equip": {
@ -290,6 +319,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"shield0": {
"cls": "items",
"timeCost": null,
"name": "破旧的盾",
"text": "一个很破旧的铁盾",
"equip": {
@ -303,6 +333,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"shield1": {
"cls": "items",
"timeCost": null,
"name": "铁盾",
"text": "一个很普通的铁盾",
"equip": {
@ -316,6 +347,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"shield2": {
"cls": "items",
"timeCost": null,
"name": "银盾",
"text": "一个很普通的银盾",
"equip": {
@ -329,6 +361,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"shield3": {
"cls": "items",
"timeCost": null,
"name": "骑士盾",
"text": "一个很普通的骑士盾",
"equip": {
@ -342,6 +375,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"shield4": {
"cls": "items",
"timeCost": null,
"name": "圣盾",
"text": "一个很普通的圣盾",
"equip": {
@ -355,6 +389,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"shield5": {
"cls": "items",
"timeCost": null,
"name": "神圣盾",
"text": "一个很普通的神圣盾",
"equip": {
@ -369,6 +404,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"superPotion": {
"cls": "items",
"timeCost": null,
"name": "圣水",
"itemEffect": "core.status.hero.hp *= 2",
"itemEffectTip": ",生命值翻倍",
@ -378,6 +414,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"book": {
"cls": "constants",
"timeCost": null,
"name": "怪物手册",
"text": "可以查看当前楼层各怪物属性",
"hideInToolbox": true,
@ -386,6 +423,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"fly": {
"cls": "constants",
"timeCost": null,
"name": "楼层传送器",
"text": "可以自由往来去过的楼层",
"hideInReplay": true,
@ -395,11 +433,13 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"coin": {
"cls": "constants",
"timeCost": null,
"name": "幸运金币",
"text": "持有时打败怪物可得双倍金币"
},
"freezeBadge": {
"cls": "constants",
"timeCost": null,
"name": "冰冻徽章",
"text": "可以将面前的熔岩变成平地",
"useItemEffect": "(function () {\n\tvar success = false;\n\n\tvar snowFourDirections = false; // 是否多方向雪花如果是将其改成true\n\tif (snowFourDirections) {\n\t\t// 多方向雪花\n\t\tfor (var direction in core.utils.scan) { // 多方向雪花默认四方向如需改为八方向请将这两个scan改为scan2\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (core.getBlockId(nx, ny) == 'lava') {\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (core.getBlockId(core.nextX(), core.nextY()) == 'lava') {\n\t\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\n\tif (success) {\n\t\tcore.playSound('打开界面');\n\t\tcore.drawTip(core.material.items[itemId].name + '使用成功', itemId);\n\t} else {\n\t\tcore.playSound('操作失败');\n\t\tcore.drawTip(\"当前无法使用\" + core.material.items[itemId].name, itemId);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n})();",
@ -407,21 +447,25 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"cross": {
"cls": "constants",
"timeCost": null,
"name": "十字架",
"text": "持有后无视怪物的无敌属性"
},
"dagger": {
"cls": "constants",
"timeCost": null,
"name": "屠龙匕首",
"text": "该道具尚未被定义"
},
"amulet": {
"cls": "constants",
"timeCost": null,
"name": "护符",
"text": "持有时无视负面地形"
},
"bigKey": {
"cls": "tools",
"timeCost": null,
"name": "大黄门钥匙",
"text": "可以开启当前层所有黄门",
"itemEffect": "core.addItem('yellowKey', 1);\ncore.addItem('blueKey', 1);\ncore.addItem('redKey', 1);",
@ -429,18 +473,15 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
"useItemEffect": "(function () {\n\tvar actions = core.searchBlock(\"yellowDoor\").map(function (block) {\n\t\treturn { \"type\": \"openDoor\", \"loc\": [block.x, block.y], \"async\": true };\n\t});\n\tactions.push({ \"type\": \"waitAsync\" });\n\tactions.push({ \"type\": \"tip\", \"text\": core.material.items[itemId].name + \"使用成功\" });\n\tcore.insertAction(actions);\n})();",
"canUseItemEffect": "(function () {\n\treturn core.searchBlock('yellowDoor').length > 0;\n})();"
},
"greenKey": {
"cls": "tools",
"name": "绿钥匙",
"text": "可以打开一扇绿门"
},
"steelKey": {
"cls": "tools",
"timeCost": null,
"name": "铁门钥匙",
"text": "可以打开一扇铁门"
},
"pickaxe": {
"cls": "tools",
"timeCost": null,
"name": "破墙镐",
"text": "可以破坏勇士面前的墙",
"useItemEffect": "(function () {\n\tvar canBreak = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.disable) return false;\n\t\treturn block.event.canBreak;\n\t};\n\n\tvar success = false;\n\tvar pickaxeFourDirections = false; // 是否多方向破如果是将其改成true\n\tif (pickaxeFourDirections) {\n\t\t// 多方向破\n\t\tfor (var direction in core.utils.scan) { // 多方向破默认四方向如需改成八方向请将这两个scan改为scan2\n\t\t\tvar delta = core.utils.scan[direction];\n\t\t\tvar nx = core.getHeroLoc('x') + delta.x,\n\t\t\t\tny = core.getHeroLoc('y') + delta.y;\n\t\t\tif (canBreak(nx, ny)) {\n\t\t\t\tcore.removeBlock(nx, ny);\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// 仅破当前\n\t\tif (canBreak(core.nextX(), core.nextY())) {\n\t\t\tcore.removeBlock(core.nextX(), core.nextY());\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\n\tif (success) {\n\t\tcore.playSound('破墙镐');\n\t\tcore.drawTip(core.material.items[itemId].name + '使用成功', itemId);\n\t} else {\n\t\t// 无法使用\n\t\tcore.playSound('操作失败');\n\t\tcore.drawTip(\"当前无法使用\" + core.material.items[itemId].name, itemId);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n})();",
@ -448,6 +489,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"icePickaxe": {
"cls": "tools",
"timeCost": null,
"name": "破冰镐",
"text": "可以破坏勇士面前的一堵冰墙",
"useItemEffect": "(function () {\n\tcore.drawTip(core.material.items[itemId].name + '使用成功', itemId);\n\tcore.insertAction({ \"type\": \"openDoor\", \"loc\": [\"core.nextX()\", \"core.nextY()\"] });\n})();",
@ -455,6 +497,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"bomb": {
"cls": "tools",
"timeCost": null,
"name": "炸弹",
"text": "可以炸掉勇士面前的怪物",
"useItemEffect": "(function () {\n\tvar bombList = []; // 炸掉的怪物坐标列表\n\tvar todo = []; // 炸弹后事件\n\tvar money = 0,\n\t\texp = 0; // 炸弹获得的金币和经验\n\n\tvar canBomb = function (x, y) {\n\t\tvar block = core.getBlock(x, y);\n\t\tif (block == null || block.disable || block.event.cls.indexOf('enemy') != 0) return false;\n\t\tvar enemy = core.material.enemys[block.event.id];\n\t\treturn enemy && !enemy.notBomb;\n\t};\n\n\tvar bomb = function (x, y) {\n\t\tif (!canBomb(x, y)) return;\n\t\tbombList.push([x, y]);\n\t\tvar id = core.getBlockId(x, y),\n\t\t\tenemy = core.material.enemys[id];\n\t\tmoney += core.getEnemyValue(enemy, 'money', x, y) || 0;\n\t\texp += core.getEnemyValue(enemy, 'exp', x, y) || 0;\n\t\tcore.push(todo, core.floors[core.status.floorId].afterBattle[x + \",\" + y]);\n\t\tcore.push(todo, enemy.afterBattle);\n\t\tcore.removeBlock(x, y);\n\t}\n\n\t// 如果要多方向可炸把这里的false改成true\n\tif (false) {\n\t\tvar scan = core.utils.scan; // 多方向炸时默认四方向,如果要改成八方向炸可以改成 core.utils.scan2\n\t\tfor (var direction in scan) {\n\t\t\tvar delta = scan[direction];\n\t\t\tbomb(core.getHeroLoc('x') + delta.x, core.getHeroLoc('y') + delta.y);\n\t\t}\n\t} else {\n\t\t// 仅炸当前\n\t\tbomb(core.nextX(), core.nextY());\n\t}\n\n\tif (bombList.length == 0) {\n\t\tcore.playSound('操作失败');\n\t\tcore.drawTip('当前无法使用' + core.material.items[itemId].name, itemId);\n\t\tcore.addItem(itemId, 1);\n\t\treturn;\n\t}\n\n\tcore.playSound('炸弹');\n\tcore.drawTip(core.material.items[itemId].name + '使用成功', itemId);\n\n\t// 取消这里的注释可以炸弹后获得金币和经验\n\t// core.status.hero.money += money;\n\t// core.status.hero.exp += exp;\n\n\t// 取消这里的注释可以炸弹引发战后事件\n\t// if (todo.length > 0) core.insertAction(todo);\n\n})();",
@ -462,6 +505,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"centerFly": {
"cls": "tools",
"timeCost": null,
"name": "中心对称飞行器",
"text": "可以飞向当前楼层中心对称的位置",
"useItemEffect": "core.playSound('centerFly.mp3');\ncore.clearMap('hero');\ncore.setHeroLoc('x', core.bigmap.width - 1 - core.getHeroLoc('x'));\ncore.setHeroLoc('y', core.bigmap.height - 1 - core.getHeroLoc('y'));\ncore.drawHero();\ncore.drawTip(core.material.items[itemId].name + '使用成功');",
@ -469,6 +513,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"upFly": {
"cls": "tools",
"timeCost": null,
"name": "上楼器",
"text": "可以飞往楼上的相同位置",
"useItemEffect": "(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) + 1];\n\tif (core.status.event.id == 'action') {\n\t\tcore.insertAction([\n\t\t\t{ \"type\": \"changeFloor\", \"loc\": [core.getHeroLoc('x'), core.getHeroLoc('y')], \"floorId\": floorId },\n\t\t\t{ \"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();",
@ -476,6 +521,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"downFly": {
"cls": "tools",
"timeCost": null,
"name": "下楼器",
"text": "可以飞往楼下的相同位置",
"useItemEffect": "(function () {\n\tvar floorId = core.floorIds[core.floorIds.indexOf(core.status.floorId) - 1];\n\tif (core.status.event.id == 'action') {\n\t\tcore.insertAction([\n\t\t\t{ \"type\": \"changeFloor\", \"loc\": [core.getHeroLoc('x'), core.getHeroLoc('y')], \"floorId\": floorId },\n\t\t\t{ \"type\": \"tip\", \"text\": core.material.items[itemId].name + '使用成功' }\n\t\t]);\n\t} else {\n\t\tcore.changeFloor(floorId, null, core.status.hero.loc, null, function () {\n\t\t\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n\t\t\tcore.replay();\n\t\t});\n\t}\n})();",
@ -483,6 +529,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"earthquake": {
"cls": "tools",
"timeCost": null,
"name": "地震卷轴",
"text": "可以破坏当前层的所有墙",
"useItemEffect": "(function () {\n\tvar indexes = [];\n\tfor (var index in core.status.thisMap.blocks) {\n\t\tvar block = core.status.thisMap.blocks[index];\n\t\tif (!block.disable && block.event.canBreak) {\n\t\t\tindexes.push(index);\n\t\t}\n\t}\n\tcore.removeBlockByIndexes(indexes);\n\tcore.redrawMap();\n\tcore.playSound('炸弹');\n\tcore.drawTip(core.material.items[itemId].name + '使用成功');\n})();",
@ -490,6 +537,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"poisonWine": {
"cls": "tools",
"timeCost": null,
"name": "解毒药水",
"text": "可以解除中毒状态",
"useItemEffect": "core.triggerDebuff('remove', 'poison');",
@ -497,6 +545,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"weakWine": {
"cls": "tools",
"timeCost": null,
"name": "解衰药水",
"text": "可以解除衰弱状态",
"useItemEffect": "core.triggerDebuff('remove', 'weak');",
@ -504,6 +553,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"curseWine": {
"cls": "tools",
"timeCost": null,
"name": "解咒药水",
"text": "可以解除诅咒状态",
"useItemEffect": "core.triggerDebuff('remove', 'curse');",
@ -511,6 +561,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"superWine": {
"cls": "tools",
"timeCost": null,
"name": "万能药水",
"text": "可以解除所有不良状态",
"useItemEffect": "core.triggerDebuff('remove', ['poison', 'weak', 'curse']);",
@ -518,11 +569,13 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"hammer": {
"cls": "tools",
"timeCost": null,
"name": "圣锤",
"text": "该道具尚未被定义"
},
"lifeWand": {
"cls": "tools",
"timeCost": null,
"name": "生命魔杖",
"text": "可以恢复100点生命值",
"useItemEvent": [
@ -581,6 +634,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"jumpShoes": {
"cls": "tools",
"timeCost": null,
"name": "跳跃靴",
"text": "能跳跃到前方两格处",
"useItemEffect": "core.playSound(\"跳跃\");\ncore.insertAction({ \"type\": \"jumpHero\", \"loc\": [core.nextX(2), core.nextY(2)] });",
@ -588,6 +642,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"skill1": {
"cls": "constants",
"timeCost": null,
"name": "技能:二倍斩",
"text": "可以打开或关闭主动技能二倍斩",
"hideInReplay": true,
@ -596,10 +651,12 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a =
},
"wand": {
"cls": "items",
"timeCost": null,
"name": "新物品"
},
"pack": {
"cls": "items",
"timeCost": null,
"name": "钱袋",
"itemEffect": "core.status.hero.money += 500",
"itemEffectTip": ",金币+500"

View File

@ -2656,6 +2656,107 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 =
}
}
},
"mapTurn": function () {
var __enable = false;
function ensureFlags() {
var m = core.getFlag("mapTurnState");
if (m == null || typeof m !== "object") {
core.setFlag("mapTurnState", {
clock: 0,
mapTurn: 0,
enemyActionGauge: {},
activeEnemiesByFloor: {},
activeEnemiesVersion: 0
});
} else {
if (m.clock == null) m.clock = 0;
if (m.mapTurn == null) m.mapTurn = 0;
if (!m.enemyActionGauge) m.enemyActionGauge = {};
if (!m.activeEnemiesByFloor) m.activeEnemiesByFloor = {};
if (m.activeEnemiesVersion == null) m.activeEnemiesVersion = 0;
}
var sk = core.getFlag("skillState");
if (sk == null || typeof sk !== "object") {
core.setFlag("skillState", { activeStatusSkills: {}, activeBattleSkillId: null, temp: {} });
} else {
if (!sk.activeStatusSkills) sk.activeStatusSkills = {};
if (sk.activeBattleSkillId === undefined) sk.activeBattleSkillId = null;
if (!sk.temp) sk.temp = {};
}
}
this.mapTurn = {
isEnabled: function () {
return __enable;
},
setEnabled: function (v) {
__enable = !!v;
},
consumeTime: function (deltaTime, reason) {
if (!__enable || !deltaTime || deltaTime <= 0) return;
ensureFlags();
var n = Math.floor(deltaTime);
var s = core.getFlag("mapTurnState");
s.clock = (s.clock || 0) + n;
for (var i = 0; i < n; i++)
this.advanceMapTurnOne(reason);
},
advanceMapTurnOne: function (reason) {
if (!__enable) return;
ensureFlags();
var s = core.getFlag("mapTurnState");
s.mapTurn = (s.mapTurn || 0) + 1;
this.resolveEnemyActionsForSingleTick(reason);
},
resolveEnemyActionsForSingleTick: function (reason) {
/* 占位:按 docs/map-turn-spec.md §6.2 接入 activeEnemies 与怪物行动 */
},
rebuildActiveEnemies: function (floorId) {
ensureFlags();
var s = core.getFlag("mapTurnState");
floorId = floorId || core.status.floorId;
if (!floorId) return;
if (!s.activeEnemiesByFloor) s.activeEnemiesByFloor = {};
s.activeEnemiesByFloor[floorId] = [];
s.activeEnemiesVersion = (s.activeEnemiesVersion || 0) + 1;
},
patchActiveEnemiesForBlockChange: function (floorId, hint) {
ensureFlags();
},
settleBattleTimeCost: function () {
ensureFlags();
var baseBattleTimeCost = 1;
var statusBattleTimeCostMax = 0;
var skills = core.getFlag("skillState").activeStatusSkills || {};
for (var skillId in skills) {
var st = skills[skillId];
var v = st && st.battleTimeCost;
if (typeof v === "number" && v > statusBattleTimeCostMax) statusBattleTimeCostMax = v;
}
return Math.max(baseBattleTimeCost, statusBattleTimeCostMax);
},
applyStatusAfterBattle: function (battleResult) {
if (battleResult !== "success") return;
ensureFlags();
var skills = core.getFlag("skillState").activeStatusSkills || {};
for (var skillId in skills) {
var state = skills[skillId];
if (typeof state.remainBattles === "number") {
state.remainBattles -= 1;
if (state.remainBattles <= 0) delete skills[skillId];
}
}
},
clearOnDeath: function () {
ensureFlags();
var ss = core.getFlag("skillState");
ss.temp = {};
ss.activeBattleSkillId = null;
ss.activeStatusSkills = {};
}
};
}
}
}