path.tin 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. #nop vim: set filetype=tt:;
  2. /*
  3. 本文件属于 PaoTin++ 的一部分
  4. ===========
  5. PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 享有并保留一切法律权利
  6. 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。
  7. ===========
  8. */
  9. #pathdir {n} {s} {1};
  10. #pathdir {e} {w} {2};
  11. #pathdir {s} {n} {4};
  12. #pathdir {w} {e} {8};
  13. #pathdir {u} {d} {16};
  14. #pathdir {d} {u} {32};
  15. #pathdir {ne} {sw} {3};
  16. #pathdir {nw} {se} {9};
  17. #pathdir {se} {nw} {6};
  18. #pathdir {sw} {ne} {12};
  19. #pathdir {nu} {sd} {17};
  20. #pathdir {eu} {wd} {18};
  21. #pathdir {su} {nd} {20};
  22. #pathdir {wu} {ed} {24};
  23. #pathdir {nd} {su} {33};
  24. #pathdir {ed} {wu} {34};
  25. #pathdir {sd} {nu} {36};
  26. #pathdir {wd} {eu} {40};
  27. #pathdir {neu} {swd} {19};
  28. #pathdir {nwu} {sed} {25};
  29. #pathdir {seu} {nwd} {22};
  30. #pathdir {swu} {ned} {28};
  31. #pathdir {ned} {swu} {35};
  32. #pathdir {nwd} {seu} {42};
  33. #pathdir {sed} {nwu} {38};
  34. #pathdir {swd} {neu} {44};
  35. #pathdir {out} {enter} {19};
  36. #pathdir {enter} {out} {44};
  37. VAR {当前路径} map.path.current {};
  38. event.HandleOnce {map/init} {map/path} {map} {map.path.Init};
  39. #alias {map.path.Init} {
  40. event.Handle {map/GotRoomInfo} {path.Hint} {map/path} {path.Hint};
  41. storage.Load map-path map.path.list;
  42. };
  43. ///=== {
  44. // ## path.Trace [<是否可翻转>] [<是否可压缩>]
  45. // 开始为本房间录制路径。
  46. // 两个参数指定了本次即将录制的路径特点,并会在录制完成时真正运用。
  47. // 如果省略参数,则默认为可翻转、可压缩。
  48. // 在录制完成之前,你仍然可以随时用 path.SetType 来重新设置。
  49. // };
  50. #alias {path.Trace} {
  51. #local reversible {@default{%1;true}};
  52. #local compact {@default{%2;true}};
  53. #if { @path.isTracing{} } {
  54. errLog 录制路径工作正在进行中。;
  55. #return;
  56. };
  57. #local area {@map.GetArea{}};
  58. #if { "$area" == "" } {
  59. event.HandleOnce {map/GotArea} {path.Trace} {map/path} {path.Trace};
  60. #delay map.Trace.retry {map.GetArea} 1;
  61. #return;
  62. };
  63. #var map.path.current {
  64. {from} {@map.Room.CID{}}
  65. {to} {}
  66. {reversible} {$reversible}
  67. {compact} {$compact}
  68. };
  69. event.Handle {GMCP.Move} {path.Trace} {map/path} {path.response};
  70. #path create;
  71. path.message 开始录制路径。;
  72. option.Enable KeypadWalk;
  73. ui.walk.SetCmd path.ui.move;
  74. };
  75. ///=== {
  76. // ## path.SetType [<是否可翻转>] [<是否可压缩>]
  77. // 重新设置当前正在录制的路径特点。
  78. // 两个参数指定了此时正在录制的路径特点,并会在录制完成时真正运用。
  79. // 如果省略参数,则默认为前值,不予改变。
  80. // 在录制完成之前,你仍然可以随时用 path.SetType 来重新设置。
  81. // };
  82. #alias {path.SetType} {
  83. #if { "%0" == "" } {
  84. xtt.Usage %90;
  85. #return;
  86. };
  87. #if { ! @path.isTracing{} } {
  88. errLog 尚未开始录制。;
  89. #return;
  90. };
  91. #local reversible {@default{%1;$map.path.current[reversible]}};
  92. #local compact {@default{%2;$map.path.current[compact]}};
  93. #var map.path.current[reversible] {$reversible};
  94. #var map.path.current[compact] {$compact};
  95. };
  96. #alias {path.ui.move} {
  97. #local dir {%1};
  98. #local short {@dir.Short{%1}};
  99. #path insert {$short};
  100. go $dir;
  101. };
  102. #alias {path.response} {
  103. #local cmd {@ga.ThisCmd{}};
  104. #if { @isFalse{$gGMCP[Move][成功]} } {
  105. #if { "$cmd" == "go %+" } {
  106. #path delete;
  107. };
  108. #return;
  109. };
  110. #if { @dir.IsDir{$cmd} } {
  111. #local short {@dir.Short{$cmd}};
  112. #if { "$cmd" !== "$short" } {
  113. #path insert {$cmd};
  114. };
  115. };
  116. #elseif { "$cmd" != "go %*" } {
  117. warnLog 未知的移动方式,无法识别,请手动维护路径。;
  118. path.Mark {$gMapRoom[name]/$cmd};
  119. #return;
  120. };
  121. #local length {};
  122. #path get length length;
  123. path.message 路径录制中,{<139>$cmd<159>} 已添加,目前长度: $length;
  124. };
  125. ///=== {
  126. // ## path.BotStep <机器人名称> [<机器人参数> ...]
  127. // 插入一个机器人行走步骤。
  128. // 接到本指令时,PaoTin++ 会进行如下处理:
  129. // - 1: 暂停路径录制;
  130. // - 2: 在路径中插入一个机器人步骤;
  131. // - 3: 自动调用机器人行走;
  132. // - 4: 机器人行走完成后,自动恢复录制。
  133. // 按照约定,符合本规范的机器人必须发射以下事件之一:
  134. // - map/walk/continue: 代表机器人成功结束运行,通过该区域
  135. // - map/walk/failed: 代表机器人遇到了无法逾越的障碍,放弃行走
  136. // 本命令会监听以上事件并做相应处理。
  137. // };
  138. #alias {path.BotStep} {
  139. #local bot {%1};
  140. #info arguments save;
  141. #local args {$info[ARGUMENTS]};
  142. #unvar info[ARGUMENTS];
  143. #unlocal args[0];
  144. #if { "$bot" == "" } {
  145. xtt.Usage %90;
  146. #return;
  147. };
  148. #if { ! @path.isTracing{} } {
  149. errLog 路径录制尚未开始。;
  150. #return;
  151. };
  152. #if { !@existsAlias{map.$bot} } {
  153. errLog 不存在机器人 $bot,请检查是否拼写错误。;
  154. #return;
  155. };
  156. #class path.BotStep open;
  157. #alias {path.BotStep.done} {
  158. path.BotStep.end;
  159. path.message 机器人运行完成,继续录制路径。;
  160. };
  161. #line sub var #alias {path.BotStep.failed} {
  162. path.BotStep.end;
  163. path.message 机器人运行出错,姑且继续录制,如有不脱请取消录制。;
  164. };
  165. #line sub var #alias {path.BotStep.end} {
  166. #class path.BotStep kill;
  167. #path start;
  168. event.Handle {GMCP.Move} {path.Trace} {pathdir} {path.response};
  169. path.message <129>录制路径中…… <299>;
  170. };
  171. event.ClassHandleOnce {map/walk/continue} {map.$bot} {pathdir} {path.BotStep.done};
  172. event.ClassHandleOnce {map/walk/failed} {map.$bot} {pathdir} {path.BotStep.failed};
  173. #class path.BotStep close;
  174. event.UnHandle {GMCP.Move} {path.Trace} {pathdir};
  175. #local step {$bot};
  176. #local count {&args[]};
  177. #if { $count > 1 } {
  178. #local idx {};
  179. #loop 2 {$count} {idx} {
  180. #local arg {$args[$idx]};
  181. #if { "$arg" == "%*;%*" } {
  182. #cat {step} {/{$arg}};
  183. };
  184. #else {
  185. #cat {step} {/$arg};
  186. };
  187. };
  188. };
  189. #path stop;
  190. #path insert {$step} {$step};
  191. path.message 已经暂停路径录制,将会在机器人运行结束后自动继续。;
  192. map.%0;
  193. };
  194. ///=== {
  195. // ## path.Mark [<记号>]
  196. // 在录制路径的过程中,插入一个记号,方便将来手动编辑路径。
  197. // 如果省略记号,默认为当前房间名。
  198. // };
  199. #alias {path.Mark} {
  200. #local mark {@default{%1;$gMapRoom[name]}};
  201. #if { ! @path.isTracing{} } {
  202. errLog 路径录制尚未开始。;
  203. #return;
  204. };
  205. #path insert {Mark/$mark} {Mark/$mark};
  206. };
  207. #alias {map.Mark} {
  208. #local mark {%1};
  209. okLog 这里有一个记号:「$mark」。;
  210. map.BotReturn map.Mark;
  211. };
  212. ///=== {
  213. // ## path.Cancel
  214. // 取消当前正在进行的录制路径工作。
  215. // };
  216. #alias {path.Cancel} {
  217. #if { ! @path.isTracing{} } {
  218. errLog 路径录制尚未开始。;
  219. #return;
  220. };
  221. #var map.path.current {};
  222. #path destroy;
  223. event.UnHandle {GMCP.Move} {path.Trace};
  224. path.message 放弃了本次路径录制工作。;
  225. };
  226. ///=== {
  227. // ## path.Finish
  228. // 完成录制。
  229. // };
  230. #alias {path.Finish} {
  231. #local area {@map.GetArea{}};
  232. #if { "$area" == "" } {
  233. event.HandleOnce {map/GotArea} {path.Finish} {map/path} {path.Finish};
  234. #delay map.Finish.retry {map.GetArea} 1;
  235. #return;
  236. };
  237. #local here {@map.Room.CID{}};
  238. #var map.path.current[to] {$here};
  239. #path stop;
  240. event.UnHandle {GMCP.Move} {path.Trace};
  241. #local fpath {};
  242. #path save forward fpath;
  243. #local bpath {};
  244. #path save backward bpath;
  245. #if { "$map.path.current[from]" == "$map.path.current[to]" } {
  246. okLog 检测到遍历路径。;
  247. #local length {};
  248. #path get length length;
  249. #local pathName {${here}-遍历${area}-$length};
  250. #var map.path.list[$pathName] {$fpath};
  251. };
  252. #else {
  253. #if { @isTrue{$map.path.current[compact]} } {
  254. #local fpath {@path.Simplify{$fpath}};
  255. };
  256. okLog 正向路径: {#$fpath#};
  257. #local pathName {${map.path.current[from]}-${map.path.current[to]}};
  258. #var map.path.list[$pathName] {$fpath};
  259. #if { @isTrue{$map.path.current[reversible]} } {
  260. #if { @isTrue{$map.path.current[compact]} } {
  261. #local bpath {@path.Simplify{$bpath}};
  262. };
  263. okLog 反向路径: {#$bpath#};
  264. #local pathName {${map.path.current[to]}-${map.path.current[from]}};
  265. #var map.path.list[$pathName] {$bpath};
  266. };
  267. };
  268. #var map.path.current {};
  269. #path destroy;
  270. path.message 路径录制结束。你可以使用 {@mslp.Exec{path.List;path.List}} 命令查看本房间的关联路径。;
  271. path.Hint;
  272. storage.Save map-path map.path.list;
  273. };
  274. ///=== {
  275. // ## path.Hint
  276. // 如果此处有路径,则提醒玩家。
  277. // };
  278. #alias {path.Hint} {
  279. #nop 录制路径的过程中,提示以录制为主。;
  280. #if { @path.isTracing{} } {
  281. #return;
  282. };
  283. #local area {@map.GetArea{}};
  284. #if { "$area" == "" } {
  285. #local here {@xiaoyao.Locate{}};
  286. #if { "$here" == "" } {
  287. path.hint;
  288. #return;
  289. };
  290. };
  291. #local here {@map.Room.CID{}};
  292. #local path {@table.Keys{map.path.list; {${here}-%*}}};
  293. #local count {@slist.Size{$path}};
  294. #if { $count > 0 } {
  295. #local path {@fp.Transform{{$path};\@str.Replace{VALUE;{%*-%*};{&2}}}};
  296. #local path {@fp.Transform{{$path};\@mslp.Exec{{path.Walk ${here}-VALUE};<139>\@str.Replace{VALUE;{$area的};{}}<299>}} };
  297. path.hint $path;
  298. };
  299. #else {
  300. path.hint;
  301. };
  302. };
  303. ///=== {
  304. // ## path.List
  305. // 查看本房间都有哪些关联路径,类似于北侠 node 或者 walk 命令。
  306. // };
  307. #alias {path.List} {
  308. #local area {@map.GetArea{}};
  309. #if { "$area" == "" } {
  310. event.HandleOnce {map/GotArea} {path.List} {map/path} {path.List};
  311. #delay map.List.retry {map.GetArea} 1;
  312. #return;
  313. };
  314. #local here {@map.Room.CID{}};
  315. #local count {0};
  316. #local name {};
  317. #foreach {*map.path.list[]} {name} {
  318. #if { "$name" == "${here}-%*" } {
  319. #local path {$map.path.list[$name]};
  320. #local size {@slist.Size{$path}};
  321. #echo {%s(<129>$size<299>): %s} {@mslp.Exec{{path.Walk $name};<139>$name<299>}} {<169>$path<099>};
  322. #math count {$count + 1};
  323. };
  324. };
  325. #if { $count > 0 } {
  326. okLog 共列出 $count 条关联路径。;
  327. };
  328. #else {
  329. warnLog 尚未找到本房间的关联路径。;
  330. };
  331. path.message 为本房间录制更多路径请使用 {@mslp.Exec{path.Trace;path.Trace}}。;
  332. };
  333. ///=== {
  334. // #@ path.Get <路径名称>
  335. // 查询并返回路径。
  336. // };
  337. #func {path.Get} {
  338. #local name {%0};
  339. #if { "$name" == "" } {
  340. #return {};
  341. };
  342. #return {$map.path.list[$name]};
  343. };
  344. VAR {千里通步进推进器} {path.Walk.Stepper} {};
  345. ///=== {
  346. // ## path.Walk.SetStepper <步进推进器>
  347. // 设置路径行走的步进推进器。如果设置了步进推进器,则千里通将进入步进推进模式。
  348. // 你推一步,它就走一步,不推就不走。
  349. // 你可以用 path.Walk.Resume 来推动千里通继续前进,一次一步。
  350. // 注意,步进推进器仅在下一次行走任务中生效。一旦行走结束,无论成功失败,步进推进器设置将被清空。
  351. // };
  352. #alias {path.Walk.SetStepper} {
  353. #local stepper {%21};
  354. #if { "$stepper" == "" } {
  355. xtt.Usage %90;
  356. #return;
  357. };
  358. #var path.Walk.Stepper {$stepper};
  359. };
  360. ///=== {
  361. // ## path.Walk <路径名称> [<回调代码>]
  362. // 沿着指定的路径名称执行行走任务。行走完成后,执行回调代码。
  363. // 如果省略回调代码,用户仍可通过订阅 map/walk/continue 事件获得回调机会。
  364. // 注意约定的回调钩子名称为 map/path/end。
  365. // };
  366. #alias {path.Walk} {
  367. #local name {%1};
  368. #local callback {%22};
  369. #local path {$map.path.list[$name]};
  370. #if { "$name" == "" } {
  371. xtt.Usage %90;
  372. #return;
  373. };
  374. #if { "$callback" != "" } {
  375. #line sub {escapes;var} event.HandleOnce map/walk/continue {map/path/end} {map/path} {$callback};
  376. };
  377. path.WalkSteps {$path};
  378. };
  379. ///=== {
  380. // ## path.WalkSteps <路径> [<回调代码>]
  381. // 走一个自定义路径。行走完成后,执行回调代码。
  382. // };
  383. #alias {path.WalkSteps} {
  384. #local path {%1};
  385. #local callback {%22};
  386. #local len {@slist.Size{$path}};
  387. #if { $len == 0 } {
  388. xtt.Usage %90;
  389. #return;
  390. };
  391. #local path {@fp.Transform{{$path};path.step {VALUE}}};
  392. #if { $len > 1 } {
  393. env.Set brief 3;
  394. #local path {@slist.Insert{{$path};$len;path.last-step}};
  395. };
  396. #if { "$callback" != "" } {
  397. #line sub {escapes;var} event.HandleOnce map/walk/continue {map/path/end} {map/path} {$callback};
  398. };
  399. #path load {$path;path.end};
  400. sync.Wait {#path walk};
  401. };
  402. #alias {path.step} {
  403. #class path.step open;
  404. #local cmd {@dir.Long{%1}};
  405. #if { "$path.Walk.Stepper" === "" } {
  406. event.HandleOnce {map/walk/continue} {map/step} {map/path} {path.step.next};
  407. };
  408. #else {
  409. #line sub {escapes;var} event.HandleOnce {map/walk/continue} {map/step} {map/path} {$path.Walk.Stepper};
  410. };
  411. event.HandleOnce {map/walk/failed} {map/step} {map/path} {#path destroy; path.last-step; path.end};
  412. map.step.Try {} {$cmd} {};
  413. };
  414. VAR {停止走路标志} path.walk.stop {0};
  415. #alias {path.Walk.Stop} {
  416. #var path.walk.stop 1;
  417. };
  418. #alias {path.Walk.Resume} {
  419. #var path.walk.stop 0;
  420. path.step.next;
  421. };
  422. #alias {path.Walk.Reset} {
  423. #var path.walk.stop 0;
  424. #path destroy;
  425. };
  426. #alias {path.step.next} {
  427. #if { ! $path.walk.stop } {
  428. #path walk;
  429. };
  430. };
  431. #alias {path.last-step} {
  432. env.UnSet brief;
  433. #path walk;
  434. };
  435. #alias {path.end} {
  436. okLog 行走完成。;
  437. #local path {};
  438. #path get info {path};
  439. #if { $path[position] <= $path[length] } {
  440. sync.Wait {
  441. #path run;
  442. };
  443. };
  444. #var path.Walk.Stepper {};
  445. event.Emit {map/walk/continue} {map/path/end};
  446. event.HandleOnce {map/GotArea} {path/end} {map/path} {path.Hint};
  447. map.GetArea;
  448. };
  449. #func {path.isTracing} {
  450. #if { &map.path.current[] > 0 } {
  451. #return 1;
  452. };
  453. #else {
  454. #return 0;
  455. };
  456. };
  457. #alias {path.message} {
  458. #local msg {%0};
  459. #local trace {@mslp.Exec{path.Trace;<159>path.Trace<299>}};
  460. #local cancel {@mslp.Exec{path.Cancel;<119>path.Cancel<299>}};
  461. #local finish {@mslp.Exec{path.Finish;<129>path.Finish<299>}};
  462. #if { @path.isTracing{} } {
  463. infoLog <159>$msg<299> 取消录制请使用 {$cancel},完成录制请使用 {$finish}。<299>;
  464. };
  465. #else {
  466. infoLog <159>$msg<299> 要想录制新路径,请使用 {$trace}。<299>;
  467. };
  468. path.hint %0;
  469. };
  470. VAR {map/path 模块提醒内容} {path-hint} {NOTHING};
  471. #alias {path.hint} {
  472. #local hint {%0};
  473. #if { "$path-hint" == "$hint" } {
  474. #return;
  475. };
  476. #var path-hint {$hint};
  477. #local trace {【@mslp.Exec{path.Trace;<159>开始录制<299>}】};
  478. #local cancel {【@mslp.Exec{path.Cancel;<119>取消录制<299>}】};
  479. #local finish {【@mslp.Exec{path.Finish;<129>完成录制<299>}】};
  480. #if { @path.isTracing{} } {
  481. #local hint {$finish $cancel <159>$hint<299>};
  482. };
  483. #else {
  484. #local hint {$trace <159>$hint<299>};
  485. };
  486. prompt.Set {{path}{$hint}};
  487. };