wsmud_Trigger

武神传说 MUD

  1. // ==UserScript==
  2. // @name wsmud_Trigger
  3. // @namespace cqv3
  4. // @version 0.0.46
  5. // @date 03/03/2019
  6. // @modified 08/09/2022
  7. // @homepage https://greasyfork.org/zh-CN/scripts/378984
  8. // @description 武神传说 MUD
  9. // @author Bob.cn, 初心, 白三三
  10. // @match http://*.wsmud.com/*
  11. // @match http://*.wamud.com/*
  12. // @run-at document-end
  13. // @require https://cdn.staticfile.org/vue/2.2.2/vue.min.js
  14. // @grant unsafeWindow
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // @grant GM_deleteValue
  18. // @grant GM_listValues
  19. // @grant GM_setClipboard
  20. // ==/UserScript==
  21.  
  22. (function () {
  23. 'use strict';
  24.  
  25. function CopyObject(obj) {
  26. return JSON.parse(JSON.stringify(obj));
  27. }
  28. function is_match(src, input) {
  29. if (src.length == 0 && input.length == 0) {
  30. return true;
  31. }
  32. if (src[0] == "*" && src.length == 1) {
  33. return true;
  34. }
  35. if (src.length == 0 || input.length == 0) {
  36. return false;
  37. }
  38. if (src[0] == "?") {
  39. return is_match(src.substring(1), input.substring(1));
  40. } else
  41. if (src[0] == "*") {
  42. return is_match(src.substring(1), input) || is_match(src.substring(1), input.substring(1)) || is_match(src, input.substring(1));
  43. } else
  44. if (src[0] == input[0]) {
  45. return is_match(src.substring(1), input.substring(1));
  46. } else {
  47. return false;
  48. }
  49.  
  50. }
  51.  
  52. /***********************************************************************************\
  53. Notification Center
  54. \***********************************************************************************/
  55.  
  56. class Notification {
  57. constructor(name, params) {
  58. this.name = name;
  59. this.params = params;
  60. }
  61. }
  62.  
  63. class NotificationObserver {
  64. constructor(targetName, action) {
  65. this.targetName = targetName;
  66. this.action = action;
  67. }
  68. }
  69.  
  70. const NotificationCenter = {
  71. observe: function (notificationName, action) {
  72. const index = this._getOberverIndex();
  73. const observer = new NotificationObserver(notificationName, action);
  74. this._observers[index] = observer;
  75. return index;
  76. },
  77. removeOberver: function (index) {
  78. delete this._observers[index];
  79. },
  80. /**
  81. * @param {Notification} notification
  82. */
  83. post: function (notification) {
  84. for (const key in this._observers) {
  85. if (!this._observers.hasOwnProperty(key)) continue;
  86. const observer = this._observers[key];
  87. if (observer.targetName != notification.name) continue;
  88. observer.action(notification.params);
  89. }
  90. },
  91.  
  92. _observerCounter: 0,
  93. _observers: {},
  94. _getOberverIndex: function () {
  95. const index = this._observerCounter;
  96. this._observerCounter += 1;
  97. return index;
  98. }
  99. };
  100.  
  101. /***********************************************************************************\
  102. Monitor Center
  103. \***********************************************************************************/
  104.  
  105. class Monitor {
  106. constructor(run) {
  107. this.run = run;
  108. }
  109. }
  110.  
  111. const MonitorCenter = {
  112. addMonitor: function (monitor) {
  113. this._monitors.push(monitor);
  114. },
  115. run: function () {
  116. for (const monitor of this._monitors) {
  117. monitor.run();
  118. }
  119. },
  120.  
  121. _monitors: []
  122. };
  123.  
  124. /***********************************************************************************\
  125. Trigger Template And Trigger
  126. \***********************************************************************************/
  127.  
  128. //---------------------------------------------------------------------------
  129. // Trigger Template
  130. //---------------------------------------------------------------------------
  131.  
  132. const EqualAssert = function (lh, rh) {
  133. return lh == rh;
  134. };
  135.  
  136. const ContainAssert = function (lh, rh) {
  137. if (/^\s*\*?\s*$/.test(lh)) return true;
  138. const list = lh.split("|");
  139. return list.indexOf(rh) != -1;
  140. };
  141. const ContainReverseAssert = function (lh, rh) {
  142. console.log(lh, rh);
  143. if (/^\s*\*?\s*$/.test(lh)) return true;
  144. const list = lh.split("|");
  145. return list.indexOf(rh) == -1;
  146. };
  147.  
  148. const KeyAssert = function (lh, rh) {
  149. if (/^\s*\*?\s*$/.test(lh)) return true;
  150. const list = lh.split("|");
  151. for (const key of list) {
  152. if (rh.indexOf(key) != -1) return true;
  153. }
  154. return false;
  155. };
  156.  
  157. class Filter {
  158. constructor(name, type, defaultValue, assert) {
  159. this.name = name;
  160. this.type = type;
  161. this.defaultValue = defaultValue;
  162. this.assert = assert == null ? EqualAssert : assert;
  163. }
  164. description(value) {
  165. if (value != null) {
  166. this._desc = value;
  167. return;
  168. }
  169. return this._desc == null ? this.name : this._desc;
  170. }
  171. }
  172.  
  173. class SelectFilter extends Filter {
  174. constructor(name, options, defaultNumber, assert) {
  175. const defaultValue = options[defaultNumber];
  176. super(name, "select", defaultValue, assert);
  177. this.options = options;
  178. }
  179. }
  180.  
  181. const InputFilterFormat = {
  182. number: "数字",
  183. text: "文本"
  184. };
  185.  
  186. class InputFilter extends Filter {
  187. /**
  188. * @param {String} name
  189. * @param {InputFilterFormat} format
  190. * @param {*} defaultValue
  191. */
  192. constructor(name, format, defaultValue, assert) {
  193. super(name, "input", defaultValue, assert);
  194. this.format = format;
  195. }
  196. }
  197.  
  198. class TriggerTemplate {
  199. constructor(event, filters, introdution) {
  200. this.event = event;
  201. this.filters = filters;
  202. this.introdution = `${introdution}\n// 如需更多信息,可以到论坛触发器版块发帖。`;
  203. }
  204. getFilter(name) {
  205. for (const filter of this.filters) {
  206. if (filter.name == name) return filter;
  207. }
  208. return null;
  209. }
  210. }
  211.  
  212. const TriggerTemplateCenter = {
  213. add: function (template) {
  214. this._templates[template.event] = template;
  215. },
  216. getAll: function () {
  217. return Object.values(this._templates);
  218. },
  219. get: function (event) {
  220. return this._templates[event];
  221. },
  222.  
  223. _templates: {},
  224. };
  225.  
  226. //---------------------------------------------------------------------------
  227. // Trigger
  228. //---------------------------------------------------------------------------
  229.  
  230. class Trigger {
  231. constructor(name, template, conditions, source) {
  232. this.name = name;
  233. this.template = template;
  234. this.conditions = conditions;
  235. this.source = source;
  236. this._action = function (params) {
  237. let realParams = CopyObject(params);
  238. for (const key in conditions) {
  239. if (!conditions.hasOwnProperty(key)) continue;
  240. const filter = template.getFilter(key);
  241. const fromUser = conditions[key];
  242. const fromGame = params[key];
  243. if (!filter.assert(fromUser, fromGame)) return;
  244. delete realParams[key];
  245. }
  246. let realSource = source;
  247. for (const key in realParams) {
  248. realSource = `($${key}) = ${realParams[key]}\n${realSource}`;
  249. }
  250. if (/\/\/\s*~silent\s*\n/.test(source) == false) {
  251. realSource = `@print 💡<hio>触发=>${name}</hio>\n${realSource}`;
  252. }
  253. ToRaid.perform(realSource, name, false);
  254. };
  255. this._observerIndex = null;
  256. }
  257.  
  258. event() { return this.template.event; }
  259. active() { return this._observerIndex != null; }
  260.  
  261. _activate() {
  262. if (this._observerIndex != null) return;
  263. if (this.template == null) return;
  264. this._observerIndex = NotificationCenter.observe(this.template.event, this._action);
  265. }
  266. _deactivate() {
  267. if (this._observerIndex == null) return;
  268. NotificationCenter.removeOberver(this._observerIndex);
  269. this._observerIndex = null;
  270. }
  271. }
  272.  
  273. class TriggerData {
  274. constructor(name, event, conditions, source, active) {
  275. this.name = name;
  276. this.event = event;
  277. this.conditions = conditions;
  278. this.source = source;
  279. this.active = active;
  280. }
  281. }
  282.  
  283. const TriggerCenter = {
  284. run: function () {
  285. const allData = GM_getValue(this._saveKey(), {});
  286. for (const name in allData) {
  287. this._loadTrigger(name);
  288. }
  289. },
  290. reload: function () {
  291. for (const name in this._triggers) {
  292. if (!this._triggers.hasOwnProperty(name)) continue;
  293. const trigger = this._triggers[name];
  294. trigger._deactivate();
  295. delete this._triggers[name];
  296. }
  297. this.run();
  298. },
  299.  
  300. // for upload and download
  301. getAllData: function () {
  302. return GM_getValue(this._saveKey(), {});
  303. },
  304. corver: function (triggerDatas) {
  305. for (const old of this.getAll()) {
  306. this.remove(old.name);
  307. }
  308. for (const name in triggerDatas) {
  309. const trigger = triggerDatas[name];
  310. this.create(trigger.name, trigger.event, trigger.conditions, trigger.source, trigger.active);
  311. }
  312. },
  313.  
  314. getAll: function () {
  315. return Object.values(this._triggers);
  316. },
  317. create: function (name, event, conditions, source, active) {
  318. const checkResult = this._checkName(name);
  319. if (checkResult != true) return checkResult;
  320.  
  321. const theActive = active == null ? false : active;
  322. const data = new TriggerData(name, event, conditions, source, theActive);
  323. this._updateData(data);
  324.  
  325. this._loadTrigger(name);
  326. return true;
  327. },
  328. modify: function (originalName, name, conditions, source) {
  329. const trigger = this._triggers[originalName];
  330. if (trigger == null) return "修改不存在的触发器?";
  331.  
  332. const event = trigger.event();
  333. if (originalName == name) {
  334. const data = new TriggerData(name, event, conditions, source, trigger.active());
  335. this._updateData(data);
  336. this._reloadTrigger(name);
  337. return true;
  338. }
  339.  
  340. const result = this.create(name, event, conditions, source);
  341. if (result == true) {
  342. this.remove(originalName);
  343. this._loadTrigger(name);
  344. }
  345. return result;
  346. },
  347. remove: function (name) {
  348. const trigger = this._triggers[name];
  349. if (trigger == null) return;
  350.  
  351. trigger._deactivate();
  352. delete this._triggers[name];
  353. let allData = GM_getValue(this._saveKey(), {});
  354. delete allData[name];
  355. GM_setValue(this._saveKey(), allData);
  356. },
  357.  
  358. activate: function (name) {
  359.  
  360. for (let x in this._triggers) {
  361. if (is_match(name, x)) {
  362. const trigger = this._triggers[x];
  363. if (trigger == null) continue;
  364. if (trigger.active()) continue;
  365. trigger._activate();
  366. let data = this._getData(x);
  367. data.active = true;
  368. this._updateData(data);
  369. }
  370.  
  371. }
  372.  
  373. },
  374. deactivate: function (name) {
  375. for (let x in this._triggers) {
  376. if (is_match(name, x)) {
  377. const trigger = this._triggers[x];
  378. if (trigger == null) continue;
  379. if (!trigger.active()) continue;
  380. trigger._deactivate();
  381. let data = this._getData(x);
  382. data.active = false;
  383. this._updateData(data);
  384. }
  385.  
  386. }
  387.  
  388. },
  389. _triggers: {},
  390.  
  391. _saveKey: function () {
  392. return `${Role.id}@triggers`;
  393. },
  394. _reloadTrigger: function (name) {
  395. const oldTrigger = this._triggers[name];
  396. if (oldTrigger != null) {
  397. oldTrigger._deactivate();
  398. }
  399. this._loadTrigger(name);
  400. },
  401. _loadTrigger: function (name) {
  402. const data = this._getData(name);
  403. if (data == null) return;
  404. // patch new trigger
  405. if (data['event'] === '新聊天信息' && data['conditions']['忽略发言人'] === undefined) {
  406. data['conditions']['忽略发言人'] = ''
  407. }
  408. const trigger = this._toTrigger(data);
  409. this._triggers[name] = trigger;
  410. if (data.active) {
  411. trigger._activate();
  412. }
  413. },
  414. _getData: function (name) {
  415. let allData = GM_getValue(this._saveKey(), {});
  416. const data = allData[name];
  417. return data;
  418. },
  419. _updateData: function (data) {
  420. let allData = GM_getValue(this._saveKey(), {});
  421. allData[data.name] = data;
  422. GM_setValue(this._saveKey(), allData);
  423. },
  424. _toTrigger: function (data) {
  425. const template = TriggerTemplateCenter.get(data.event);
  426. const trigger = new Trigger(data.name, template, data.conditions, data.source);
  427. return trigger;
  428. },
  429. _checkName: function (name) {
  430. if (this._triggers[name] != null) return "无法修改名称,已经存在同名触发器!";
  431. if (!/\S+/.test(name)) return "触发器的名称不能为空。";
  432. if (!/^[_a-zA-Z0-9\u4e00-\u9fa5]+$/.test(name)) return "触发器的名称只能使用中文、英文和数字字符。";
  433. return true;
  434. }
  435. };
  436.  
  437. /***********************************************************************************\
  438. WSMUD
  439. \***********************************************************************************/
  440.  
  441. var WG = null;
  442. var messageAppend = null;
  443. var messageClear = null;
  444. var ToRaid = null;
  445. var Role = null;
  446.  
  447.  
  448. //---------------------------------------------------------------------------
  449. // status
  450. //---------------------------------------------------------------------------
  451.  
  452. (function () {
  453. const type = new SelectFilter("改变类型", ["新增", "移除", "层数刷新"], 0);
  454. const value = new InputFilter("BuffId", InputFilterFormat.text, "weapon", ContainAssert);
  455. const target = new SelectFilter("触发对象", ["自己", "他人"], 0);
  456. let filters = [type, value, target];
  457. const intro = `// Buff状态改变触发器
  458. // 触发对象id:(id)
  459. // buff的sid:(sid)
  460. // buff层数:(count)
  461. // duration持续时间:(duration)`;
  462. const t = new TriggerTemplate("Buff状态改变", filters, intro);
  463. TriggerTemplateCenter.add(t);
  464.  
  465. const run = function () {
  466. const post = function (data, sid, type) {
  467. let params = {
  468. "改变类型": type,
  469. "BuffId": sid,
  470. "触发对象": data.id == Role.id ? "自己" : "他人"
  471. };
  472. params["id"] = data.id;
  473. params["sid"] = sid;
  474. params["count"] = 0;
  475. params["duration"] = 0;
  476. if (data.count != null) params["count"] = data.count;
  477. if (data.duration != null) params["duration"] = data.duration;
  478. const n = new Notification("Buff状态改变", params);
  479. NotificationCenter.post(n);
  480. };
  481. WG.add_hook("status", data => {
  482. if (data.action == null || data.id == null || data.sid == null) return;
  483. const types = {
  484. "add": "新增",
  485. "remove": "移除",
  486. "refresh": "层数刷新"
  487. };
  488. const type = types[data.action];
  489. if (type == null) return;
  490. if (data.sid instanceof Array) {
  491. for (const s of data.sid) {
  492. post(data, s, type);
  493. }
  494. } else {
  495. post(data, data.sid, type);
  496. }
  497. });
  498. };
  499. const monitor = new Monitor(run);
  500. MonitorCenter.addMonitor(monitor);
  501. })();
  502.  
  503. //---------------------------------------------------------------------------
  504. // msg
  505. //---------------------------------------------------------------------------
  506.  
  507. (function () {
  508. const channel = new SelectFilter(
  509. "频道",
  510. ["全部", "世界", "队伍", "门派", "全区", "帮派", "谣言", "系统"],
  511. 0,
  512. function (fromUser, fromGame) {
  513. if (fromUser == "全部") return true;
  514. return fromUser == fromGame;
  515. }
  516. );
  517. const talker = new InputFilter("发言人", InputFilterFormat.text, "", ContainAssert);
  518. const pass_talker = new InputFilter("忽略发言人", InputFilterFormat.text, "", ContainReverseAssert);
  519. const key = new InputFilter("关键字", InputFilterFormat.text, "", KeyAssert);
  520. let filters = [channel, talker, pass_talker, key];
  521. const intro = `// 新聊天信息触发器
  522. // 聊天信息内容:(content)
  523. // 发言人:(name)
  524. // 发言人id:(id)
  525. // 频道:(channel)`;
  526. const t = new TriggerTemplate("新聊天信息", filters, intro);
  527. TriggerTemplateCenter.add(t);
  528.  
  529. const run = function () {
  530. WG.add_hook("msg", data => {
  531. if (data.ch == null || data.content == null) return;
  532. const types = {
  533. "chat": "世界",
  534. "tm": "队伍",
  535. "fam": "门派",
  536. "es": "全区",
  537. "pty": "帮派",
  538. "rumor": "谣言",
  539. "sys": "系统"
  540. };
  541. const channel = types[data.ch];
  542. if (channel == null) return;
  543. const name = data.name == null ? "无" : data.name;
  544. const id = data.uid == null ? null : data.uid;
  545. const datacontent = data.content.replace(/\n/g, "")
  546. let params = {
  547. "频道": channel,
  548. "发言人": name,
  549. "关键字": data.content,
  550. "忽略发言人": name
  551. };
  552. params["content"] = datacontent;
  553. params["name"] = name;
  554. params["id"] = id;
  555. params["channel"] = channel;
  556. const n = new Notification("新聊天信息", params);
  557. NotificationCenter.post(n);
  558. });
  559. };
  560. const monitor = new Monitor(run);
  561. MonitorCenter.addMonitor(monitor);
  562. })();
  563.  
  564. //---------------------------------------------------------------------------
  565. // item add
  566. //---------------------------------------------------------------------------
  567.  
  568. (function () {
  569. const name = new InputFilter("人物名称", InputFilterFormat.text, "", KeyAssert);
  570. name.description("人名关键字");
  571. let filters = [name];
  572. const intro = `// 人物刷新触发器
  573. // 刷新人物id:(id)
  574. // 刷新人物名称:(name)`;
  575. const t = new TriggerTemplate("人物刷新", filters, intro);
  576. TriggerTemplateCenter.add(t);
  577.  
  578. const run = function () {
  579. WG.add_hook("itemadd", data => {
  580. if (data.name == null || data.id == null) return;
  581. let params = {
  582. "人物名称": data.name,
  583. };
  584. params["id"] = data.id;
  585. params["name"] = data.name;
  586. const n = new Notification("人物刷新", params);
  587. NotificationCenter.post(n);
  588. });
  589. };
  590. const monitor = new Monitor(run);
  591. MonitorCenter.addMonitor(monitor);
  592. })();
  593.  
  594. //---------------------------------------------------------------------------
  595. // dialog pack
  596. //---------------------------------------------------------------------------
  597.  
  598. (function () {
  599. const name = new InputFilter("名称关键字", InputFilterFormat.text, "", KeyAssert);
  600. let filters = [name];
  601. const intro = `// 物品拾取触发器
  602. // 拾取物品id:(id)
  603. // 拾取物品名称:(name)
  604. // 拾取物品数量:(count)
  605. // 物品品质:(quality) 值:白、绿、蓝、黄、紫、橙、红、未知`;
  606. const t = new TriggerTemplate("物品拾取", filters, intro);
  607. TriggerTemplateCenter.add(t);
  608.  
  609. const run = function () {
  610. WG.add_hook("dialog", function (data) {
  611. if (data.dialog != "pack" || data.id == null || data.name == null || data.count == null || data.remove != null) return;
  612. let params = {
  613. "名称关键字": data.name,
  614. };
  615. params["id"] = data.id;
  616. params["name"] = data.name;
  617. params["count"] = data.count;
  618. let quality = "未知";
  619. const tag = /<\w{3}>/.exec(data.name)[0];
  620. const tagMap = {
  621. "<wht>": "白",
  622. "<hig>": "绿",
  623. "<hic>": "蓝",
  624. "<hiy>": "黄",
  625. "<HIZ>": "紫",
  626. "<hio>": "橙",
  627. "<ord>": "红"
  628. }
  629. quality = tagMap[tag];
  630. params["quality"] = quality;
  631. const n = new Notification("物品拾取", params);
  632. NotificationCenter.post(n);
  633. });
  634. };
  635. const monitor = new Monitor(run);
  636. MonitorCenter.addMonitor(monitor);
  637. })();
  638.  
  639. //---------------------------------------------------------------------------
  640. // text
  641. //---------------------------------------------------------------------------
  642.  
  643. (function () {
  644. const name = new InputFilter("关键字", InputFilterFormat.text, "", KeyAssert);
  645. let filters = [name];
  646. const intro = `// 新提示信息触发器
  647. // 提示信息:(text)`;
  648. const t = new TriggerTemplate("新提示信息", filters, intro);
  649. TriggerTemplateCenter.add(t);
  650.  
  651. const run = function () {
  652. WG.add_hook("text", data => {
  653. if (data.msg == null) return;
  654. let params = {
  655. "关键字": data.msg,
  656. };
  657. params["text"] = data.msg;
  658. const n = new Notification("新提示信息", params);
  659. NotificationCenter.post(n);
  660. });
  661. };
  662. const monitor = new Monitor(run);
  663. MonitorCenter.addMonitor(monitor);
  664. })();
  665.  
  666. //---------------------------------------------------------------------------
  667. // combat
  668. //---------------------------------------------------------------------------
  669.  
  670. (function () {
  671. const type = new SelectFilter("类型", ["进入战斗", "脱离战斗"], 0);
  672. let filters = [type];
  673. const intro = "// 战斗状态切换触发器";
  674. const t = new TriggerTemplate("战斗状态切换", filters, intro);
  675. TriggerTemplateCenter.add(t);
  676.  
  677. const run = function () {
  678. WG.add_hook("combat", data => {
  679. let params = null;
  680. if (data.start != null && data.start == 1) {
  681. params = { "类型": "进入战斗" };
  682. } else if (data.end != null && data.end == 1) {
  683. params = { "类型": "脱离战斗" };
  684. }
  685. const n = new Notification("战斗状态切换", params);
  686. NotificationCenter.post(n);
  687. });
  688. WG.add_hook("text", function (data) {
  689. if (data.msg == null) return;
  690. if (data.msg.indexOf('只能在战斗中使用') != -1 || data.msg.indexOf('这里不允许战斗') != -1 || data.msg.indexOf('没时间这么做') != -1) {
  691. const params = { "类型": "脱离战斗" };
  692. const n = new Notification("战斗状态切换", params);
  693. NotificationCenter.post(n);
  694. }
  695. });
  696. };
  697. const monitor = new Monitor(run);
  698. MonitorCenter.addMonitor(monitor);
  699. })();
  700.  
  701. //---------------------------------------------------------------------------
  702. // combat
  703. //---------------------------------------------------------------------------
  704.  
  705. (function () {
  706. const type = new SelectFilter("类型", ["已经死亡", "已经复活"], 0);
  707. let filters = [type];
  708. const intro = "// 死亡状态改变触发器";
  709. const t = new TriggerTemplate("死亡状态改变", filters, intro);
  710. TriggerTemplateCenter.add(t);
  711.  
  712. const run = function () {
  713. WG.add_hook("die", data => {
  714. const value = data.relive == null ? "已经死亡" : "已经复活";
  715. let params = {
  716. "类型": value
  717. };
  718. const n = new Notification("死亡状态改变", params);
  719. NotificationCenter.post(n);
  720. });
  721. };
  722. const monitor = new Monitor(run);
  723. MonitorCenter.addMonitor(monitor);
  724. })();
  725.  
  726. //---------------------------------------------------------------------------
  727. // time
  728. //---------------------------------------------------------------------------
  729.  
  730. (function () {
  731. const hours = [
  732. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  733. 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
  734. 20, 21, 22, 23
  735. ];
  736. const minutes = [
  737. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  738. 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
  739. 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
  740. 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
  741. 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
  742. 50, 51, 52, 53, 54, 55, 56, 57, 58, 59
  743. ];
  744. const hour = new SelectFilter("时", hours, 0, EqualAssert);
  745. const minute = new SelectFilter("分", minutes, 0, EqualAssert);
  746. const second = new SelectFilter("秒", minutes, 0, EqualAssert);
  747. let filters = [hour, minute, second];
  748. const intro = "// 时辰已到触发器";
  749. const t = new TriggerTemplate("时辰已到", filters, intro);
  750. TriggerTemplateCenter.add(t);
  751.  
  752. const run = function () {
  753. function timer() {
  754. const date = new Date();
  755. const params = {
  756. "时": date.getHours(),
  757. "分": date.getMinutes(),
  758. "秒": date.getSeconds()
  759. };
  760. const n = new Notification("时辰已到", params);
  761. NotificationCenter.post(n);
  762.  
  763. const nowTime = Date.now();
  764. const nextTime = parseInt((nowTime + 1e3) / 1e3) * 1e3 + 1;
  765.  
  766. setTimeout(() => {
  767. timer();
  768. }, nextTime - nowTime);
  769. }
  770. timer();
  771. };
  772. const monitor = new Monitor(run);
  773. MonitorCenter.addMonitor(monitor);
  774. })();
  775.  
  776. //---------------------------------------------------------------------------
  777. // dispfm
  778. //---------------------------------------------------------------------------
  779.  
  780. (function () {
  781. const sid = new InputFilter("技能id", InputFilterFormat.text, "", ContainAssert);
  782. let filters = [sid];
  783. const intro = `// 技能释放触发器
  784. // 技能id:(id)
  785. // 出招时间:(rtime)
  786. // 冷却时间:(distime)`;
  787. const t = new TriggerTemplate("技能释放", filters, intro);
  788. TriggerTemplateCenter.add(t);
  789.  
  790. const sid1 = new InputFilter("技能id", InputFilterFormat.text, "", ContainAssert);
  791. let filters1 = [sid1];
  792. const intro1 = `// 技能冷却结束触发器
  793. // 技能id:(id)`;
  794. const t1 = new TriggerTemplate("技能冷却结束", filters1, intro1);
  795. TriggerTemplateCenter.add(t1);
  796.  
  797. const run = function () {
  798. WG.add_hook("dispfm", data => {
  799. if (data.id == null || data.distime == null || data.rtime == null) return;
  800. let params = {
  801. "技能id": data.id
  802. };
  803. params["id"] = data.id;
  804. params["rtime"] = data.rtime;
  805. params["distime"] = data.distime;
  806. const n = new Notification("技能释放", params);
  807. NotificationCenter.post(n);
  808.  
  809. setTimeout(_ => {
  810. let params = {
  811. "技能id": data.id
  812. };
  813. params["id"] = data.id;
  814. const n = new Notification("技能冷却结束", params);
  815. NotificationCenter.post(n);
  816. }, data.distime);
  817. });
  818. };
  819. const monitor = new Monitor(run);
  820. MonitorCenter.addMonitor(monitor);
  821. })();
  822.  
  823. //---------------------------------------------------------------------------
  824. // hp mp
  825. //---------------------------------------------------------------------------
  826.  
  827. var RoomItems = {};
  828.  
  829. (function () {
  830. const name = new InputFilter("人名关键字", InputFilterFormat.text, "", KeyAssert);
  831. const type = new SelectFilter("类型", ["气血", "内力"], 0, EqualAssert);
  832. const compare = new SelectFilter("当", ["低于", "高于"], 0, EqualAssert);
  833. const valueType = new SelectFilter("值类型", ["百分比", "数值"], 0, EqualAssert);
  834. const value = new InputFilter("值", InputFilterFormat.number, 0, function (fromUser, fromGame) {
  835. const parts = fromGame.split(";");
  836. const oldvalue = parseFloat(parts[0]);
  837. const newvalue = parseFloat(parts[1]);
  838. if (oldvalue >= fromUser && newvalue < fromUser) return true;
  839. if (oldvalue <= fromUser && newvalue > fromUser) return true;
  840. return false;
  841. });
  842. let filters = [name, type, compare, valueType, value];
  843. const intro = `// 气血内力改变触发器
  844. // 人物id:(id)
  845. // 人物当前气血:(hp)
  846. // 人物最大气血:(maxHp)
  847. // 人物当前内力:(mp)
  848. // 人物最大内力:(maxMp)`;
  849. const t = new TriggerTemplate("气血内力改变", filters, intro);
  850. TriggerTemplateCenter.add(t);
  851.  
  852. const run = function () {
  853. WG.add_hook("items", data => {
  854. if (data.items == null) return;
  855. RoomItems = {};
  856. for (const item of data.items) {
  857. RoomItems[item.id] = CopyObject(item);
  858. }
  859. });
  860. WG.add_hook("itemadd", data => {
  861. RoomItems[data.id] = CopyObject(data);
  862. });
  863. const decorate = function (params, item) {
  864. params["id"] = item.id;
  865. params["hp"] = item.hp;
  866. params["maxHp"] = item.max_hp;
  867. params["mp"] = item.mp;
  868. params["maxMp"] = item.max_mp;
  869. };
  870. WG.add_hook("sc", data => {
  871. if (data.id == null) return;
  872. let item = RoomItems[data.id];
  873. if (item == null) return;
  874. if (data.hp != null) {
  875. let compare = "低于";
  876. if (data.hp > item.hp) compare = "高于";
  877. const oldValue = item.hp;
  878. const oldPer = (item.hp / item.max_hp * 100).toFixed(2);
  879. item.hp = data.hp;
  880. if (item.max_hp < item.hp) item.max_hp = item.hp;
  881. if (data.max_hp != null) item.max_hp = data.max_hp;
  882. const newValue = item.hp;
  883. const newPer = (item.hp / item.max_hp * 100).toFixed(2);
  884. let params1 = {
  885. "人名关键字": item.name,
  886. "类型": "气血",
  887. "当": compare,
  888. "值类型": "百分比",
  889. "值": `${oldPer};${newPer}`
  890. };
  891. decorate(params1, item);
  892. const n1 = new Notification("气血内力改变", params1);
  893. NotificationCenter.post(n1);
  894. let params2 = {
  895. "人名关键字": item.name,
  896. "类型": "气血",
  897. "当": compare,
  898. "值类型": "数值",
  899. "值": `${oldValue};${newValue}`
  900. };
  901. decorate(params2, item);
  902. const n2 = new Notification("气血内力改变", params2);
  903. NotificationCenter.post(n2);
  904. }
  905. if (data.mp != null) {
  906. let compare = "低于";
  907. if (data.mp > item.mp) compare = "高于";
  908. const oldValue = item.mp;
  909. const oldPer = (item.mp / item.max_mp * 100).toFixed(2);
  910. item.mp = data.mp;
  911. if (item.max_mp < item.mp) item.max_mp = item.mp;
  912. if (data.max_mp != null) item.max_mp = data.max_mp;
  913. const newValue = item.mp;
  914. const newPer = (item.mp / item.max_mp * 100).toFixed(2);
  915. let params1 = {
  916. "人名关键字": item.name,
  917. "类型": "内力",
  918. "当": compare,
  919. "值类型": "百分比",
  920. "值": `${oldPer};${newPer}`
  921. };
  922. decorate(params1, item);
  923. const n1 = new Notification("气血内力改变", params1);
  924. NotificationCenter.post(n1);
  925. let params2 = {
  926. "人名关键字": item.name,
  927. "类型": "内力",
  928. "当": compare,
  929. "值类型": "数值",
  930. "值": `${oldValue};${newValue}`
  931. };
  932. decorate(params2, item);
  933. const n2 = new Notification("气血内力改变", params2);
  934. NotificationCenter.post(n2);
  935. }
  936. });
  937. };
  938. const monitor = new Monitor(run);
  939. MonitorCenter.addMonitor(monitor);
  940. })();
  941.  
  942. //---------------------------------------------------------------------------
  943. // damage
  944. //---------------------------------------------------------------------------
  945.  
  946. (function () {
  947. const name = new InputFilter("人名关键字", InputFilterFormat.text, "", KeyAssert);
  948. const valueType = new SelectFilter("值类型", ["百分比", "数值"], 0, EqualAssert);
  949. const value = new InputFilter("值", InputFilterFormat.number, 0, (fromUser, fromGame) => {
  950. const parts = fromGame.split(";");
  951. const oldvalue = parseFloat(parts[0]);
  952. const newvalue = parseFloat(parts[1]);
  953. if (oldvalue <= fromUser && newvalue > fromUser) return true;
  954. return false;
  955. });
  956. let filters = [name, valueType, value];
  957. const intro = `// 伤害已满触发器
  958. // 备注:限制条件-值 不支持多条件
  959. // 人物id:(id)
  960. // 人物名称:(name)
  961. // 伤害数值:(value)
  962. // 伤害百分比:(percent)`;
  963. const t = new TriggerTemplate("伤害已满", filters, intro);
  964. TriggerTemplateCenter.add(t);
  965.  
  966. const run = function () {
  967. const decorate = function (params, item, value, percent) {
  968. params["id"] = item.id;
  969. params["name"] = item.name;
  970. params["value"] = value;
  971. params["percent"] = percent;
  972. };
  973. WG.add_hook("sc", data => {
  974. if (data.id == null || data.damage == null) return;
  975. let item = RoomItems[data.id];
  976. if (item == null || item.id == null || item.name == null || item.max_hp == null) return;
  977. // 获取之前保存的伤害和伤害百分比
  978. const oldValue = item._damage == null ? 0 : item._damage;
  979. const oldPer = item._damagePer == null ? 0 : item._damagePer;
  980. const value = data.damage;
  981. const percent = (data.damage / item.max_hp * 100).toFixed(2);
  982. // 保存伤害和伤害百分比
  983. item._damage = value;
  984. item._damagePer = percent;
  985. let params1 = {
  986. "人名关键字": item.name,
  987. "值类型": "百分比",
  988. "值": `${oldPer};${percent}`
  989. };
  990. decorate(params1, item, value, percent);
  991. const n1 = new Notification("伤害已满", params1);
  992. NotificationCenter.post(n1);
  993. let params2 = {
  994. "人名关键字": item.name,
  995. "值类型": "数值",
  996. "值": `${oldValue};${value}`
  997. };
  998. decorate(params2, item, value, percent);
  999. const n2 = new Notification("伤害已满", params2);
  1000. NotificationCenter.post(n2);
  1001. });
  1002. };
  1003. const monitor = new Monitor(run);
  1004. MonitorCenter.addMonitor(monitor);
  1005. })();
  1006.  
  1007. /***********************************************************************************\
  1008. UI
  1009. \***********************************************************************************/
  1010.  
  1011. const Message = {
  1012. append: function (msg) {
  1013. messageAppend(msg);
  1014. },
  1015. clean: function () {
  1016. messageClear();
  1017. },
  1018. };
  1019.  
  1020. const UI = {
  1021. triggerHome: function () {
  1022. const content = `
  1023. <style>.breakText {word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}</style>
  1024. <span class="zdy-item" style="width:120px" v-for="t in triggers" :style="activeStyle(t)">
  1025. <div style="width: 30px; float: left; background-color: rgba(255, 255, 255, 0.31); border-radius: 4px;" v-on:click="editTrigger(t)">⚙</div>
  1026. <div class="breakText" style="width: 85px; float: right;" v-on:click="switchStatus(t)">{{ t.name }}</div>
  1027. </span>
  1028. `;
  1029. const rightText = "<span v-on:click='createTrigger()'><wht>新建</wht></span>";
  1030. UI._appendHtml("🍟 <hio>触发器</hio>", content, rightText);
  1031. new Vue({
  1032. el: '#app',
  1033. data: {
  1034. triggers: TriggerCenter.getAll()
  1035. },
  1036. methods: {
  1037. switchStatus: function (t) {
  1038. if (t.active()) {
  1039. TriggerCenter.deactivate(t.name);
  1040. } else {
  1041. TriggerCenter.activate(t.name);
  1042. }
  1043. UI.triggerHome();
  1044. },
  1045. editTrigger: UI.editTrigger,
  1046. activeStyle: function (t) {
  1047. if (t.active()) {
  1048. return {
  1049. "background-color": "#a0e6e0",
  1050. "border": "1px solid #7284ff",
  1051. "color": "#001bff"
  1052. };
  1053. } else {
  1054. return { };
  1055. }
  1056. },
  1057. createTrigger: UI.selectTriggerTemplate
  1058. }
  1059. });
  1060. },
  1061. selectTriggerTemplate: function () {
  1062. const content = `
  1063. <span class="zdy-item" style="width:120px" v-for="t in templates" v-on:click="select(t)">{{ t.event }}</span>
  1064. `;
  1065. const leftText = "<span v-on:click='back()'>< 返回</span>";
  1066. UI._appendHtml("<wht>选择触发事件</wht>", content, null, leftText);
  1067. new Vue({
  1068. el: '#app',
  1069. data: {
  1070. templates: TriggerTemplateCenter.getAll()
  1071. },
  1072. methods: {
  1073. select: UI.createTrigger,
  1074. back: UI.triggerHome
  1075. }
  1076. });
  1077. },
  1078. createTrigger: function (template) {
  1079. UI._updateTrigger(template);
  1080. },
  1081. editTrigger: function (trigger) {
  1082. UI._updateTrigger(trigger.template, trigger);
  1083. },
  1084. _updateTrigger: function (template, trigger) {
  1085. const content = `
  1086. <div style="margin:0 2em 0 2em">
  1087. <div style="float:left;width:120px">
  1088. <span class="zdy-item" style="width:90px" v-for="f in filters">
  1089. <p style="margin:0"><wht>{{ f.description() }}</wht></p>
  1090. <input v-if="f.type=='input'" style="width:80%" v-model="conditions[f.name]">
  1091. <select v-if="f.type=='select'" v-model="conditions[f.name]">
  1092. <option v-for="opt in f.options" :value="opt">{{ opt }}</option>
  1093. </select>
  1094. </span>
  1095. </div>
  1096. <div style="float:right;width:calc(100% - 125px)">
  1097. <textarea class = "settingbox hide" style = "height:10rem;display:inline-block;font-size:0.8em;width:100%" v-model="source"></textarea>
  1098. <span class="raid-item shareTrigger" v-if="canShared" v-on:click="share()">分享此触发器</span>
  1099. </div>
  1100. </div>
  1101. `;
  1102. const title = `<input style='width:110px' type="text" placeholder="输入触发器名称" v-model="name">`;
  1103. let rightText = "<span v-on:click='save'><wht>保存</wht></span>";
  1104. if (trigger) {
  1105. rightText = "<span v-on:click='remove'>删除</span>"
  1106. }
  1107. let leftText = "<span v-on:click='back'>< 返回</span>";
  1108. if (trigger) {
  1109. leftText = "<span v-on:click='saveback'>< 保存&返回</span>"
  1110. }
  1111. UI._appendHtml(title, content, rightText, leftText);
  1112. let conditions = {};
  1113. if (trigger != null) {
  1114. conditions = trigger.conditions;
  1115. } else {
  1116. for (const f of template.filters) {
  1117. conditions[f.name] = f.defaultValue;
  1118. }
  1119. }
  1120. let source = template.introdution;
  1121. if (trigger != null) source = trigger.source;
  1122. new Vue({
  1123. el: '#app',
  1124. data: {
  1125. filters: template.filters,
  1126. name: trigger ? trigger.name : "",
  1127. conditions: conditions,
  1128. source: source,
  1129. canShared: trigger != null
  1130. },
  1131. methods: {
  1132. save: function () {
  1133. const result = TriggerCenter.create(this.name, template.event, this.conditions, this.source);
  1134. if (result == true) {
  1135. UI.triggerHome();
  1136. } else {
  1137. alert(result);
  1138. }
  1139. },
  1140. remove: function () {
  1141. const verify = confirm("确认删除此触发器吗?");
  1142. if (verify) {
  1143. TriggerCenter.remove(trigger.name);
  1144. UI.triggerHome();
  1145. }
  1146. },
  1147. back: function () {
  1148. UI.selectTriggerTemplate();
  1149. },
  1150. saveback: function () {
  1151. const result = TriggerCenter.modify(trigger.name, this.name, this.conditions, this.source);
  1152. if (result == true) {
  1153. UI.triggerHome();
  1154. } else {
  1155. alert(result);
  1156. }
  1157. },
  1158.  
  1159. share: function () {
  1160. ToRaid.shareTrigger(TriggerCenter._getData(trigger.name));
  1161. }
  1162. }
  1163. })
  1164. },
  1165.  
  1166. _appendHtml: function (title, content, rightText, leftText) {
  1167. var realLeftText = leftText == null ? "" : leftText;
  1168. var realRightText = rightText == null ? "" : rightText;
  1169. var html = `
  1170. <div class = "item-commands" style="text-align:center" id="app">
  1171. <div style="margin-top:0.5em">
  1172. <div style="width:8em;float:left;text-align:left;padding:0px 0px 0px 2em;height:1.23em" id="wsmud_raid_left">${realLeftText}</div>
  1173. <div style="width:calc(100% - 16em);float:left;height:1.23em">${title}</div>
  1174. <div style="width:8em;float:left;text-align:right;padding:0px 2em 0px 0px;height:1.23em" id="wsmud_raid_right">${realRightText}</div>
  1175. </div>
  1176. <br><br>
  1177. ${content}
  1178. </div>`;
  1179. Message.clean();
  1180. Message.append(html);
  1181. },
  1182. };
  1183.  
  1184. /***********************************************************************************\
  1185. Trigger Config
  1186. \***********************************************************************************/
  1187.  
  1188. const TriggerConfig = {
  1189. get: function () {
  1190. let all = {};
  1191. let keys = GM_listValues();
  1192. keys.forEach(key => {
  1193. if (key != "roles") {
  1194. all[key] = GM_getValue(key);
  1195. }
  1196. });
  1197. return all;
  1198. },
  1199. set: function (config) {
  1200. for (const key in config) {
  1201. GM_setValue(key, config[key]);
  1202. }
  1203. TriggerCenter.reload();
  1204. }
  1205. };
  1206.  
  1207. /***********************************************************************************\
  1208. Ready
  1209. \***********************************************************************************/
  1210.  
  1211. let Running = false;
  1212.  
  1213. $(document).ready(function () {
  1214. __init__();
  1215. if (WG == undefined || WG == null || ToRaid == undefined || ToRaid == null) {
  1216. setTimeout(__init__, 300);
  1217. }
  1218. });
  1219.  
  1220. function __init__() {
  1221. WG = unsafeWindow.WG;
  1222.  
  1223. messageAppend = unsafeWindow.messageAppend;
  1224. messageClear = unsafeWindow.messageClear;
  1225. ToRaid = unsafeWindow.ToRaid;
  1226.  
  1227. if (WG == undefined || WG == null || ToRaid == undefined || ToRaid == null) {
  1228. setTimeout(() => { __init__() }, 300);
  1229. return;
  1230. }
  1231. Role = unsafeWindow.Role;
  1232.  
  1233. unsafeWindow.TriggerUI = UI;
  1234. unsafeWindow.TriggerConfig = TriggerConfig;
  1235. unsafeWindow.TriggerCenter = TriggerCenter;
  1236.  
  1237. WG.add_hook("login", function (data) {
  1238. if (Running) return;
  1239. Running = true;
  1240.  
  1241. TriggerCenter.run();
  1242. MonitorCenter.run();
  1243. });
  1244. }
  1245. })();