step.tin 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. #nop vim: set filetype=tt:;
  2. /*
  3. 本文件属于 PaoTin++ 的一部分
  4. ===========
  5. PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 享有并保留一切法律权利
  6. 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。
  7. ===========
  8. */
  9. event.HandleOnce {map/init} {map/step} {map} {map.step.Init};
  10. #alias {map.step.Init} {
  11. #0;
  12. };
  13. VAR {走路命令,默认为 go,推车时请改为 gan che to} map.step.go.cmd {go};
  14. VAR {清除门卫命令,默认为 attack} map.step.crush.cmd {attack};
  15. #alias {map.step.SetCmd} {
  16. #local cmd {@default{%0;go}};
  17. #var map.step.go.cmd {$cmd};
  18. };
  19. #alias {map.step.SetCrushCmd} {
  20. #local cmd {@default{%0;attack}};
  21. #var map.step.crush.cmd {$cmd};
  22. };
  23. ///=== {
  24. // #= map.step 走路概述
  25. //
  26. // 本文阐述一个好的走路机器人应当如何去做好一件最基本的事情:移动到相邻房间。
  27. // 这小小的一步移动看似简单,其实涉及到许多游戏底层的机制,要想做好并不容易。
  28. //
  29. // 在展开讨论之前,为了方便叙述,先引入一些概念:
  30. // - Exit(出口): 出口是房间的一个基本属性,大多数房间可以通过出口移动到相邻房间。
  31. // - Link(连接): 连接是房间之间的拓扑关系,通过连接,可以从一个房间移动到另一个。
  32. // - Step(单步): 一个具体的行走命令。一般来说,如果行走成功,会触发 GMCP.Move。
  33. //
  34. // 上面三个概念大体相同,但又有区别。举例来说,有的房间的出口其实是假的(成都的锦江),
  35. // 并不通向任何房间。所以出口不一定是一个有用的连接。
  36. // 假的出口有用吗?有的人可能会认为没用,但实际上它也是服务器给出的规范信息,可以用来
  37. // 区分同名房间,所以实际上也是有用的。
  38. //
  39. // 而连接也不一定是出口,比如有些机关陷阱,也就是说房间之间移动不一定要通过出口来完成。
  40. // 最常见的一类连接是迷宫,迷宫不能用普通的方法进行移动,只能用专门的机器人,那么对于
  41. // 想要通过迷宫的调用者来说,迷宫就像是一个从迷宫入口到迷宫出口的连接。
  42. //
  43. // 至于单步,大体上和连接是一个意思,但是含义侧重点不同。连接强调的是房间和房间的拓扑
  44. // 关系,而单步则强调的是在一个长的路径当中,一个具体的步骤。
  45. //
  46. // 总的来说,Link 强调拓扑细节,Step 强调它在路径中的地位,而 Exit 则是房间的固有属性。
  47. //
  48. // 接下来正式讨论。走路首先要关心的是走路目的,就是说你为什么走这个路:
  49. // - 探路: 对周围的世界充满了好奇心,充满了未知,但也遵循谨慎原则。
  50. // - 赶路: 即 P2P,以通过该房间为目的。
  51. // - 遍历: 遍历某个区域,以尽可能访问更多的房间为目的。
  52. //
  53. // 不同的走路目的,就会产生不同的走路模式:
  54. // - Try(对应探索): 谨慎地走一步,尽可能走,但如果走不通就放弃并记录失败原因。
  55. // - Must(对应P2P): 执着地走一步,一定要走成功,如果不成功就反复尝试。
  56. // - Should(对应遍历): 武断地走一步,行不行就这一下,也不做过多地尝试。
  57. //
  58. // 除了走路模式,走路机器还应该知道当前位置是哪里,计划走哪个出口,以及下一步通往哪里。
  59. // 1. 对于探路目的来说,只知道出口,不知道下一步通往哪里。
  60. // 2. 对于遍历和赶路目的来说,由于存在非标准出口,可能只知道连接到哪里,无法对应出口。
  61. // 3. 对于特殊房间来说,除了必须的移动命令,还需要额外的其它命令,来创造移动的条件。
  62. //
  63. // 以上这些就算是走路的前置条件(参数)了,每走一步路之前,都应该想清楚这些问题。
  64. // 注意这里只阐述一个单步的逻辑,并不负责整条路径的生成和行走策略。
  65. // 实际运用中,只有机器人可以坚实地走好每一单步,然后才可以沿着路径长途跋涉,走得更远。
  66. //
  67. // 说完参数,再说说返回值。按照设计,map 系统的每个组件无论大小,只要涉及到移动,则无论
  68. // 远近都需要发送以下两个标准事件:
  69. // - map/walk/continue: 机器人执行成功,调用者可以按照计划继续执行后续动作。
  70. // - map/walk/failed: 机器人执行失败,调用者应当取消后续动作,或进行补救措施。
  71. // 因为所有的 map 机器人会共享以上事件,因此只有钩子名称相互匹配的事件句柄才会被唤醒。
  72. // 请仔细核对你所订阅的事件对应的钩子名称。
  73. //
  74. // 一旦明确参数和返回值之后,接下来首先要做的事,就是搞清楚每个 Step 的细节,主要分两类:
  75. // - 出口相关: 通过出口离开本房间,这也是大多数普通房间之间连接的方式。
  76. // - 出口无关: 不通过任何出口,而是通过机器离开本房间,常见的比如渡口,马车行,爬山等。
  77. //
  78. // 走路机器人需要根据具体情况,通过分析每个 Step 的描述,来获知需要使用的命令,然后判断
  79. // 属于出口有关还是出口无关。
  80. // 对于出口有关的命令,PaoTin++ 将出口分为四类,同时为了便于书写,发明了以下记法用以区分:
  81. // - 出口类型 记录的命令 格式
  82. // - Normal: 标准出口 north 方向名全称
  83. // - Dynamic: 动态出口 North 首字母大写
  84. // - Hidden: 隐藏出口 NORTH 全部大写
  85. // - Denied: 禁止出口 nortH 尾字母大写
  86. //
  87. // 以上是四类出口相关的命令。下面还有一些出口无关的命令:
  88. // - 连接类型 记录的命令 含义
  89. // - GuoJiang: 渡过长江 GuoJiang 过江机器人
  90. // - GuoHe: 渡过长江 GuoHe 过河机器人
  91. // - Ride: 自驾小船 Ride/north 自驾小船,向北前进
  92. // - LxcEnter: 进凌霄城 LxcEnter 进凌霄城机器人
  93. // - Maze: 迷宫走路 Maze/云海进 按照云海进入走法穿越迷宫
  94. // - Path: 连续走路 Path/{n;w;e;s} 连续走好几个步骤,视同为一个 Step
  95. // - Bot: 机器走路 Foo/bar 机器名为 Foo,并需要 bar 参数
  96. //
  97. // 这些走路方式因为其特殊性,已经基本上无法通过出口来完成了,因此要用专门的机器人来走路。
  98. // 如果不同的房间存在于类似的行走方式,典型的比如渡口,那么机器人就具有一定的通用性。
  99. // 否则机器人基本上是为某房间定制的。这里统一用 Bot 方式来表示,不再区分。规则是:
  100. //
  101. // - 凡是大骆驼格式打头的命令,则一律当成机器走路来处理。后面用斜杠提供参数,
  102. // 如果存在多个参数,以大括号包裹的字符串列表形式表示。
  103. //
  104. // 还有一大类常见情况就是各类关卡(Gate),关卡大多都有一个对应的出口,但需要在行走之前或
  105. // 之后,执行一些额外的修饰命令。常见的情形有:
  106. // - 修饰命令 记录的命令 含义
  107. // - Give: 贿赂命令 Give/1 coin/shan xiao/north 给 shan xiao 一文铜板,然后往北
  108. // - Answer: 回答口令 Answer/自立为王/north 回答自立为王,然后往北
  109. // - Ask: 征得许可 Ask/shiwei/通传/east 向侍卫请求通传,然后向东
  110. // - Unwield: 卸下武器 Unwield/east 卸下武器,然后向东
  111. // 对于无法归类的关卡,可以用以下两个通用的方式来解决:
  112. // - Prepare: 前置动作 Prepare/yell bridge/north 执行命令 yell bridge,然后向北
  113. // - Postpare: 后置动作 Postpare/sheng bridge/north 向北离开之后,执行命令 sheng bridge
  114. //
  115. // 这些修饰命令本身并不属于完整的行走命令,因此不能单独使用,需要和前述两类行走命令联合使用。
  116. //
  117. // 除此之外,还有一些连接存在限制条件,也会影响连接的可用性,它们有:
  118. // - 限制条件 记录的命令 含义
  119. // - Fee: 收费连接 Fee/50/* 该连接需要收取费用,可以在选路时参考
  120. // - Cash: 收费连接 Cash/50 gold/* 该连接需要收取费用,需要提前准备好现金
  121. // - Weighted: 加权连接 WGT/80/* 连续权重(1~100)
  122. // - Cond: 通用条件出口,格式为 Cond/检查器(参数)/方向
  123. // - 经验条件 Cond/EXP(>10M)/* 经验大于 10M
  124. // - 等级条件 Cond/LVL(>60)/* 角色等级大于 50 级
  125. // - 轻功条件 Cond/DOG(>100)/* 轻功大于 100 级
  126. // - 道具条件 Cond/ITEM(...)/* 携带有指定 ID 的物品
  127. // - 解密条件 Cond/QUEST(...)/* 已完成指定解密
  128. // - 变量条件 Cond/VAR(变量;表达式)/* 检查变量和表达式的关系
  129. // - 逻辑条件 Cond/EXPR(表达式)/* 检查表达式是否成立
  130. // - 其它条件 Cond/FOO(...)/* 通过检查器 FOO 来校验
  131. // - 联合条件 Cond/EXP(>10M),ITEM(yaoqing han)/*
  132. //
  133. // 接下来重点说说 Normal/Gate/Dynamic/Hidden 这四大类出口。
  134. //
  135. // 首先说说标准出口(Normal)。标准出口顾名思义,就是最朴素的出口,它必须满足全部以下公理:
  136. // - 1. 标准名称: 它拥有标准的名称,即 20 个标准方向名称之一。
  137. // - 2. 标准显示方式: 在 look 和 GMCP.Move 数据流中,可以看到此出口信息。
  138. // - 3. 标准的行走命令: 都可以用 go 命令行走。
  139. // - 4. GMCP: 通过 go 命令都可以接收到 GMCP.Move 事件,无论成功失败。
  140. //
  141. // 很显然,大多数房间的每个出口都满足这些公理,但也有一些例外。首先比较常见的一种例外就是动态出口。
  142. // 动态出口(Dynamic)是指那些某些时候不会出现而某些时候又会出现的出口,典型的比如一扇门。动态出口
  143. // 除了这一点(即违背了前述公理2)之外,别的方面均与标准出口(Normal)完全相同。因此在走路机器人中,
  144. // 动态出口的处理也和标准出口完全相同。动态出口仅仅只影响 GPS 定位和 P2P 寻路。
  145. //
  146. // 还有一种特殊情形就是隐藏出口(Hidden),隐藏出口可能会被人误以为只是一个看不见的标准出口。但实
  147. // 际上它和标准出口有非常大的区别。它主要有以下特点:
  148. // - 1. 不支持短名称: 虽然大多数隐藏出口有一个类似与标准出口的名称,但实际上它不能像标准出口
  149. // 一样,用短名称来代替。这是因为短名称实际上是一个系统内置的 go 命令别名,
  150. // 即 n = go north
  151. // - 2. 不予显示: 隐藏出口通常都是不显示的,不论是 look、GMCP.Move,都看不到它们。
  152. // - 3. 非标准行走: 如果用 go 命令来行走隐藏出口,将会失败。隐藏出口实际上是新的命令。
  153. // - 4. GMCP反馈: 由于无法通过 go 命令来行走,所以仅当出口正确时才会有 GMCP 成功反馈。如
  154. // 果不存在该出口,则无法通过 GMCP 获得失败反馈。
  155. // 从各方面来看,隐藏出口更像是一般的命令,而不是出口。只不过该命令在成功时会移动玩家,仅此而已。
  156. //
  157. // 有鉴于此,Normal/Dynamic/Gate 采用 GMCP 进行确认,而 Hidden 则依赖于 GMCP + 房间信息 + sync.Wait。
  158. // 对于 Normal/Dynamic/Gate 来说,走之前先注册 GMCP.Move 事件钩子,然后根据 GMCP.Move 事件的反馈,
  159. // 就知道是否走成功。
  160. // };
  161. #alias {map.pl.helper} {
  162. #var step {%1};
  163. #if { "%2" != "" } {
  164. #var link[type] {%2};
  165. };
  166. #if { "%3" != "" } {
  167. #var link[%3] {%4};
  168. };
  169. };
  170. ///=== {
  171. // #@ map.ParseLink <link 命令>
  172. // 解析 link 命令,将 DSL 文本解析成结构体。
  173. // };
  174. #func {map.ParseLink} {
  175. #local step {%0};
  176. #local link {};
  177. #while {"$step" != ""} {
  178. #switch {"$step"} {
  179. #match {"Give/%+/%+/%+"} {map.pl.helper {&3} {} {gate} { {type}{give} {what}{&1}{who}{&2} } };
  180. #match {"Answer/%+/%+"} {map.pl.helper {&2} {} {gate} { {type}{answer} {words}{&1} } };
  181. #match {"Say/%+/%+"} {map.pl.helper {&2} {} {gate} { {type}{say} {words}{&1} } };
  182. #match {"Ask/%+/%+/%+"} {map.pl.helper {&3} {} {gate} { {type}{ask} {who}{&1}{what}{&2} } };
  183. #match {"Unwield/%+"} {map.pl.helper {&1} {} {gate} { {type}{unwield} } };
  184. #match {"Prepare/%+/%+"} {map.pl.helper {&2} {} {gate} { {prepare} {&1} } };
  185. #match {"Postpare/%+/%+"} {map.pl.helper {&2} {} {gate} { {postpare} {&1} } };
  186. #match {"Fee/%d/%+"} {map.pl.helper {&2} {} {fee} {&1} };
  187. #match {"Cash/%d %+/%+"} {map.pl.helper {&3} {} {cash} {{amount}{&1}{unit}{&2}} };
  188. #match {"WGT/%d/%+"} {map.pl.helper {&2} {} {weighted} {&1} };
  189. #match {"GuoJiang"} {map.pl.helper {} {GuoJiang} };
  190. #match {"GuoHe"} {map.pl.helper {} {GuoHe} };
  191. #match {"Ride"} {map.pl.helper {} {Ride} };
  192. #match {"Ride/%+"} {map.pl.helper {} {Ride} {dir} {&1}; };
  193. #match {"Maze/%+"} {map.pl.helper {} {Maze} {name} {&2}; };
  194. #match {"Cond/%+/%+"} {
  195. #var step {&2};
  196. #local parts {&1};
  197. #list parts {explode} {,};
  198. #local part {};
  199. #foreach {*parts[]} {part} {
  200. #local part {$parts[$part]};
  201. #local part {@str.Replace{{$part};{%+(%+)};{{checker}{&&1}{args}{&&2}}}};
  202. #var link[cond][$part[checker]] {$part[args]};
  203. };
  204. };
  205. #match {"{Normal|Dynamic|Hidden}/{[a-z]+}"} {
  206. #var link[type] {&1};
  207. #var link[exit] {&2};
  208. #var link[cmd] {$map.step.go.cmd &2};
  209. #var step {};
  210. };
  211. #match {"{[A-Z][^/]*}/%+"} {map.pl.helper {} {Bot} {name} {&1}; #var link[args] {&2} };
  212. #match {"{[A-Z]+}"} {
  213. #if { @dir.IsDir{@str.ToLower{$step}} } {
  214. #var link[type] {Hidden};
  215. #local exit {@str.ToLower{$step}};
  216. #var link[exit] {$exit};
  217. #var link[cmd] {$exit};
  218. };
  219. #else {
  220. #var link[type] {Bot};
  221. #var link[name] {$step};
  222. };
  223. #var step {};
  224. };
  225. #match {"{[A-Z][a-z]+[A-Za-z]*}"} {
  226. #if { @dir.IsDir{@str.ToLower{$step}} } {
  227. #var link[type] {Dynamic};
  228. #local exit {@str.ToLower{$step}};
  229. #var link[exit] {$exit};
  230. #var link[cmd] {$map.step.go.cmd $exit};
  231. };
  232. #else {
  233. #var link[type] {Bot};
  234. #var link[name] {$step};
  235. };
  236. #var step {};
  237. };
  238. #default {
  239. #if { @dir.IsDir{$step} } {
  240. #var link[type] {Normal};
  241. #var link[exit] {$step};
  242. #var link[cmd] {$map.step.go.cmd $step};
  243. };
  244. #else {
  245. #var link[type] {Command};
  246. #var link[cmd] {$step};
  247. };
  248. #var step {};
  249. };
  250. };
  251. };
  252. #return {$link};
  253. };
  254. #alias {map.step.Try} {
  255. map.step.Try.do try {%1} {%2} {%3};
  256. };
  257. #alias {map.step.Must} {
  258. map.step.Try.do must {%1} {%2} {%3};
  259. };
  260. #alias {map.step.Should} {
  261. map.step.Try.do should {%1} {%2} {%3};
  262. };
  263. #alias {map.step.Try.do} {
  264. #var map-step-context {
  265. {mode} {%1}
  266. {room} {%2}
  267. {step} {%3}
  268. {next} {%4}
  269. };
  270. #local link {@map.ParseLink{$map-step-context[step]}};
  271. #var map-step-context[link] {$link};
  272. #if { "$link[cond]" != "" } {
  273. #local checker {};
  274. #foreach {*link[cond][]} {checker} {
  275. #local args {$link[cond][$checker]};
  276. #local func {map.step.Cond.checker.$checker};
  277. #if { @existsFunction{$func} } {
  278. #local ok @$func{$args};
  279. #if { ! $ok } {
  280. errLog 条件「$checker($args)」不满足,无法继续行走。;
  281. map.step.Try.fail;
  282. #return;
  283. };
  284. };
  285. };
  286. dbgLog map => 所有条件($link[cond])检查无误,可以继续行走。;
  287. };
  288. #if { "$link[gate]" != "" } {
  289. map.step.open-gate;
  290. };
  291. #switch {"$link[type]"} {
  292. #case {"Denied"} {
  293. errLog 这里不能去。;
  294. map.step.Try.fail;
  295. #return;
  296. };
  297. #case {"{Normal|Dynamic}"} {
  298. event.HandleOnce GMCP.Move {map/step/try} {map} {map.step.Try.result};
  299. xtt.Send {$link[cmd]};
  300. };
  301. #case {"Hidden"} {
  302. #local token {@sync.UUID{}};
  303. #line sub var sync.Wait {
  304. event.HandleOnce GMCP.Move {map/step/try} {map} {
  305. sync.Ignore $token;
  306. map.step.Try.ok;
  307. };
  308. xtt.Send {$link[cmd]};
  309. sync.Wait {
  310. event.UnHandle GMCP.Move {map/step/try} {map};
  311. map.step.Try.fail;
  312. } {$token};
  313. };
  314. };
  315. #case {"Ride"} {
  316. event.HandleOnce map/walk/continue {map.Ride} {map.step.Try.do} {map.step.Try.ok};
  317. event.HandleOnce map/walk/failed {map.Ride} {map.step.Try.do} {map.step.Try.fail};
  318. map.Ride $link[dir];
  319. };
  320. #case {"{GuoJiang|GuoHe|Shalou}"} {
  321. #local bot {$link[type]};
  322. event.HandleOnce map/walk/continue {map.$bot} {map.step.Try.do} {map.step.Try.ok};
  323. event.HandleOnce map/walk/failed {map.$bot} {map.step.Try.do} {map.step.Try.fail};
  324. map.$bot;
  325. };
  326. #case {"Bot"} {
  327. #local bot {$link[name]};
  328. #if { ! @existsAlias{map.$bot} } {
  329. errLog 未知的机器: {$bot};
  330. map.step.Try.fail;
  331. #return;
  332. };
  333. event.HandleOnce map/walk/continue {map.$bot} {map.step.Try.do} {map.step.Try.ok};
  334. event.HandleOnce map/walk/failed {map.$bot} {map.step.Try.do} {map.step.Try.fail};
  335. #if { "$link[args]" == "" } {
  336. map.$bot;
  337. };
  338. #else {
  339. map.$bot $link[args];
  340. };
  341. };
  342. #case {"Maze"} {
  343. #local maze {$link[name]};
  344. event.HandleOnce map/walk/continue {map.Maze/$maze} {map.explore.try-go} {map.step.Try.ok};
  345. event.HandleOnce map/walk/failed {map.Maze/$maze} {map.explore.try-go} {map.step.Try.fail};
  346. map.Maze $maze;
  347. };
  348. #case {"Command"} {
  349. $link[cmd];
  350. sync.Wait {busy.Wait {map.step.Try.ok}};
  351. };
  352. #default {
  353. errLog TODO: 未知的特殊出口: {$link}。;
  354. map.step.Try.fail;
  355. #return;
  356. };
  357. };
  358. };
  359. #alias {map.step.open-gate} {
  360. #local gate {$map-step-context[link][gate]};
  361. #switch {"$gate[type]"} {
  362. #case {"give"} {
  363. give $gate[what] to $gate[who];
  364. };
  365. #case {"answer"} {
  366. answer $gate[words];
  367. };
  368. #case {"say"} {
  369. say $gate[words];
  370. };
  371. #case {"ask"} {
  372. ask $gate[who] about $gate[what];
  373. };
  374. #case {"unwield"} {
  375. unwield all;
  376. };
  377. };
  378. #if { "$gate[prepare]" != "" } {
  379. $gate[prepare];
  380. };
  381. };
  382. #alias {map.step.Try.ok} {
  383. event.Emit map/walk/continue map/step;
  384. };
  385. #alias {map.step.Try.fail} {
  386. errLog 行走失败。;
  387. event.Emit map/walk/failed map/step;
  388. };
  389. #alias {map.step.Try.result} {
  390. #nop 公理: 行走成功时一定会反馈房间信息。;
  391. #if { @isTrue{$gGMCP[Move][成功]} } {
  392. #if { "$map-step-context[link][gate][postpare]" != "" } {
  393. $map-step-context[link][gate][postpare];
  394. };
  395. event.HandleOnce {map/GotRoomInfo} {map/step/Try} {map} {map.step.Try.ok};
  396. #return;
  397. };
  398. map.step.Try.failed.reason {$map-step-context};
  399. };
  400. #alias {map.step.Try.failed.reason} {
  401. #local cmd {$map-step-context[link][cmd]};
  402. #if { "$cmd" == "" } {
  403. #return;
  404. };
  405. #if { "$cmd" != "@ga.ThisCmd{}" } {
  406. event.HandleOnce GA {map/step/Try} {map} {map.step.Try.failed.reason};
  407. #return;
  408. };
  409. #class map.step.Try.failed.reason open;
  410. #var map-go-failed-context {$map-step-context};
  411. #nop 行走失败时,分情况进行处理。总体而言,分三种情况:;
  412. #nop 1,retry-简单重试;2,crush-清除门卫;3,abandon-放弃该方向。;
  413. #nop 战斗中;
  414. #action {^你逃跑失败。{|ID=map/step/Try}$} {map.step.Try.retry};
  415. #nop busy 中;
  416. #action {^你的动作还没有完成,不能移动。$} {map.step.Try.retry};
  417. #nop 随机绊倒;
  418. #action {^你不小心被什么东西绊了一下,差点摔个大跟头。$} {map.step.Try.retry};
  419. #nop 徐州的巷尾;
  420. #action {^地痞对你恶狠狠说道:“小贱人,不给大爷上供也想跑?”$} {map.step.Try.retry};
  421. #nop 大轮寺的入幽口;
  422. #action {^宝象突然跳到你面前,拦住了去路。$} {map.step.Try.retry};
  423. #nop 襄阳的城东小路;
  424. #action {^此去往东是荒郊野岭,盗贼猛兽出没之地,我劝%*$} {map.step.Try.retry};
  425. #nop 武当山的断恩岭;
  426. #action {^那条路不知深浅,还是不要去了。$} {map.step.Try.retry};
  427. #nop 秦州的小路;
  428. #action {^你在小路中仔细对照,寻找往前的路径。$ } {map.step.Try.retry};
  429. #action {^青海湖畔美不胜收,你不由停下脚步,欣赏起了风景。$} {map.step.Try.retry};
  430. #nop {西南地绵绵群山|滇北群山|藏边群山|六盘山};
  431. #action {^你还在山中跋涉,一时半会恐怕走不出这%*山!$} {map.step.Try.retry};
  432. #action {^你在浙闽之间的群山中转来转去,却还没有走出去。$} {map.step.Try.retry};
  433. #action {^你在浙闽之间的群山中兜兜转转,一时却还未能走出去。$} {map.step.Try.retry};
  434. #nop 峨嵋山的一线天;
  435. #action {^到了这里,宽度不过一人,想快也快不起来了。$} {map.step.Try.retry};
  436. #nop 南阳的七星河畔;
  437. #action {^河滩上乱石遍地,深深浅浅,几乎没法快起来。$} {map.step.Try.retry};
  438. #nop 香泉镇的夹关山;
  439. #action {^最狭窄处只容一人通过,想快也快不起来。$} {map.step.Try.retry};
  440. #nop 伏牛山的山壁;
  441. #action {^此处越发难行,你不得不慢了下来。$} {map.step.Try.retry};
  442. #nop 伏牛山的鹞子垭;
  443. #action {^路途更加险峻,兼之视线受阻,你不得不停下仔细观察脚下和前路。$} {map.step.Try.retry};
  444. #nop 天台山的曹娥江岸;
  445. #action {^岸边虽然尚算平坦,但一脚深一脚浅的没法快起来。$} {map.step.Try.retry};
  446. #nop 秦州的祁山古道的小路;
  447. #action {^你在小路中仔细对照,寻找往前的路径。$} {map.step.Try.retry};
  448. #nop 庐陵的山间道;
  449. #action {^道路崎岖难行,一不留神就会滚下山去,你心道:“还是慢点为妙。”$} {map.step.Try.retry};
  450. #nop 庐陵的白鹭洲;
  451. #action {^你赶紧擦拭裤脚上的水迹。$} {map.step.Try.retry};
  452. #nop 昆明的龙门;
  453. #action {^此处位于峭壁之上,你战战兢兢,根本快不起来。$} {map.step.Try.retry};
  454. #nop 昆明的迎仙桥;
  455. #action {^桥上挤满了游人,快不起来。$} {map.step.Try.retry};
  456. #nop 商州的瓮峪古道;
  457. #action {^到了此处,山道开始拐弯,你稍稍放慢观察一下。$} {map.step.Try.retry};
  458. #nop 潼关的陡坡;
  459. #action {^因为道路变得极陡,你行至此处不得不放慢脚步。$} {map.step.Try.retry};
  460. #nop 潼关的深谷;
  461. #action {^这里险峻异常,稍有不慎就是跌落深渊的下场。$} {map.step.Try.retry};
  462. #nop 袁州的塘源冲;
  463. #action {^看着四周的淤泥,你谨慎地选择下脚的方位。$} {map.step.Try.retry};
  464. #nop 衢州的一线天;
  465. #action {^在这里可没法走那么快。$} {map.step.Try.retry};
  466. #nop 商州的峡谷;
  467. #action {^走到最狭窄处不足一尺,你能侧身缓行。$} {map.step.Try.retry};
  468. #nop 商州的山路;
  469. #action {^此处位于巴山北麓,崎岖险峻,非常难行。$} {map.step.Try.retry};
  470. #nop 张家口的蛤蟆嘴;
  471. #action {^这里道路艰险难行,已经无法再提速了。$} {map.step.Try.retry};
  472. #nop 张家口西门横山;
  473. #action {^你的视线被巍峨的恒山吸引,不由放慢了脚步。$} {map.step.Try.retry};
  474. #nop 华山;
  475. #action {^在栈道上想快也快不起来。$} {map.step.Try.retry};
  476. #nop 梅岭古道;
  477. #action {^山道往上愈发难行,你不得不缓下脚步。$} {map.step.Try.retry};
  478. #nop 卢氏县的小铁沟;
  479. #action {^你踏在泥泞的水坑里,深一脚浅一脚地无法快速通行。$} {map.step.Try.retry};
  480. #nop 成都的金牛道;
  481. #action {^你小心翼翼往前挪动,遇到艰险难行处,只好放慢脚步。$} {map.step.Try.retry};
  482. #nop 雁荡山的山路;
  483. #action {^你小心翼翼往前挪动,生怕一不在意就跌落山下。$} {map.step.Try.retry};
  484. #nop 武当山的沼泽;
  485. #action {^你走着走着就陷进了一处沼泽当中,艰难地从沼泽中拔出来。$} {map.step.Try.retry};
  486. #nop 建宁府的山间路;
  487. #action {^这里山路崎岖,不小心就会滑落,实在不太好走,你不得不放慢脚步。$} {map.step.Try.retry};
  488. #nop 湟中的沙漠中;
  489. #action {^{荒路|沙石地|沙漠中}几乎没有路了,你走不了那么快。$} {map.step.Try.retry};
  490. #nop 苗疆山路;
  491. #action {^%+1..8u在大雨的冲刷下,愈加难行!$} {map.step.Try.retry};
  492. #nop 苗疆山路;
  493. #action {^山路崎岖,你不得不停下来歇歇脚。$} {map.step.Try.retry};
  494. #nop 汉口镇的双峰山;
  495. #action {^走路太快,你没在意脚下,被杂草绊了一下。$} {map.step.Try.retry};
  496. #nop 长沙府的山间小路;
  497. #action {^小路宽度不足以容纳两人并肩行走,你需要小心翼翼地前行。$} {map.step.Try.retry};
  498. #nop 铜官镇的小道;
  499. #action {^你忽然不辨方向,不知道该往哪里走了。$} {map.step.Try.retry};
  500. #nop 上饶的山路;
  501. #action {^你没看清路就乱走,结果转了一圈又回到原地。$} {map.step.Try.retry};
  502. #nop 绍兴府的鉴湖;
  503. #action {^牛三调笑道:「别走那么快啊」$} {map.step.Try.retry};
  504. #nop 凌霄城的城门;
  505. #action {^吊桥还没有升起来,你就这样走了,可能会给外敌可乘之机的。$} {map.step.Try.retry};
  506. #nop 台州府的山间道;
  507. #action {^你小心翼翼地看着脚下,不敢再快了。$} {map.step.Try.retry};
  508. #nop 泰山派的云梯道;
  509. #action {^石阶在脚下微微颤动,仿佛连山风都在考量你的心志。你下意识放慢了脚步,不敢贸然前行。$} {
  510. map.step.Try.retry;
  511. };
  512. #nop 凉州的金塔寺;
  513. #action {^走在石阶上,你看着身边的悬崖,稍有不慎就是粉身碎骨的下场,不由得靠里侧身,放缓脚步。$} {
  514. map.step.Try.retry;
  515. };
  516. #nop 襄阳的蒙古营门;
  517. #action {^守将大声喝到:干什么的?(answer)$} {answer 送信; map.step.Try.retry};
  518. #nop 北京的各大城门;
  519. #action {^官兵拦住你说道:站住,把%*留下再说!$} {unwield right; map.step.Try.retry};
  520. #nop 北京的各大城门,同上,空手可进;
  521. #action {^官兵拦住了你。$} {#0};
  522. #nop 都统制府的大门;
  523. #action {^都统制府侍卫说道:「没有经过通传,任何人等不得擅闯都统治府。」$} {
  524. ask shiwei about 通传; map.step.Try.retry;
  525. };
  526. #nop 襄阳的南门;
  527. #action {^守军拦住了你的去路,大声喝到:干什么的?要想通过先问问我们守将大人!$} {
  528. ask shou jiang about 投军; map.step.Try.retry;
  529. };
  530. #nop 北京的鳌府后院,不吃神行千里,杀人可进;
  531. #action {^女管家挡住了你。$} {map.step.Try.crush {guan jia}};
  532. #nop 扬州的财主后院,不吃神行千里,杀人可进;
  533. #action {^崔员外挡住了你。$} {map.step.Try.crush {cui yuanwai}};
  534. #nop 仙霞岭的山路;
  535. #action {^卢道之说道:「我就看不惯长这么好看的男人!」$} {map.step.Try.crush {lu daozhi}};
  536. #action {^卢道之说道:「女人长这么丑,出来吓人吗?」$} {map.step.Try.crush {lu daozhi}};
  537. #nop 明教的疯子。;
  538. #action {^阿善恶狠狠地盯着你:臭贼,我看你这次往哪儿跑。$} {map.step.Try.crush {a shan}};
  539. #nop 扬州的衙门大门;
  540. #action {^衙役喝道:「威……武……」$} {map.step.Try.crush {ya yi}};
  541. #nop 扬州的兵营大门;
  542. #action {^官兵们拦住了你的去路{|。}$} {map.step.Try.abandon};
  543. #nop 北京的校场;
  544. #action {^门边的侍卫伸手把你给拦住了。“想混吃混喝不成?...”$} {map.step.Try.abandon};
  545. #nop 大同的神机府;
  546. #action {^神机营士兵挡住了你的去路。$} {map.step.Try.abandon};
  547. #nop 襄阳的吕家内宅;
  548. #action {^亲兵拦住了你的去路,大声喝到:敢闯吕大人的家,你不想活了?$} {map.step.Try.abandon};
  549. #nop 全真派的更衣室;
  550. #action {^你不是全真派的,还想去白洗澡?!$} {map.step.Try.abandon};
  551. #nop 武当山的小路,接了武当新手任务可进;
  552. #action {^前面的小树林中不时发出几声惨叫声,还是不要再往里走了!$} {map.step.Try.abandon};
  553. #nop 襄阳的雪峰脚下,解密村姑可进;
  554. #action {^你抬头向上一望,我的妈呀,这么高怎么爬呀?该找个本地人问问。$} {map.step.Try.abandon};
  555. #nop 大理的厢房;
  556. #action {^那是{女|男}子的厢房!$} {map.step.Try.abandon};
  557. #nop 杀手帮的大道;
  558. #action {^嘿,那边可是{女|男}浴室啊,你进去干什么?唉....$} {map.step.Try.abandon};
  559. #nop 扬州的浴室走廊;
  560. #action {^看清楚些,那里可是{女|男}浴室耶!难道你想闯入(breakin)吗?$} {map.step.Try.abandon};
  561. #nop 各酒楼的宴客厅门厅;
  562. #action {^别老来浑水摸鱼了。$} {map.step.Try.abandon};
  563. #action {^此时宴客厅并无宴会,大门紧闭着。$} {map.step.Try.abandon};
  564. #nop 扬州的中央广场;
  565. #action {^绿萼白了你一眼道:「都那么大了还来白吃,不害羞。」$} {map.step.Try.abandon};
  566. #nop 洛阳的洛阳西门;
  567. #action {^既然已经退隐江湖,就不要在关注那些风风雨雨。$} {map.step.Try.abandon};
  568. #nop 各地的宠物竞技场;
  569. #action {^只有宠物可以上台参赛(join)。$} {map.step.Try.abandon};
  570. #nop 河北的官道;
  571. #action {^商家堡区域暂未开放。$} {map.step.Try.abandon};
  572. #nop 扬州的巫师会客室;
  573. #action {^巫师的房间还是不进为好!$} {map.step.Try.abandon};
  574. #nop 扬州的武庙;
  575. #action {^这里通向神的世界,只有神和神的创造物才可以下去。$} {map.step.Try.abandon};
  576. #nop 各地药炉;
  577. #action {^你要租药炉需要跟药铺老板打招呼了。$} {map.step.Try.abandon};
  578. #nop 成都的假出口;
  579. #action {^锦江看看就好了,难道你真要投河自尽吗?$} {map.step.Try.abandon};
  580. #nop 峨嵋的神灯阁二楼,有峨嵋邀请函可进;
  581. #action {^东面传来一个淡淡的声音,不是掌门邀请,就不要过来了。$} {map.step.Try.abandon};
  582. #nop 峨嵋的蛇窟入口;
  583. #action {^从黑暗中走出一个老妇人挡住你,说:“你%*不能进蛇窟。”$} {map.step.Try.abandon};
  584. #nop 大轮寺的日木伦殿;
  585. #action {^护法喇嘛拦住你道:那里是本寺{女|男}弟子休息的居所} {map.step.Try.abandon};
  586. #action {^护法喇嘛拦住你说道:最近寺内伙食紧张} {map.step.Try.abandon};
  587. #nop TODO: 云冈石窟的土路,需要炸开巨岩就可以过去;
  588. #action {^巨岩横亘在路当中,你没法通过。$} {map.step.Try.abandon};
  589. #nop TODO: 威海卫的临海楼,可以爬上去;
  590. #action {^上面已经塌了,你还上去干什么?$} {map.step.Try.abandon};
  591. #nop TODO: 康亲王府的石屋,可以从房顶爬过去;
  592. #action {^侍卫伸手拦住你说道:里面没什么好看的,马上离开这里....!$} {map.step.Try.abandon};
  593. #nop TODO: 北京的刑部府衙大门,可以从房顶爬过去;
  594. #action {^侍卫伸手拦住你朗声说道:里面没什么好看的,马上离开这里....!$} {map.step.Try.abandon};
  595. #nop 没有出口;
  596. #action {^这个方向没有出路。$} {map.step.Try.abandon};
  597. #action {^哎哟,你一头撞在墙上,才发现这个方向没有出路。$} {map.step.Try.abandon};
  598. #nop 长江黄河粘鼠板;
  599. #action {^你一脚深一脚浅地沿着%*走去,虽然不快,但离目标越来越近了。$} {map.step.Try.hard};
  600. #action {^你一脚深一脚浅地沿着%*走去,跌跌撞撞,几乎在原地打转。$} {map.step.Try.hard};
  601. #nop 通缉时的城门守将。;
  602. #action {^武将拦住了你的去路。$} {map.step.Try.abandon};
  603. #nop 这是被通缉了。;
  604. #action {^看起来武将想杀死你!武将看了看通缉告示,又看了看你,一脸狞笑,自己送上门来啊。$} {
  605. map.step.Try.abandon;
  606. };
  607. #action {^%*$} {#cat map-go-failed-context[reason] {%%0}} {5.5};
  608. #alias {map.step.Try.retry} {
  609. event.UnHandle GA {map/step/Try} {map};
  610. #if { @char.InCombat{} } {
  611. event.HandleOnce {char/nofight} {map/step/Try} {map} {map.step.Try.retry};
  612. #return;
  613. };
  614. #if { @char.IsBusy{} } {
  615. event.HandleOnce {char/nobusy} {map/step/Try} {map} {map.step.Try.retry};
  616. busy.Halt;
  617. #return;
  618. };
  619. #class map.step.Try.failed.reason kill;
  620. #delay map.step.Try.retry {
  621. #local ctx {$map-step-context};
  622. map.step.Try.do $ctx[mode] {$ctx[room]} {$ctx[step]} {$ctx[next]};
  623. } 1;
  624. };
  625. #alias {map.step.Try.crush} {
  626. event.UnHandle GA {map/step/Try} {map};
  627. $map.step.crush.cmd %%1;
  628. event.HandleOnce {char/nofight} {map/step/Try} {map} {map.step.Try.retry};
  629. };
  630. #alias {map.step.Try.abandon} {
  631. event.UnHandle GA {map/step/Try} {map};
  632. #nop 有关卡的地方总是需要重试,不要轻易放弃。;
  633. #if { "$map-step-context[link][gate]" != "" } {
  634. #delay map.step.Try.retry {map.step.Try.retry} 1;
  635. #return;
  636. };
  637. #class map.step.Try.failed.reason kill;
  638. map.step.Try.fail;
  639. };
  640. #alias {map.step.Try.hard} {
  641. event.UnHandle GA {map/step/Try} {map};
  642. #if { "$map-step-context[mode]" == "must" } {
  643. #delay map.step.Try.retry {map.step.Try.retry} 1;
  644. };
  645. #else {
  646. errLog TODO: 长江黄河粘鼠板暂时不处理。;
  647. #class map.step.Try.failed.reason kill;
  648. map.step.Try.fail;
  649. };
  650. };
  651. #alias {map.step.Try.do.fail} {
  652. #local __unused {%%0};
  653. #local ctx {$map-go-failed-context};
  654. #class map.explore.gather-go-failed-reason kill;
  655. #class map.step.Try.failed.reason kill;
  656. #if { "$ctx[reason]" != "" } {
  657. errLog 未知原因引起的行走失败:{$ctx[reason]};
  658. };
  659. #nop 有关卡的地方总是需要重试,不要轻易放弃。;
  660. #if { "$map-step-context[link][gate]" != "" } {
  661. #delay map.step.Try.retry {map.step.Try.retry} 1;
  662. #return;
  663. };
  664. map.step.Try.fail;
  665. };
  666. event.ClassHandleOnce GA {map/step/Try} {map} {map.step.Try.do.fail};
  667. #class map.step.Try.failed.reason open;
  668. };
  669. #func {map.step.Cond.checker.EXP} {
  670. #local cond {%0};
  671. #if { $char[HP][经验] $cond } {
  672. #return 1;
  673. };
  674. #else {
  675. #return 0;
  676. };
  677. };
  678. #func {map.step.Cond.checker.LVL} {
  679. #local cond {%0};
  680. #if { $char[档案][人物等级] $cond } {
  681. #return 1;
  682. };
  683. #else {
  684. #return 0;
  685. };
  686. };
  687. #func {map.step.Cond.checker.DOG} {
  688. #local cond {%0};
  689. #if { @char.SkillLevel{基本轻功} $cond } {
  690. #return 1;
  691. };
  692. #else {
  693. #return 0;
  694. };
  695. };
  696. #func {map.step.Cond.checker.EDOG} {
  697. #local cond {%0};
  698. #if { @char.SkillJifaLevel{基本轻功} $cond } {
  699. #return 1;
  700. };
  701. #else {
  702. #return 0;
  703. };
  704. };
  705. #func {map.step.Cond.checker.ITEM} {
  706. #local cond {%0};
  707. #if { @char.backpack.Has{ITEM;$cond} } {
  708. #return 1;
  709. };
  710. #else {
  711. #return 0;
  712. };
  713. };
  714. #func {map.step.Cond.checker.VAR} {
  715. #local cond {%0};
  716. #local cond {@str.Replace{{$cond};{%*;%*};{{var}{&1}{expr}{&2}}}};
  717. #local cond[var] {\$$cond[var]};
  718. #local bool {};
  719. #line sub {escapes;var} #math {bool} {$cond[var] $cond[expr]};
  720. #if { $bool } {
  721. #return 1;
  722. };
  723. #else {
  724. #return 0;
  725. };
  726. };
  727. #func {map.step.Cond.checker.EXPR} {
  728. #local cond {%0};
  729. #local bool {};
  730. #line sub {escapes;var} #math {bool} {$cond};
  731. #if { $bool } {
  732. #return 1;
  733. };
  734. #else {
  735. #return 0;
  736. };
  737. };
  738. #alias {map.ui.step} {
  739. map.step.Try {@map.RID{$gMapRoom}} {%1};
  740. };