ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

WarriorJS 塔楼攻略:深入 The Powder Keep——9 层倒计时炸弹与全向战斗的编程挑战

WarriorJS 塔楼攻略:深入 The Powder Keep——9 层倒计时炸弹与全向战斗的编程挑战 教育CLI【免费下载链接】warriorjs An exciting game of programming and Artificial Intelligence项目地址https://gitcode.com/gh_mirrors/wa/warriorjs点击查看免费下载The Powder Keep火药要塞是 WarriorJS 中难度更高的一座官方塔楼共 9 层。它讲述了一座被遗弃却未被清空的古老堡垒地板之下传来微弱的滴答声你必须在二维空间中穿越开阔房间、倾听声音定位目标、束缚敌人并在倒计时炸弹爆炸前救出人质。读完本文你将掌握该塔的安装方式、塔楼数据模型、9 层地图与能力配置、Ticking 爆炸机制、Bind/Rescue/Detonate 等核心能力的底层实现并得到每层可直接改写的参考战术代码。塔楼背景与难度定位在 WarriorJS 中塔楼Tower是独立发布的 npm 包为游戏提供一组全新的关卡、能力与单位。仓库的 towers/README.md 明确说明了两个官方塔楼的分工The Narrow Path入门塔楼建议先玩The Powder Keep更具挑战性的进阶塔楼。The Powder Keep 的主题设定写在其包描述中——An old fortress where something ticks beneath the floor一座地板下有什么在滴答作响的古老堡垒。其完整背景故事与关卡配置全部定义在 towers/the-powder-keep/src/index.ts 中堡垒早已被废弃但从未被搬空房间空旷而黑暗深处的墙壁中传来如第二心跳般微弱的滴答声。在这里并非所有东西都能靠战斗解决——有些东西只能靠逃跑或及时解救来应对。该塔的核心理念可概括为三句话9 层渐进式教学从找楼梯起步逐步引入四方向战斗、束缚、听觉定位、倒计时炸弹与爆破清场威胁来自四面八方楼层是二维的敌人可能出现在前后左右任意方向时间即生命从第 6 层开始Ticking滴答效果让部分人质携带倒计时炸弹超时即全员爆炸。安装与启动The Powder Keep 作为独立包与 CLI 一起使用。仓库中 towers/the-powder-keep/package.json 显示其包名为warriorjs/tower-the-powder-keep当前版本 0.13.0keyword 为warriorjs-tower要求 Node.js 24。官方文档给出的安装方式有两种。全局安装与 CLI 一同全局安装npm install --global warriorjs/tower-the-powder-keep本地安装npm install warriorjs/tower-the-powder-keep安装完成后运行warriorjs在塔楼选择界面选中The Powder Keep即可开始。关于CLI 如何发现这座塔可以从 apps/cli/src/loadTowers.ts 的实现中得到印证warriorjs/tower-the-narrow-path被硬编码为内置塔楼internalTowerPackageNames开箱即用其余官方塔与社区塔通过扫描node_modules中的warriorjs/tower-*/package.json与warriorjs-tower-*/package.json发现因此The Powder Keep 必须显式安装否则 CLI 无法列出它发现到的每个包通过require加载其默认导出构造成Tower实例。也就是说只安装warriorjs/cli并不会自带 The Powder Keep这条安装命令是开始游戏的前置条件。塔楼数据模型一份 9 层配置如何被读取整座塔的全部关卡数据都集中定义在 towers/the-powder-keep/src/index.ts导出为一个TowerDefinition对象。对照 libs/core/src/types.ts 中的类型定义可以看到这个数据模型的完整结构interface TowerDefinition { name: string; description: string; warrior: WarriorDefinition; // 塔级默认属性如 maxHealth levels: LevelDefinition[]; // 每层配置 } interface LevelDefinition { description: string; // 楼层叙事文本 tip: string; // 玩法提示游戏内可见 clue?: string; // 进阶线索面向想拿 Ace 评分的玩家 timeBonus: number; // 时间奖励上限 aceScore: number; // 达成 Ace 评价的目标分数 floor: { size: Size; // 楼层宽高 stairs: LocationConfig; // 楼梯坐标 warrior: WarriorOverrides; // 出生点、朝向、本层新增能力 units: UnitConfig[]; // 单位布局含位置、朝向、效果 }; }The Powder Keep 在塔级设置了warrior.maxHealth: 20全塔战士生命上限为 20。每一层通过WarriorOverrides只声明本层新增或叠加的能力与出生位置单位则通过UnitConfig给出类型如Sludge、ThickSludge、Captive、坐标与朝向并可携带effects如ticking: Ticking.with({ time: 7 })。一个值得注意的结构事实是从第 3 层起各层warrior.abilities中只列新解锁的能力如第 3 层仅bind与rescue第 5 层甚至完全不声明 abilities而各层 clue 又会引用先前层的能力例如第 7 层的 clue 要求bind 周围的敌人。由此可以推断塔内能力随关卡进度累积——后一关继承前一关已解锁的全部能力未显式声明的关卡沿用已有能力集。这也解释了为什么第 5 关可以放心使用第 2 关解锁的feel()与第 1 关解锁的walk()。全层攻略9 层地图、能力与参考代码下面按顺序逐层展开。玩家代码统一写在Player.playTurn(warrior)中能力调用形式为warrior.walk()、warrior.feel()、warrior.attack()等方向使用相对方向forward、right、backward、left。各层核心参数先汇总如下层楼层尺寸出生点朝向楼梯新增能力timeBonusaceScore16×4(0,1) EAST(2,3)directionOfStairs、think、walk201924×2(0,0) EAST(3,1)attack、feel、health、maxHealth、rest408433×3(1,1) EAST(0,0)bind、rescue5010144×3(1,1) EAST(3,2)directionOf、listen5514455×2(0,1) EAST(1,1)无沿用既有能力4510766×2(0,1) EAST(5,0)无首次出现 Ticking5010875×3(0,1) EAST(4,0)无Ticking 升级7013487×1(0,0) EAST(6,0)detonate、look309194×3(0,1) EAST(3,0)distanceOf70176第 1 层学会读地图6×4 空旷房间楼梯在 (2,3)出生点 (0,1) 面向东。本层无任何敌人解锁directionOfStairs、think、walk三个能力。目标是让玩家建立先感知、再移动的基本循环class Player { playTurn(warrior) { warrior.walk(warrior.directionOfStairs()); } }directionOfStairs()返回指向楼梯的绝对方向walk()接受该方向并移动一步。think()则用于在游戏日志中打印调试信息。这一层 20 秒的时间奖励与 19 分的 Ace 分数线都很低属于纯粹的预热。第 2 层四方向威胁与低血量管理4×2 房间出生点 (0,0) 面向东楼梯 (3,1)。解锁attack、feel、health、maxHealth、rest。单位布局Sludge 位于 (1,0)面向西ThickSludge 位于 (2,1)面向西Sludge 位于 (1,1)面向北。官方提示强调威胁可能来自任何方向因为出生点周围的前、右、后三个方向都可能出现敌人。本层 clue 给出的完整决策树是用feel()检查每个方向是否有敌人有则攻击四周无敌人且血量偏低时用rest()回血。attack在本层被配置为Attack.with({ power: 5 })即每次攻击造成 5 点伤害rest被配置为Rest.with({ healthGain: 0.1 })。查看 libs/abilities/src/Rest.ts 的实现可知每回合回复量按Math.round(maxHealth * healthGain)计算——20 点最大生命对应每回合回复 2 点且满血时rest()不会产生任何效果。class Player { playTurn(warrior) { const dirs [forward, right, backward, left]; const enemy dirs.find((dir) warrior.feel(dir).getUnit()?.isEnemy()); if (enemy) { warrior.attack(enemy); } else if (warrior.health() warrior.maxHealth()) { warrior.rest(); } else { warrior.walk(); } } }第 3 层包围战与束缚的艺术3×3 房间出生点位于正中心 (1,1)楼梯 (0,0)。解锁bind与rescue。周围四格中三个方向是 Sludge另一个方向是人质 CaptiveSludge (1,0)、Sludge (0,1)、Sludge (2,1)Captive (1,2)。clue 的指令非常明确数一数周围未被束缚的敌人数量如果两个或以上就bind()其中一个。 原因在于同时被三个敌人围攻时逐一攻击会付出大量生命代价而bind()可以让一个敌人原地罚站从 libs/abilities/src/Bind.ts 的实现看绑定作用于指定方向的单位将其receiver.bind()使其无法移动。人质则需要走到相邻格后用rescue()解救见 libs/abilities/src/Rescue.ts它只对处于绑定状态的单位生效并调用unit.release()。class Player { playTurn(warrior) { const dirs [forward, right, backward, left]; const enemies dirs.filter((dir) { const unit warrior.feel(dir).getUnit(); return unit unit.isEnemy() !unit.isBound(); }); if (enemies.length 2) { warrior.bind(enemies[0]); } else { const captiveDir dirs.find((dir) { const unit warrior.feel(dir).getUnit(); return unit !unit.isEnemy() !unit.isBound(); }); if (captiveDir) { warrior.rescue(captiveDir); } else if (enemies.length 1) { warrior.attack(enemies[0]); } else if (warrior.health() warrior.maxHealth()) { warrior.rest(); } else { warrior.walk(); } } } }第 4 层用耳朵代替眼睛4×3 房间出生点 (1,1) 面向东楼梯 (3,2)。解锁listen与directionOf。布局中有 2 名人质、3 名敌人Captive (0,0)、Captive (0,2)、Sludge (2,0)、ThickSludge (3,1)、Sludge (2,2)。本层的核心是听觉感知。listen()返回一个数组包含所有存在单位除自己外的空间见 libs/abilities/src/Listen.ts 的实现它遍历其他单位并换算为相对位置directionOf(space)则返回指向某个空间的方向见 libs/abilities/src/DirectionOf.ts。clue 给出了经典套路朝listen()[0]的方向前进直到listen().length 0再去找楼梯。class Player { playTurn(warrior) { const target warrior.listen()[0]; if (target) { warrior.walk(warrior.directionOf(target)); } else { warrior.walk(warrior.directionOfStairs()); } } }第 5 层楼梯就在身边但良心不允许5×2 房间出生点 (0,1) 面向东楼梯 (1,1)——就在出生点旁边。本层不声明任何新能力。布局ThickSludge (4,0)、ThickSludge (3,1)、Captive (4,1)。设计的张力在于直接上楼梯很简单但清空楼层得分更高。clue 给出了绕行思路如果走向某个单位的方向与去楼梯的方向相同就先朝其他空方向移动直到能安全地朝敌人推进。feel()返回的 Space 对象提供isStairs()与isEmpty()两个判断方法这正是本层 tip 强调的工具。class Player { playTurn(warrior) { const stairsDir warrior.directionOfStairs(); const inStairsDir warrior.feel(stairsDir).getUnit(); if (inStairsDir inStairsDir.isEnemy()) { // 楼梯方向被挡先找空位绕行 const detour [forward, right, backward, left].find( (dir) dir ! stairsDir warrior.feel(dir).isEmpty(), ); warrior.walk(detour); } else { // 安全时优先清剿 const target warrior.listen()[0]; if (target) { warrior.walk(warrior.directionOf(target)); } else { warrior.walk(stairsDir); } } } }第 6 层第一次倒计时6×2 房间出生点 (0,1) 面向东楼梯 (5,0)。本层首次引入Ticking效果位于 (4,1) 的人质携带Ticking.with({ time: 7 })。另外还有 Captive (0,0)、Sludge (1,0)面向西与 Sludge (3,1)面向西。Ticking 效果的实现位于 libs/effects/src/Ticking.ts每个回合passTurn()将time减 1并发射tick事件当time归零时调用trigger()发射explode事件描述为爆炸、天花板坍塌、杀死所有单位随后让包括自己在内的所有单位承受等于各自当前生命的伤害——也就是说倒计时结束等于全员阵亡游戏直接失败。因此本层 tip 直指要害用space.getUnit().isUnderEffect(ticking)先救下滴答作响的人质——他们撑不了多久。 参考战术是先用listen()过滤出携带 ticking 效果的目标优先朝它移动如果路径被 Sludge 挡住需要先清掉挡路者。class Player { playTurn(warrior) { const ticking warrior.listen().filter((space) space.getUnit()?.isUnderEffect(ticking), ); if (ticking.length) { const dir warrior.directionOf(ticking[0]); const unit warrior.feel(dir).getUnit(); if (unit unit.isEnemy()) { warrior.attack(dir); } else if (unit !unit.isEnemy()) { warrior.rescue(dir); } else { warrior.walk(dir); } } else { warrior.walk(warrior.directionOfStairs()); } } }第 7 层滴答声更急了但前路被堵5×3 房间出生点 (0,1) 面向东楼梯 (4,0)。单位更多Sludge (1,0)面向南、Sludge (1,2)面向北、Captive (2,1)、携带Ticking.with({ time: 10 })的 Captive (4,1)面向西、Captive (2,0)。clue 给出的顺序是先定位滴答人质的方向清掉挡在路径上的敌人必要时先bind()周围的敌人再出手。由于此时已累积了attack、bind、listen、directionOf等全部前期能力本层是一道综合应用题时间管理 清障 束缚三件事同时发生。class Player { playTurn(warrior) { const ticking warrior.listen().filter((space) space.getUnit()?.isUnderEffect(ticking), ); if (ticking.length) { const dir warrior.directionOf(ticking[0]); const space warrior.feel(dir); if (space.getUnit()?.isEnemy()) { warrior.attack(dir); } else if (space.isEmpty()) { warrior.walk(dir); } else { warrior.rescue(dir); } } else { warrior.walk(warrior.directionOfStairs()); } } }第 8 层一条直线上的爆破教学7×1 单行房间出生点 (0,0) 面向东楼梯 (6,0)。解锁detonate与look这是本塔第一次也是唯一一次引入爆炸物主题的玩法。布局ThickSludge (2,0)面向西、Sludge (3,0)面向西、携带Ticking.with({ time: 9 })的 Captive (5,0)。配置参数见 towers/the-powder-keep/src/index.tsdetonate: Detonate.with({ targetPower: 8, surroundingPower: 4 })look: Look.with({ range: 3 })。look()返回前方最多range个空间遇墙截断用于侦察libs/abilities/src/Look.ts。detonate()的实现见 libs/abilities/src/Detonate.ts其伤害半径很有讲究目标格受到targetPower8 点伤害周围 4 个格子受到surroundingPower4 点伤害这四个格子分别是前方 1 格右 1 格前方 1 格左 1 格前方 2 格以及自己所在的格子偏移量[1,1]、[1,-1]、[2,0]、[0,0]。这意味着detonate()是一把双刃剑它会自伤 4 点也可能波及两侧的人质。更危险的是源码中的一个关键分支——如果爆炸命中的单位正处于ticking效果之下会立即触发其triggerEffect(ticking)引发连锁爆炸导致全灭。所以永远不要在人质尤其是滴答人质附近使用 detonate。clue 建议look()返回的数组中如果前两个格子里都是敌人就放心引爆炸弹。本层敌军集中在 (2,0) 与 (3,0)正好落在爆破半径内而滴答人质在 (5,0)处于安全距离。class Player { playTurn(warrior) { const spaces warrior.look(); // 前方最多 3 格 const first spaces[0]?.getUnit(); const second spaces[1]?.getUnit(); if (first?.isEnemy() second?.isEnemy()) { warrior.detonate(); // 集群敌人一炮清掉 } else if (first?.isEnemy()) { warrior.attack(); } else { const ticking warrior.listen().filter((space) space.getUnit()?.isUnderEffect(ticking), ); if (ticking.length) { const dir warrior.directionOf(ticking[0]); const unit warrior.feel(dir).getUnit(); if (unit !unit.isEnemy()) { warrior.rescue(dir); } else { warrior.walk(dir); } } else { warrior.walk(warrior.directionOfStairs()); } } } }第 9 层终局——数量碾压与精确爆破4×3 房间出生点 (0,1) 面向东楼梯 (3,0)。解锁最后一个能力distanceOf。本层是全塔最密集的战场7 个 Sludge 分布在 (0,0)、(1,0)、(1,1)、(2,1)、(3,1)、(0,2)、(1,2)另有 Captive (2,0)携带Ticking.with({ time: 20 })面向南与 Captive (2,2)面向北。distanceOf(space)返回战士到指定空间的曼哈顿距离libs/abilities/src/DistanceOf.ts本层的 tip 点出了它的用法用warrior.distanceOf()让火焰远离你要救的人。 clue 则给出两步走先bind()周围敌人再开打引爆炸药前检查自己的血量。由于第 8 层已经解锁detonate与look终局的理想战术是用distanceOf()计算爆破点到两名人质的距离确保爆炸半径目标格 前方 2 格 两条对角 自身不覆盖人质同时对成群的 Sludge 使用detonate()剩余敌人用attack()处理穿插rest()维持血量。class Player { playTurn(warrior) { const captives warrior.listen().filter((space) !space.getUnit().isEnemy()); const inBlastRadius captives.some((space) warrior.distanceOf(space) 2); if (!inBlastRadius) { const ahead warrior.look(); const first ahead[0]?.getUnit(); const second ahead[1]?.getUnit(); if (first?.isEnemy() second?.isEnemy()) { warrior.detonate(); } else if (first?.isEnemy()) { warrior.attack(); } } else { // 有危险就绕开优先救人质 const captive captives[0]; warrior.walk(warrior.directionOf(captive)); } } }提示distanceOf 2是一个示意阈值实际爆破半径取决于你的朝向与surroundingOffsets的组合请结合 libs/abilities/src/Detonate.ts 中四个偏移量推算。核心机制源码级解析Ticking倒计时即灭团Ticking 是本塔的署名机制其完整实现只有几十行libs/effects/src/Ticking.tspassTurn(): void { if (this.time) { this.time - 1; } this.unit.emit({ type: tick, description: is ticking, params: {} }); if (!this.time) { this.trigger(); } } trigger(): void { this.unit.emit({ type: explode, description: explodes, collapsing the ceiling and killing every unit, params: {} }); [...this.unit.getOtherUnits(), this.unit].forEach((anotherUnit) anotherUnit.takeDamage(anotherUnit.health), ); }两个关键事实伤害公式是扣除全部生命takeDamage(health)意味着任何单位包括战士一旦在场内就会直接死亡不存在残血存活时间归零的判定发生在回合推进时time: 7表示从当前回合开始战士大约有 7 个回合的行动窗口去完成解救。在关卡配置中它通过Ticking.with({ time: 7 })绑定到UnitConfig.effects这与第 6、7、8、9 层出现的不同time值7 / 10 / 9 / 20共同构成逐层升级的倒计时压力。Bind 与 Rescue束缚与解救的配对设计Bind.ts 与 Rescue.ts 是一对镜像能力bind(direction)对指定方向的单位调用receiver.bind()使其无法移动方向参数默认为forwardrescue(direction)仅当目标isBound()时生效调用unit.release(receiver)将其释放。两者都以方向为唯一参数并支持feel()预判目标。第 3 层的以少敌多与第 7、9 层的先绑后打都建立在这对能力的配合上。Detonate威力与自伤的平衡Detonate.ts 中的surroundingOffsets是理解爆破范围的关键const surroundingOffsets: RelativeOffset[] [ [1, 1], // 前方 1 格、右 1 格对角 [1, -1], // 前方 1 格、左 1 格对角 [2, 0], // 前方 2 格 [0, 0], // 自己 ];即目标格 8 点伤害 两条斜前对角、前方两格、自身 4 点伤害。第 8 层的关卡设计敌军集中在 (2,0) 与 (3,0)正好覆盖前方 2 格与目标格正是为了让玩家在一发入魂与自伤 4 点之间做权衡。感知系能力全家桶listen()返回所有其他单位所在空间的数组顺序即内部遍历顺序因此listen()[0]是距离最近还是最远取决于单位注册顺序实战中应配合directionOf()使用look(range)返回前方最多range个空间遇墙截断——适合单行道侦察第 8 层的 7×1 地图就是为它设计的directionOf(space)/directionOfStairs()把空间或楼梯换算成绝对方向供walk()使用distanceOf(space)曼哈顿距离计算用于判断爆破安全半径第 9 层。评分体系timeBonus 与 aceScore 意味着什么每层配置都带timeBonus与aceScore两个数值见 libs/core/src/types.ts 中LevelConfig的定义。结合 WarriorJS 的计分模型可以理解为timeBonus是该层可获得的时间奖励上限回合数越少剩余时间奖励越多拖得越久这部分得分越少aceScore是达成该层最高评价Ace / S 级所需的目标分数线通常要求以接近最优的回合数清空楼层。因此第 69 层的救人不耽误清场爆破不误伤人质本质上是回合数时间优化问题每多绕一步、多发呆一回合都在侵蚀时间奖励。表中最紧张的组合是第 8 层timeBonus: 30而地图长达 7 格滴答人质只有 9 回合要求玩家用detonate()一次性清掉两个敌人而非逐个攻击。实战策略总结结合关卡数据与源码实现打穿 The Powder Keep 可以遵循五条原则滴答优先任何回合先检查warrior.listen()中是否有isUnderEffect(ticking)的单位有则立即规划最短路径前往解救倒计时归零等于全员阵亡libs/effects/src/Ticking.ts。被围先绑周围未束缚敌人 2 时先用bind()稳住一个再逐一attack()第 3、7、9 层。爆炸要算半径detonate()会自伤并覆盖斜前方与前方两格命中滴答单位会连锁引爆必须用look()与distanceOf()提前验证安全。低血就歇rest()每回合回复maxHealth * healthGain本塔为 2 点满血无效在无敌人威胁的空窗期使用。能力是累积的不必担心这层没列的能力能不能用——塔内能力随进度继承前几层解锁的feel、health、walk等会一直陪伴到第 9 层。想深入了解塔楼与能力的数据契约可以继续阅读 libs/core/src/types.ts、towers/the-powder-keep/src/index.ts 以及 towers/README.md想弄清 CLI 如何发现并加载这座塔可查看 apps/cli/src/loadTowers.ts。赞分享教育CLI【免费下载链接】warriorjs An exciting game of programming and Artificial Intelligence项目地址https://gitcode.com/gh_mirrors/wa/warriorjs点击查看免费下载相关推荐WarriorJS 官方 Effects 包 warriorjs/effects 深度解析Ticking 倒计时炸弹效应的实现与实战配置WarriorJS 官方 Effects 包 warriorjs/effects 深度解析Ticking 倒计时炸弹效应的实现与实战配置 WarriorJS教育CLI深入理解 WarriorJS在 JavaScript 编程战斗中登顶之塔的完全入门指南深入理解 WarriorJS在 JavaScript 编程战斗中登顶之塔的完全入门指南 本篇指南围绕 docs/player/overview.md http教育CLIWarriorJS 入门塔「The Narrow Path」完整攻略9 层走廊机制、能力解锁与源码级实现解析WarriorJS 入门塔「The Narrow Path」完整攻略9 层走廊机制、能力解锁与源码级实现解析 导读 The Narrow Path狭窄之路教育CLI上一篇从安装到精通lisa.py完整使用手册让AI成为你的调试助手下一篇IDURAR ERP CRM的后端API版本迁移工具自动化脚本与测试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表