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.
 
 
 

629 lines
13 KiB

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