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.
 
 
 

644 line
13 KiB

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