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.
 
 
 

547 lines
11 KiB

  1. const configMaterials = require('../../items/config/materials');
  2. const connections = require('../../security/connections');
  3. const events = require('../../misc/events');
  4. //Commands
  5. const rezone = require('../social/rezone');
  6. const canChat = require('../social/canChat');
  7. const startEvent = require('../social/startEvent');
  8. const stopEvent = require('../social/stopEvent');
  9. const teleport = require('../social/teleport');
  10. let commandRoles = {
  11. //Regular players
  12. join: 0,
  13. leave: 0,
  14. block: 0,
  15. unblock: 0,
  16. help: 0,
  17. //Super Mods
  18. broadcast: 8,
  19. saveAll: 8,
  20. //Admin
  21. getGold: 10,
  22. setLevel: 10,
  23. godMode: 10,
  24. clearInventory: 10,
  25. completeQuests: 10,
  26. getReputation: 10,
  27. loseReputation: 10,
  28. setStat: 10,
  29. die: 10,
  30. getXp: 10,
  31. setPassword: 10,
  32. giveSkin: 10,
  33. getMaterials: 10,
  34. rezone: 10,
  35. startEvent: 10,
  36. stopEvent: 10,
  37. teleport: 10,
  38. roll: 10,
  39. unEq: 10
  40. };
  41. //Commands that should be run on the main thread (not the zone thread)
  42. const localCommands = [
  43. 'join',
  44. 'leave',
  45. 'setPassword',
  46. 'roll',
  47. 'broadcast',
  48. 'saveAll',
  49. 'help',
  50. 'startEvent',
  51. 'stopEvent'
  52. ];
  53. //Actions that should appear when a player is right clicked
  54. const contextActions = [];
  55. const commandActions = {};
  56. module.exports = {
  57. actions: [],
  58. customChannels: [],
  59. init: function (blueprint) {
  60. if (this.customChannels) {
  61. this.customChannels = this.customChannels
  62. .filter((c, i) => (this.customChannels.indexOf(c) === i));
  63. }
  64. this.calculateActions();
  65. },
  66. calculateActions: function () {
  67. const chatCommandConfig = {
  68. localCommands,
  69. contextActions: extend([], contextActions),
  70. commandRoles,
  71. commandActions
  72. };
  73. events.emit('onBeforeGetChatCommands', chatCommandConfig);
  74. events.emit('onBeforeGetCommandRoles', commandRoles, commandActions);
  75. Object.entries(commandActions).forEach(a => {
  76. const [ actionName, actionHandler ] = a;
  77. this[actionName] = actionHandler.bind(this);
  78. });
  79. this.actions = chatCommandConfig.contextActions
  80. .filter(c => this.obj.auth.getAccountLevel() >= commandRoles[c.command]);
  81. },
  82. onBeforeChat: function (msg) {
  83. let messageText = msg.message;
  84. if (messageText[0] !== '/')
  85. return;
  86. msg.ignore = true;
  87. messageText = messageText.substr(1).split(' ');
  88. let actionName = messageText.splice(0, 1)[0].toLowerCase();
  89. actionName = Object.keys(commandRoles).find(a => (a.toLowerCase() === actionName));
  90. if (!actionName) {
  91. this.obj.socket.emit('events', {
  92. onGetMessages: [{
  93. messages: [{
  94. class: 'color-redA',
  95. message: 'Invalid command.',
  96. type: 'info'
  97. }]
  98. }]
  99. });
  100. return;
  101. } else if (this.obj.auth.getAccountLevel() < commandRoles[actionName]) {
  102. this.obj.socket.emit('events', {
  103. onGetMessages: [{
  104. messages: [{
  105. class: 'color-redA',
  106. message: 'You do not have the required permissions.',
  107. type: 'info'
  108. }]
  109. }]
  110. });
  111. return;
  112. }
  113. let config = {};
  114. const originalConfig = messageText.join(' ');
  115. if ((messageText.length === 1) && (messageText[0].indexOf('=') === -1))
  116. config = messageText[0];
  117. else {
  118. messageText.forEach(function (m) {
  119. m = m.split('=');
  120. config[m[0]] = m[1];
  121. });
  122. }
  123. if (localCommands.includes(actionName))
  124. this[actionName](config, originalConfig);
  125. else {
  126. atlas.performAction(this.obj, {
  127. cpn: 'social',
  128. method: actionName,
  129. data: config
  130. });
  131. }
  132. },
  133. //actions
  134. join: async function (value) {
  135. if (typeof (value) !== 'string')
  136. return;
  137. value = value
  138. .trim()
  139. .split(' ').join('');
  140. let obj = this.obj;
  141. if (value.length === 0)
  142. return;
  143. else if (!value.match(/^[0-9a-zA-Z]+$/)) {
  144. obj.socket.emit('events', {
  145. onGetMessages: [{
  146. messages: [{
  147. class: 'color-redA',
  148. message: 'Channel names may only contain letters and numbers.',
  149. type: 'info'
  150. }]
  151. }]
  152. });
  153. return;
  154. } else if (value.length > 15) {
  155. obj.socket.emit('events', {
  156. onGetMessages: [{
  157. messages: [{
  158. class: 'color-redA',
  159. message: 'Channel names can not be longer than 15 characters.',
  160. type: 'info'
  161. }]
  162. }]
  163. });
  164. return;
  165. }
  166. let channels = obj.auth.customChannels;
  167. if (!channels.some(c => (c === value)))
  168. channels.push(value);
  169. else
  170. return;
  171. channels.push(value);
  172. let charname = obj.auth.charname;
  173. await io.setAsync({
  174. key: charname,
  175. table: 'customChannels',
  176. value: channels,
  177. serialize: true
  178. });
  179. obj.socket.emit('events', {
  180. onGetMessages: [{
  181. messages: [{
  182. class: 'color-yellowB',
  183. message: 'joined channel: ' + value,
  184. type: 'info'
  185. }]
  186. }]
  187. });
  188. obj.socket.emit('event', {
  189. event: 'onJoinChannel',
  190. data: value
  191. });
  192. },
  193. leave: async function (value) {
  194. if (typeof (value) !== 'string')
  195. return;
  196. let obj = this.obj;
  197. let channels = obj.auth.customChannels;
  198. if (!channels.some(c => (c === value))) {
  199. obj.socket.emit('events', {
  200. onGetMessages: [{
  201. messages: [{
  202. class: 'color-redA',
  203. message: 'you are not currently in that channel',
  204. type: 'info'
  205. }]
  206. }]
  207. });
  208. return;
  209. }
  210. channels.spliceWhere(c => (c === value));
  211. let charname = obj.auth.charname;
  212. await io.setAsync({
  213. key: charname,
  214. table: 'customChannels',
  215. value: channels,
  216. serialize: true
  217. });
  218. obj.socket.emit('event', {
  219. event: 'onLeaveChannel',
  220. data: value
  221. });
  222. this.obj.socket.emit('events', {
  223. onGetMessages: [{
  224. messages: [{
  225. class: 'color-yellowB',
  226. message: 'left channel: ' + value,
  227. type: 'info'
  228. }]
  229. }]
  230. });
  231. },
  232. block: function (target) {
  233. const { obj, blockedPlayers } = this;
  234. const { name, social, syncer } = obj;
  235. if (blockedPlayers.includes(target)) {
  236. social.notifySelf({ message: 'That player has already been blocked' });
  237. return;
  238. } else if (target === name) {
  239. social.notifySelf({ message: 'You cannot block yourself' });
  240. return;
  241. }
  242. blockedPlayers.push(target);
  243. syncer.set(true, 'social', 'blockedPlayers', blockedPlayers);
  244. social.notifySelf({
  245. message: `Successfully blocked ${target}`,
  246. className: 'color-yellowB'
  247. });
  248. },
  249. unblock: function (target) {
  250. const { obj, blockedPlayers } = this;
  251. const { social, syncer } = obj;
  252. if (!blockedPlayers.includes(target)) {
  253. social.notifySelf({ message: 'That player is not blocked' });
  254. return;
  255. }
  256. blockedPlayers.spliceWhere(f => f === target);
  257. syncer.set(true, 'social', 'blockedPlayers', blockedPlayers);
  258. social.notifySelf({
  259. message: `Successfully unblocked ${target}`,
  260. className: 'color-yellowB'
  261. });
  262. },
  263. help: function () {
  264. const msg = [
  265. 'You can use the following commands:',
  266. ...Object.keys(commandRoles)
  267. .filter(c => this.obj.auth.getAccountLevel() >= commandRoles[c])
  268. .map(c => `/${c}`)
  269. ].join('<br />');
  270. this.sendMessage(msg, 'color-yellowB');
  271. },
  272. isInChannel: function (character, channel) {
  273. return character.auth.customChannels.some(c => (c === channel));
  274. },
  275. roll: function () {
  276. if (!canChat(this.obj)) {
  277. 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');
  278. return;
  279. }
  280. const roll = 1 + ~~(Math.random() * 100);
  281. cons.emit('event', {
  282. event: 'onGetMessages',
  283. data: {
  284. messages: [{
  285. class: 'color-grayB',
  286. message: `${this.obj.name} rolled ${roll}`,
  287. type: 'chat',
  288. source: this.obj.name
  289. }]
  290. }
  291. });
  292. },
  293. unEq: function () {
  294. let eq = this.obj.equipment;
  295. Object.keys(eq.eq).forEach(function (slot) {
  296. eq.unequip({ itemId: eq.eq[slot] });
  297. });
  298. },
  299. clearInventory: function () {
  300. let inventory = this.obj.inventory;
  301. inventory.items
  302. .filter(i => !i.eq)
  303. .map(i => i.id)
  304. .forEach(i => inventory.destroyItem({ itemId: i }, null, true));
  305. },
  306. getGold: function (amount) {
  307. let newGold = this.obj.trade.gold + ~~amount;
  308. newGold = Math.max(-1000000000, Math.min(1000000000, newGold));
  309. this.obj.trade.gold = newGold;
  310. this.obj.syncer.set(true, 'trade', 'gold', newGold);
  311. },
  312. setLevel: function (level) {
  313. let obj = this.obj;
  314. let syncer = obj.syncer;
  315. level = Math.max(1, ~~level);
  316. let stats = obj.stats;
  317. let values = stats.values;
  318. let oldLevel = values.level;
  319. values.level = level;
  320. obj.fireEvent('onLevelUp', values.level);
  321. let delta = level - oldLevel;
  322. values.hpMax += (40 * delta);
  323. syncer.setObject(true, 'stats', 'values', 'level', level);
  324. syncer.setObject(true, 'stats', 'values', 'hpMax', values.hpMax);
  325. process.send({
  326. method: 'object',
  327. serverId: obj.serverId,
  328. obj: {
  329. level: level
  330. }
  331. });
  332. stats.calcXpMax();
  333. },
  334. godMode: function () {
  335. let obj = this.obj;
  336. let statValues = obj.stats.values;
  337. let newValues = {
  338. int: 10000000,
  339. str: 10000000,
  340. dex: 10000000,
  341. hpMax: 10000000,
  342. hp: 10000000,
  343. manaMax: 10000000,
  344. mana: 10000000,
  345. sprintChance: 100,
  346. vit: 10000000
  347. };
  348. let syncer = obj.syncer;
  349. for (let s in newValues) {
  350. let newValue = newValues[s];
  351. statValues[s] = newValue;
  352. syncer.setObject(true, 'stats', 'values', s, newValue);
  353. }
  354. obj.spellbook.calcDps();
  355. },
  356. completeQuests: function () {
  357. let obj = this.obj;
  358. let quests = obj.quests;
  359. quests.quests.forEach(function (q) {
  360. q.isReady = true;
  361. q.complete();
  362. }, this);
  363. quests.quests = [];
  364. obj.instance.questBuilder.obtain(obj);
  365. },
  366. getReputation: function (faction) {
  367. if (typeof (faction) !== 'string')
  368. return;
  369. this.obj.reputation.getReputation(faction, 50000);
  370. },
  371. loseReputation: function (faction) {
  372. if (typeof (faction) !== 'string')
  373. return;
  374. this.obj.reputation.getReputation(faction, -50000);
  375. },
  376. setStat: function (config) {
  377. this.obj.stats.values[config.stat] = ~~config.value;
  378. },
  379. getXp: function (amount) {
  380. this.obj.stats.getXp(amount, this.obj, this.obj);
  381. },
  382. die: function () {
  383. this.obj.stats.takeDamage({
  384. damage: { amount: 20000000 },
  385. source: this.obj,
  386. target: this.obj
  387. });
  388. },
  389. setPassword: async function (config) {
  390. let keys = Object.keys(config);
  391. let username = keys[0]
  392. .split('_')
  393. .join(' ');
  394. let hashedPassword = keys[1];
  395. await io.setAsync({
  396. key: username,
  397. table: 'login',
  398. value: hashedPassword
  399. });
  400. },
  401. getMaterials: function (config) {
  402. if (typeof(config) === 'object')
  403. config = 100;
  404. let inventory = this.obj.inventory;
  405. Object.entries(configMaterials).forEach(([material, blueprint]) => {
  406. inventory.getItem({
  407. name: material,
  408. quantity: config,
  409. material: true,
  410. ...blueprint
  411. });
  412. });
  413. },
  414. broadcast: function (config, msg) {
  415. if (typeof(msg) === 'object')
  416. msg = Object.keys(msg).join(' ');
  417. cons.emit('event', {
  418. event: 'onGetMessages',
  419. data: {
  420. messages: [{
  421. class: 'color-blueA',
  422. message: msg,
  423. type: 'chat'
  424. }]
  425. }
  426. });
  427. },
  428. saveAll: async function () {
  429. const { obj: { social } } = this;
  430. social.sendMessage('Initiating Save', 'color-blueA');
  431. await connections.forceSaveAll();
  432. social.sendMessage('Save Complete', 'color-blueA');
  433. },
  434. rezone: function (msg) {
  435. rezone(this, msg);
  436. },
  437. startEvent: function (msg) {
  438. startEvent(this, msg);
  439. },
  440. stopEvent: function (msg) {
  441. stopEvent(this, msg);
  442. },
  443. teleport: function (msg) {
  444. teleport(this, msg);
  445. }
  446. };