You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

745 lines
14 KiB

  1. let roles = require('../../config/roles');
  2. let generator = require('../../items/generator');
  3. let configSlots = require('../../items/config/slots');
  4. let factions = require('../../config/factions');
  5. let connections = require('../../security/connections');
  6. const events = require('../../misc/events');
  7. const ban = require('../social/ban');
  8. const rezone = require('../social/rezone');
  9. const canChat = require('../social/canChat');
  10. const startEvent = require('../social/startEvent');
  11. const stopEvent = require('../social/stopEvent');
  12. const teleport = require('../social/teleport');
  13. let commandRoles = {
  14. //Regular players
  15. join: 0,
  16. leave: 0,
  17. unEq: 0,
  18. roll: 0,
  19. block: 0,
  20. unblock: 0,
  21. help: 0,
  22. //Mods
  23. ban: 5,
  24. mute: 5,
  25. unmute: 5,
  26. //Super Mods
  27. broadcast: 8,
  28. saveAll: 8,
  29. //Admin
  30. getItem: 10,
  31. getGold: 10,
  32. setLevel: 10,
  33. godMode: 10,
  34. clearInventory: 10,
  35. completeQuests: 10,
  36. getReputation: 10,
  37. loseReputation: 10,
  38. setStat: 10,
  39. die: 10,
  40. getXp: 10,
  41. setPassword: 10,
  42. giveSkin: 10,
  43. rezone: 10,
  44. startEvent: 10,
  45. stopEvent: 10,
  46. teleport: 10
  47. };
  48. //Commands that should be run on the main thread (not the zone thread)
  49. const localCommands = [
  50. 'ban',
  51. 'join',
  52. 'leave',
  53. 'mute',
  54. 'unmute',
  55. 'setPassword',
  56. 'roll',
  57. 'giveSkin',
  58. 'broadcast',
  59. 'saveAll',
  60. 'ban',
  61. 'help',
  62. 'startEvent',
  63. 'stopEvent'
  64. ];
  65. //Actions that should appear when a player is right clicked
  66. const contextActions = [
  67. {
  68. command: 'mute',
  69. text: 'mute'
  70. },
  71. {
  72. command: 'unmute',
  73. text: 'unmute'
  74. },
  75. {
  76. command: 'ban',
  77. text: 'ban'
  78. }
  79. ];
  80. const commandActions = {};
  81. module.exports = {
  82. customChannels: [],
  83. roleLevel: null,
  84. init: function (blueprint) {
  85. if (this.customChannels) {
  86. this.customChannels = this.customChannels
  87. .filter((c, i) => (this.customChannels.indexOf(c) === i));
  88. }
  89. events.emit('onBeforeGetCommandRoles', commandRoles, commandActions);
  90. Object.entries(commandActions).forEach(a => {
  91. const [ actionName, actionHandler ] = a;
  92. this[actionName] = actionHandler.bind(this);
  93. });
  94. this.roleLevel = roles.getRoleLevel(this.obj);
  95. this.calculateActions();
  96. },
  97. calculateActions: function () {
  98. this.actions = contextActions
  99. .filter(c => this.roleLevel >= commandRoles[c.command]);
  100. },
  101. onBeforeChat: function (msg) {
  102. let messageText = msg.message;
  103. if (messageText[0] !== '/')
  104. return;
  105. msg.ignore = true;
  106. messageText = messageText.substr(1).split(' ');
  107. let actionName = messageText.splice(0, 1)[0].toLowerCase();
  108. actionName = Object.keys(commandRoles).find(a => (a.toLowerCase() === actionName));
  109. if (!actionName) {
  110. this.obj.socket.emit('events', {
  111. onGetMessages: [{
  112. messages: [{
  113. class: 'color-redA',
  114. message: 'Invalid command.',
  115. type: 'info'
  116. }]
  117. }]
  118. });
  119. return;
  120. } else if (this.roleLevel < commandRoles[actionName]) {
  121. this.obj.socket.emit('events', {
  122. onGetMessages: [{
  123. messages: [{
  124. class: 'color-redA',
  125. message: 'You do not have the required permissions.',
  126. type: 'info'
  127. }]
  128. }]
  129. });
  130. return;
  131. }
  132. let config = {};
  133. const originalConfig = messageText.join(' ');
  134. if ((messageText.length === 1) && (messageText[0].indexOf('=') === -1))
  135. config = messageText[0];
  136. else {
  137. messageText.forEach(function (m) {
  138. m = m.split('=');
  139. config[m[0]] = m[1];
  140. });
  141. }
  142. if (localCommands.includes(actionName))
  143. this[actionName](config, originalConfig);
  144. else {
  145. atlas.performAction(this.obj, {
  146. cpn: 'social',
  147. method: actionName,
  148. data: config
  149. });
  150. }
  151. },
  152. //actions
  153. join: async function (value) {
  154. if (typeof (value) !== 'string')
  155. return;
  156. value = value
  157. .trim()
  158. .split(' ').join('');
  159. let obj = this.obj;
  160. if (value.length === 0)
  161. return;
  162. else if (!value.match(/^[0-9a-zA-Z]+$/)) {
  163. obj.socket.emit('events', {
  164. onGetMessages: [{
  165. messages: [{
  166. class: 'color-redA',
  167. message: 'Channel names may only contain letters and numbers.',
  168. type: 'info'
  169. }]
  170. }]
  171. });
  172. return;
  173. } else if (value.length > 15) {
  174. obj.socket.emit('events', {
  175. onGetMessages: [{
  176. messages: [{
  177. class: 'color-redA',
  178. message: 'Channel names can not be longer than 15 characters.',
  179. type: 'info'
  180. }]
  181. }]
  182. });
  183. return;
  184. }
  185. let channels = obj.auth.customChannels;
  186. if (!channels.some(c => (c === value)))
  187. channels.push(value);
  188. else
  189. return;
  190. channels.push(value);
  191. let charname = obj.auth.charname;
  192. await io.setAsync({
  193. key: charname,
  194. table: 'customChannels',
  195. value: channels,
  196. serialize: true
  197. });
  198. obj.socket.emit('events', {
  199. onGetMessages: [{
  200. messages: [{
  201. class: 'color-yellowB',
  202. message: 'joined channel: ' + value,
  203. type: 'info'
  204. }]
  205. }]
  206. });
  207. obj.socket.emit('event', {
  208. event: 'onJoinChannel',
  209. data: value
  210. });
  211. },
  212. leave: async function (value) {
  213. if (typeof (value) !== 'string')
  214. return;
  215. let obj = this.obj;
  216. let channels = obj.auth.customChannels;
  217. if (!channels.some(c => (c === value))) {
  218. obj.socket.emit('events', {
  219. onGetMessages: [{
  220. messages: [{
  221. class: 'color-redA',
  222. message: 'you are not currently in that channel',
  223. type: 'info'
  224. }]
  225. }]
  226. });
  227. return;
  228. }
  229. channels.spliceWhere(c => (c === value));
  230. let charname = obj.auth.charname;
  231. await io.setAsync({
  232. key: charname,
  233. table: 'customChannels',
  234. value: channels,
  235. serialize: true
  236. });
  237. obj.socket.emit('event', {
  238. event: 'onLeaveChannel',
  239. data: value
  240. });
  241. this.obj.socket.emit('events', {
  242. onGetMessages: [{
  243. messages: [{
  244. class: 'color-yellowB',
  245. message: 'left channel: ' + value,
  246. type: 'info'
  247. }]
  248. }]
  249. });
  250. },
  251. block: function (target) {
  252. const { obj, blockedPlayers } = this;
  253. const { name, social, syncer } = obj;
  254. if (blockedPlayers.includes(target)) {
  255. social.notifySelf({ message: 'That player has already been blocked' });
  256. return;
  257. } else if (target === name) {
  258. social.notifySelf({ message: 'You cannot block yourself' });
  259. return;
  260. }
  261. blockedPlayers.push(target);
  262. syncer.set(true, 'social', 'blockedPlayers', blockedPlayers);
  263. social.notifySelf({
  264. message: `Successfully blocked ${target}`,
  265. className: 'color-yellowB'
  266. });
  267. },
  268. unblock: function (target) {
  269. const { obj, blockedPlayers } = this;
  270. const { social, syncer } = obj;
  271. if (!blockedPlayers.includes(target)) {
  272. social.notifySelf({ message: 'That player is not blocked' });
  273. return;
  274. }
  275. blockedPlayers.spliceWhere(f => f === target);
  276. syncer.set(true, 'social', 'blockedPlayers', blockedPlayers);
  277. social.notifySelf({
  278. message: `Successfully unblocked ${target}`,
  279. className: 'color-yellowB'
  280. });
  281. },
  282. help: function () {
  283. const msg = [
  284. 'You can use the following commands:',
  285. ...Object.keys(commandRoles)
  286. .filter(c => this.roleLevel >= commandRoles[c])
  287. .map(c => `/${c}`)
  288. ].join('<br />');
  289. this.sendMessage(msg, 'color-yellowB');
  290. },
  291. isInChannel: function (character, channel) {
  292. return character.auth.customChannels.some(c => (c === channel));
  293. },
  294. roll: function () {
  295. if (!canChat(this.obj)) {
  296. this.sendMessage('Your character needs to be played for at least 3 minutes or be at least level 3 to be able to send messages in chat.', 'color-redA');
  297. return;
  298. }
  299. const roll = 1 + ~~(Math.random() * 100);
  300. cons.emit('event', {
  301. event: 'onGetMessages',
  302. data: {
  303. messages: [{
  304. class: 'color-grayB',
  305. message: `${this.obj.name} rolled ${roll}`,
  306. type: 'chat',
  307. source: this.obj.name
  308. }]
  309. }
  310. });
  311. },
  312. unEq: function () {
  313. let eq = this.obj.equipment;
  314. Object.keys(eq.eq).forEach(function (slot) {
  315. eq.unequip(eq.eq[slot]);
  316. });
  317. },
  318. mute: async function (target, reason = null) {
  319. if (typeof (target) === 'object') {
  320. let keys = Object.keys(target);
  321. target = keys[0];
  322. reason = keys[1] || null;
  323. }
  324. if (target === this.obj.name)
  325. return;
  326. let o = connections.players.find(f => (f.name === target));
  327. if (!o)
  328. return;
  329. let role = roles.getRoleLevel(o);
  330. if (role >= this.roleLevel)
  331. return;
  332. let social = o.social;
  333. if (social.muted) {
  334. this.sendMessage('That player has already been muted', 'color-redA');
  335. return;
  336. }
  337. let reasonMsg = '';
  338. if (reason)
  339. reasonMsg = ' (' + reason + ')';
  340. social.muted = true;
  341. this.sendMessage('Successfully muted ' + target, 'color-yellowB');
  342. this.sendMessage('You have been muted' + reasonMsg, 'color-yellowB', o);
  343. atlas.updateObject(o, {
  344. components: [{
  345. type: 'social',
  346. muted: true
  347. }]
  348. });
  349. await io.setAsync({
  350. key: new Date(),
  351. table: 'modLog',
  352. value: {
  353. source: this.obj.name,
  354. command: 'mute',
  355. target: target,
  356. reason: reason
  357. },
  358. serialize: true
  359. });
  360. },
  361. unmute: async function (target, reason = null) {
  362. if (typeof (target) === 'object') {
  363. let keys = Object.keys(target);
  364. target = keys[0];
  365. reason = keys[1] || null;
  366. }
  367. if (target === this.obj.name)
  368. return;
  369. let o = connections.players.find(f => (f.name === target));
  370. if (!o)
  371. return;
  372. let role = roles.getRoleLevel(o);
  373. if (role >= this.roleLevel)
  374. return;
  375. let social = o.social;
  376. if (!social.muted) {
  377. this.sendMessage('That player is not muted', 'color-redA');
  378. return;
  379. }
  380. let reasonMsg = '';
  381. if (reason)
  382. reasonMsg = ' (' + reason + ')';
  383. delete social.muted;
  384. this.sendMessage('Successfully unmuted ' + target, 'color-yellowB');
  385. this.sendMessage('You have been unmuted' + reasonMsg, 'color-yellowB', o);
  386. atlas.updateObject(o, {
  387. components: [{
  388. type: 'social',
  389. muted: null
  390. }]
  391. });
  392. await io.setAsync({
  393. key: new Date(),
  394. table: 'modLog',
  395. value: {
  396. source: this.obj.name,
  397. command: 'unmute',
  398. target: target,
  399. reason: reason
  400. },
  401. serialize: true
  402. });
  403. },
  404. clearInventory: function () {
  405. let inventory = this.obj.inventory;
  406. inventory.items
  407. .filter(i => !i.eq)
  408. .map(i => i.id)
  409. .forEach(i => inventory.destroyItem(i, null, true));
  410. },
  411. getItem: function (config) {
  412. if (typeof config !== 'object')
  413. return;
  414. if (config.slot === 'set') {
  415. configSlots.slots.forEach(function (s) {
  416. if (s === 'tool')
  417. return;
  418. let newConfig = extend({}, config, {
  419. slot: s
  420. });
  421. this.getItem(newConfig);
  422. }, this);
  423. return;
  424. }
  425. if (config.stats)
  426. config.stats = config.stats.split(',');
  427. if (config.name)
  428. config.name = config.name.split('_').join(' ');
  429. if (config.spellName)
  430. config.spellName = config.spellName.split('_').join(' ');
  431. if (config.type)
  432. config.type = config.type.split('_').join(' ');
  433. if (config.sprite)
  434. config.sprite = config.sprite.split('_');
  435. let spritesheet = config.spritesheet;
  436. delete config.spritesheet;
  437. let factionList = (config.factions || '').split(',');
  438. delete config.factions;
  439. let safe = config.safe;
  440. delete config.safe;
  441. let eq = config.eq;
  442. delete config.eq;
  443. let item = generator.generate(config);
  444. if (safe) {
  445. item.noDrop = true;
  446. item.noDestroy = true;
  447. item.noSalvage = true;
  448. }
  449. factionList.forEach(function (f) {
  450. if (f === '')
  451. return;
  452. let faction = factions.getFaction(f);
  453. faction.uniqueStat.generate(item);
  454. item.factions = [];
  455. item.factions.push({
  456. id: f,
  457. tier: 3
  458. });
  459. });
  460. if (spritesheet)
  461. item.spritesheet = spritesheet;
  462. let newItem = this.obj.inventory.getItem(item);
  463. if (eq)
  464. this.obj.equipment.equip(newItem.id);
  465. },
  466. getGold: function (amount) {
  467. let newGold = this.obj.trade.gold + ~~amount;
  468. newGold = Math.max(-1000000000, Math.min(1000000000, newGold));
  469. this.obj.trade.gold = newGold;
  470. this.obj.syncer.set(true, 'trade', 'gold', newGold);
  471. },
  472. setLevel: function (level) {
  473. let obj = this.obj;
  474. let syncer = obj.syncer;
  475. level = Math.max(1, ~~level);
  476. let stats = obj.stats;
  477. let values = stats.values;
  478. let oldLevel = values.level;
  479. values.level = level;
  480. let delta = level - oldLevel;
  481. values.hpMax += (40 * delta);
  482. syncer.setObject(true, 'stats', 'values', 'level', level);
  483. syncer.setObject(true, 'stats', 'values', 'hpMax', values.hpMax);
  484. process.send({
  485. method: 'object',
  486. serverId: obj.serverId,
  487. obj: {
  488. level: level
  489. }
  490. });
  491. stats.calcXpMax();
  492. },
  493. godMode: function () {
  494. let obj = this.obj;
  495. let statValues = obj.stats.values;
  496. let newValues = {
  497. int: 10000000,
  498. str: 10000000,
  499. dex: 10000000,
  500. hpMax: 10000000,
  501. hp: 10000000,
  502. manaMax: 10000000,
  503. mana: 10000000,
  504. sprintChance: 100,
  505. vit: 10000000
  506. };
  507. let syncer = obj.syncer;
  508. for (let s in newValues) {
  509. let newValue = newValues[s];
  510. statValues[s] = newValue;
  511. syncer.setObject(true, 'stats', 'values', s, newValue);
  512. }
  513. obj.spellbook.calcDps();
  514. },
  515. completeQuests: function () {
  516. let obj = this.obj;
  517. let quests = obj.quests;
  518. quests.quests.forEach(function (q) {
  519. q.isReady = true;
  520. q.complete();
  521. }, this);
  522. quests.quests = [];
  523. obj.instance.questBuilder.obtain(obj);
  524. },
  525. getReputation: function (faction) {
  526. if (typeof (faction) !== 'string')
  527. return;
  528. this.obj.reputation.getReputation(faction, 50000);
  529. },
  530. loseReputation: function (faction) {
  531. if (typeof (faction) !== 'string')
  532. return;
  533. this.obj.reputation.getReputation(faction, -50000);
  534. },
  535. setStat: function (config) {
  536. this.obj.stats.values[config.stat] = ~~config.value;
  537. },
  538. getXp: function (amount) {
  539. this.obj.stats.getXp(amount, this.obj, this.obj);
  540. },
  541. die: function () {
  542. this.obj.stats.takeDamage({
  543. amount: 99999
  544. }, 1, this.obj);
  545. },
  546. setPassword: async function (config) {
  547. let keys = Object.keys(config);
  548. let username = keys[0]
  549. .split('_')
  550. .join(' ');
  551. let hashedPassword = keys[1];
  552. await io.setAsync({
  553. key: username,
  554. table: 'login',
  555. value: hashedPassword
  556. });
  557. },
  558. giveSkin: async function (config) {
  559. let keys = Object.keys(config);
  560. let username = keys[0];
  561. let skinId = keys[1];
  562. let skins = await io.getAsync({
  563. key: username,
  564. table: 'skins',
  565. isArray: true
  566. });
  567. skins.push(skinId);
  568. await io.setAsync({
  569. key: username,
  570. table: 'skins',
  571. value: skins,
  572. serialize: true
  573. });
  574. },
  575. broadcast: function (config, msg) {
  576. if (typeof(msg) === 'object')
  577. msg = Object.keys(msg).join(' ');
  578. cons.emit('event', {
  579. event: 'onGetMessages',
  580. data: {
  581. messages: [{
  582. class: 'color-blueA',
  583. message: msg,
  584. type: 'chat'
  585. }]
  586. }
  587. });
  588. },
  589. saveAll: function () {
  590. connections.forceSaveAll();
  591. },
  592. ban: function (msg) {
  593. ban(this, msg);
  594. },
  595. rezone: function (msg) {
  596. rezone(this, msg);
  597. },
  598. startEvent: function (msg) {
  599. startEvent(this, msg);
  600. },
  601. stopEvent: function (msg) {
  602. stopEvent(this, msg);
  603. },
  604. teleport: function (msg) {
  605. teleport(this, msg);
  606. }
  607. };