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.
 
 
 

318 lines
6.2 KiB

  1. const effectTemplate = require('../config/effects/effectTemplate');
  2. module.exports = {
  3. type: 'effects',
  4. effects: [],
  5. nextId: 0,
  6. ccResistances: {
  7. stunned: 0,
  8. slowed: 0
  9. },
  10. init: function (blueprint) {
  11. let effects = blueprint.effects || [];
  12. let eLen = effects.length;
  13. for (let i = 0; i < eLen; i++) {
  14. let e = effects[i];
  15. if (!e.type)
  16. continue;
  17. this.addEffect(e);
  18. }
  19. delete blueprint.effects;
  20. },
  21. transfer: function () {
  22. let transferEffects = this.effects;
  23. this.effects = [];
  24. this.init({
  25. effects: transferEffects
  26. });
  27. },
  28. save: function () {
  29. let e = {
  30. type: 'effects',
  31. effects: this.effects
  32. .map(f => f.save ? f.save() : f)
  33. .filter(f => !!f)
  34. };
  35. return e;
  36. },
  37. simplify: function (self) {
  38. let e = {
  39. type: 'effects'
  40. };
  41. let effects = this.effects;
  42. if ((effects.length > 0) && (effects[0].obj)) {
  43. effects = effects
  44. .map(f => f.simplify())
  45. .filter(f => !!f);
  46. }
  47. e.effects = effects;
  48. return e;
  49. },
  50. destroy: function () {
  51. if (this.obj.instance)
  52. this.events.beforeRezone.call(this);
  53. },
  54. die: function () {
  55. this.events.beforeRezone.call(this, true);
  56. },
  57. reset: function () {
  58. let effects = this.effects;
  59. let eLen = effects.length;
  60. for (let i = 0; i < eLen; i++) {
  61. let effect = effects[i];
  62. if (effect.reset)
  63. effect.reset();
  64. }
  65. },
  66. reapply: function () {
  67. let effects = this.effects;
  68. let eLen = effects.length;
  69. for (let i = 0; i < eLen; i++) {
  70. let effect = effects[i];
  71. if (effect.reapply)
  72. effect.reapply();
  73. }
  74. },
  75. destroyEffect: function (effect) {
  76. this.obj.fireEvent('beforeDestroyEffect', effect);
  77. if (effect.events && effect.events.beforeDestroy)
  78. effect.events.beforeDestroy(effect);
  79. if (effect.destroy)
  80. effect.destroy();
  81. },
  82. events: {
  83. beforeRezone: function (forceDestroy) {
  84. let effects = this.effects;
  85. let eLen = effects.length;
  86. for (let i = 0; i < eLen; i++) {
  87. let effect = effects[i];
  88. if (!forceDestroy) {
  89. if (effect.persist) {
  90. this.syncRemove(effect.id);
  91. continue;
  92. }
  93. }
  94. this.destroyEffect(effect);
  95. this.syncRemove(effect.id);
  96. effects.splice(i, 1);
  97. eLen--;
  98. i--;
  99. }
  100. }
  101. },
  102. canApplyEffect: function (type) {
  103. if (!this.ccResistances.has(type))
  104. return true;
  105. let ccResistances = this.ccResistances;
  106. if ((100 - ccResistances[type]) >= 50) {
  107. ccResistances[type] += 50;
  108. return true;
  109. } return false;
  110. },
  111. addEffect: function (options, source) {
  112. //Skip 0-duration effects
  113. if ((options.has('ttl')) && (options.ttl === 0))
  114. return;
  115. options.caster = options.caster || source;
  116. //"X of Y in Z" cc resist check
  117. if (!options.force && !this.canApplyEffect(options.type))
  118. return;
  119. let oldEffect = this.effects.find(e => e.type === options.type);
  120. //If there is no existing effect or the effect is not stackable, make a new effect
  121. if (!oldEffect || !oldEffect.shouldStack)
  122. return this.buildEffect(options);
  123. //If the effect is stackable and the new effect should stack, stack with the old effect
  124. let shouldStack = oldEffect.shouldStack(options);
  125. if (shouldStack && oldEffect.incrementStack) {
  126. oldEffect.incrementStack(options);
  127. return oldEffect;
  128. }
  129. //Otherwise make a new effect
  130. return this.buildEffect(options);
  131. },
  132. getTypeTemplate: function (type) {
  133. let typeTemplate = null;
  134. if (type) {
  135. let capitalizedType = type[0].toUpperCase() + type.substr(1);
  136. let result = {
  137. type: type,
  138. url: 'config/effects/effect' + capitalizedType + '.js'
  139. };
  140. this.obj.instance.eventEmitter.emit('onBeforeGetEffect', result);
  141. typeTemplate = require('../' + result.url);
  142. }
  143. let builtEffect = extend({}, effectTemplate, typeTemplate);
  144. return builtEffect;
  145. },
  146. buildEffect: function (options) {
  147. let builtEffect = this.getTypeTemplate(options.type);
  148. for (let p in options)
  149. builtEffect[p] = options[p];
  150. builtEffect.obj = this.obj;
  151. builtEffect.id = this.nextId++;
  152. builtEffect.silent = options.silent;
  153. if (builtEffect.init)
  154. builtEffect.init(options.source);
  155. this.effects.push(builtEffect);
  156. if (!options.silent)
  157. this.obj.syncer.setArray(false, 'effects', 'addEffects', builtEffect.simplify());
  158. this.obj.instance.eventEmitter.emit('onAddEffect', this.obj, builtEffect);
  159. return builtEffect;
  160. },
  161. syncExtend: function (id, data) {
  162. let effect = this.effects.find(e => e.id === id);
  163. if (!effect)
  164. return;
  165. //Never sync silent effects
  166. if (effect.silent)
  167. return;
  168. this.obj.syncer.setArray(false, 'effects', 'extendEffects', {
  169. id,
  170. data
  171. });
  172. },
  173. syncRemove: function (id) {
  174. let effect = this.effects.find(e => e.id === id);
  175. if (!effect)
  176. return;
  177. if (effect.silent)
  178. return;
  179. this.obj.syncer.setArray(false, 'effects', 'removeEffects', id);
  180. },
  181. removeEffect: function (id) {
  182. const effect = this.effects.find(e => e.id === id);
  183. //It's possible that something else has removed the effect
  184. if (!effect)
  185. return;
  186. this.destroyEffect(effect);
  187. this.syncRemove(effect.id);
  188. this.effects.spliceWhere(e => e.id === id);
  189. },
  190. removeEffectByType: function (type) {
  191. const effects = this.effects.filter(e => e.type === type);
  192. effects.forEach(e => this.removeEffect(e.id));
  193. },
  194. getEffectByType: function (effectType) {
  195. const effect = this.effects.find(e => e.type === effectType);
  196. return effect;
  197. },
  198. fireEvent: function (event, args) {
  199. let effects = this.effects;
  200. let eLen = effects.length;
  201. for (let i = 0; i < eLen; i++) {
  202. let e = effects[i];
  203. //Maybe the effect killed us?
  204. if (!e) {
  205. i--;
  206. eLen--;
  207. continue;
  208. }
  209. if (e.ttl === 0)
  210. continue;
  211. let events = e.events;
  212. if (!events)
  213. continue;
  214. let callback = events[event];
  215. if (!callback)
  216. continue;
  217. callback.apply(e, args);
  218. }
  219. },
  220. update: function () {
  221. let effects = this.effects;
  222. let eLen = effects.length;
  223. for (let i = 0; i < eLen; i++) {
  224. let e = effects[i];
  225. if (e.ttl > 0)
  226. e.ttl--;
  227. else if (e.ttl === 0)
  228. e.destroyed = true;
  229. if (e.update)
  230. e.update();
  231. if (e.destroyed) {
  232. this.destroyEffect(e);
  233. this.syncRemove(e.id);
  234. effects.splice(i, 1);
  235. eLen--;
  236. i--;
  237. }
  238. }
  239. for (let p in this.ccResistances) {
  240. if (this.ccResistances[p] > 0)
  241. this.ccResistances[p]--;
  242. }
  243. }
  244. };