Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

858 rader
20 KiB

  1. define([
  2. 'config/animations',
  3. 'config/spirits',
  4. 'misc/scheduler'
  5. ], function (
  6. animations,
  7. classes,
  8. scheduler
  9. ) {
  10. var baseStats = {
  11. mana: 20,
  12. manaMax: 20,
  13. manaReservePercent: 0,
  14. hp: 5,
  15. hpMax: 5,
  16. xpTotal: 0,
  17. xp: 0,
  18. xpMax: 0,
  19. level: 1,
  20. str: 0,
  21. int: 0,
  22. dex: 0,
  23. magicFind: 0,
  24. itemQuantity: 0,
  25. regenHp: 0,
  26. regenMana: 5,
  27. addCritChance: 0,
  28. addCritMultiplier: 0,
  29. addAttackCritChance: 0,
  30. addAttackCritMultiplier: 0,
  31. addSpellCritChance: 0,
  32. addSpellCritMultiplier: 0,
  33. critChance: 5,
  34. critMultiplier: 150,
  35. attackCritChance: 0,
  36. attackCritMultiplier: 0,
  37. spellCritChance: 0,
  38. spellCritMultiplier: 0,
  39. armor: 0,
  40. dmgPercent: 0,
  41. vit: 0,
  42. blockAttackChance: 0,
  43. blockSpellChance: 0,
  44. dodgeAttackChance: 0,
  45. dodgeSpellChance: 0,
  46. attackSpeed: 0,
  47. castSpeed: 0,
  48. elementArcanePercent: 0,
  49. elementFrostPercent: 0,
  50. elementFirePercent: 0,
  51. elementHolyPercent: 0,
  52. elementPoisonPercent: 0,
  53. elementArcaneResist: 0,
  54. elementFrostResist: 0,
  55. elementFireResist: 0,
  56. elementHolyResist: 0,
  57. elementPoisonResist: 0,
  58. elementAllResist: 0,
  59. sprintChance: 0,
  60. xpIncrease: 0,
  61. //Fishing stats
  62. catchChance: 0,
  63. catchSpeed: 0,
  64. fishRarity: 0,
  65. fishWeight: 0,
  66. fishItems: 0
  67. };
  68. return {
  69. type: 'stats',
  70. values: baseStats,
  71. originalValues: null,
  72. statScales: {
  73. vitToHp: 10,
  74. strToArmor: 1,
  75. intToMana: (1 / 6),
  76. dexToDodge: (1 / 12)
  77. },
  78. syncer: null,
  79. stats: {
  80. logins: 0,
  81. played: 0,
  82. lastLogin: null,
  83. loginStreak: 0,
  84. mobKillStreaks: {},
  85. lootStats: {}
  86. },
  87. dead: false,
  88. init: function (blueprint, isTransfer) {
  89. this.syncer = this.obj.instance.syncer;
  90. var values = (blueprint || {}).values || {};
  91. for (var v in values) {
  92. this.values[v] = values[v];
  93. }
  94. var stats = (blueprint || {}).stats || {};
  95. for (var v in stats) {
  96. this.stats[v] = stats[v];
  97. }
  98. this.calcXpMax();
  99. if (blueprint)
  100. delete blueprint.stats;
  101. },
  102. resetHp: function () {
  103. var values = this.values;
  104. values.hp = values.hpMax;
  105. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  106. },
  107. update: function () {
  108. if (((this.obj.mob) && (!this.obj.follower)) || (this.obj.dead))
  109. return;
  110. var values = this.values;
  111. var manaMax = values.manaMax;
  112. manaMax -= (manaMax * values.manaReservePercent);
  113. var regen = {
  114. success: true
  115. };
  116. this.obj.fireEvent('beforeRegen', regen);
  117. if (!regen.success)
  118. return;
  119. var isInCombat = (this.obj.aggro.list.length > 0);
  120. if (this.obj.follower) {
  121. isInCombat = (this.obj.follower.master.aggro.list.length > 0);
  122. if (isInCombat)
  123. return;
  124. }
  125. var regenHp = 0;
  126. var regenMana = 0;
  127. regenMana = values.regenMana / 50;
  128. if (!isInCombat)
  129. regenHp = Math.max(values.hpMax / 112, values.regenHp * 0.2);
  130. else
  131. regenHp = values.regenHp * 0.2;
  132. if (values.hp < values.hpMax) {
  133. values.hp += regenHp;
  134. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  135. }
  136. if (values.hp > values.hpMax) {
  137. values.hp = values.hpMax;
  138. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  139. }
  140. if (values.mana < manaMax) {
  141. values.mana += regenMana;
  142. //Show others what mana is?
  143. var onlySelf = true;
  144. if (this.obj.player)
  145. onlySelf = false;
  146. this.obj.syncer.setObject(onlySelf, 'stats', 'values', 'mana', values.mana);
  147. }
  148. if (values.mana > manaMax) {
  149. values.mana = manaMax;
  150. if (this.obj.player)
  151. onlySelf = false;
  152. this.obj.syncer.setObject(onlySelf, 'stats', 'values', 'mana', values.mana);
  153. }
  154. },
  155. addStat: function (stat, value) {
  156. var values = this.values;
  157. if (['lvlRequire', 'allAttributes'].indexOf(stat) == -1)
  158. values[stat] += value;
  159. var sendOnlyToSelf = (['hp', 'hpMax', 'mana', 'manaMax', 'vit'].indexOf(stat) == -1);
  160. this.obj.syncer.setObject(sendOnlyToSelf, 'stats', 'values', stat, values[stat]);
  161. if (sendOnlyToSelf)
  162. this.obj.syncer.setObject(false, 'stats', 'values', stat, values[stat]);
  163. if (['addCritChance', 'addAttackCritChance', 'addSpellCritChance'].indexOf(stat) > -1) {
  164. var morphStat = stat.substr(3);
  165. morphStat = morphStat[0].toLowerCase() + morphStat.substr(1);
  166. this.addStat(morphStat, (0.05 * value));
  167. } else if (['addCritMultiplier', 'addAttackCritMultiplier', 'addSpellCritMultiplier'].indexOf(stat) > -1) {
  168. var morphStat = stat.substr(3);
  169. morphStat = morphStat[0].toLowerCase() + morphStat.substr(1);
  170. this.addStat(morphStat, value);
  171. } else if (stat == 'vit') {
  172. this.addStat('hpMax', (value * this.statScales.vitToHp));
  173. } else if (stat == 'allAttributes') {
  174. ['int', 'str', 'dex'].forEach(function (s) {
  175. this.addStat(s, value)
  176. }, this);
  177. } else if (stat == 'elementAllResist') {
  178. ['arcane', 'frost', 'fire', 'holy', 'poison'].forEach(function (s) {
  179. var element = 'element' + (s[0].toUpperCase() + s.substr(1)) + 'Resist';
  180. this.addStat(element, value);
  181. }, this);
  182. } else if (stat == 'str')
  183. this.addStat('armor', (value * this.statScales.strToArmor));
  184. else if (stat == 'int')
  185. this.addStat('manaMax', (value * this.statScales.intToMana));
  186. else if (stat == 'dex')
  187. this.addStat('dodgeAttackChance', (value * this.statScales.dexToDodge));
  188. },
  189. calcXpMax: function () {
  190. var level = (this.originalValues || this.values).level;
  191. this.values.xpMax = (level * 5) + ~~(level * 10 * Math.pow(level, 2.2));
  192. this.obj.syncer.setObject(true, 'stats', 'values', 'xpMax', this.values.xpMax);
  193. },
  194. //Source is the object that caused you to gain xp (mostly yourself)
  195. //Target is the source of the xp (a mob or quest)
  196. getXp: function (amount, source, target) {
  197. var obj = this.obj;
  198. var values = this.values;
  199. if ((this.originalValues || this.values).level == 20)
  200. return;
  201. var xpEvent = {
  202. source: source,
  203. target: target,
  204. amount: amount
  205. };
  206. this.obj.fireEvent('beforeGetXp', xpEvent);
  207. if (xpEvent.amount == 0)
  208. return;
  209. amount = ~~(xpEvent.amount * (1 + (values.xpIncrease / 100)));
  210. values.xpTotal = ~~(values.xpTotal + amount);
  211. values.xp = ~~(values.xp + amount);
  212. this.obj.syncer.setObject(true, 'stats', 'values', 'xp', values.xp);
  213. this.syncer.queue('onGetDamage', {
  214. id: obj.id,
  215. event: true,
  216. text: '+' + amount + ' xp'
  217. });
  218. var syncO = {};
  219. var didLevelUp = false;
  220. while (values.xp >= values.xpMax) {
  221. didLevelUp = true;
  222. values.xp -= values.xpMax;
  223. this.obj.syncer.setObject(true, 'stats', 'values', 'xp', values.xp);
  224. if (this.originalValues) {
  225. this.originalValues.level++;
  226. }
  227. if (values.originalLevel)
  228. values.originalLevel++;
  229. values.level++;
  230. this.obj.fireEvent('onLevelUp', (this.originalValues || this.values).level);
  231. if ((this.originalValues || this.values).level == 20)
  232. values.xp = 0;
  233. values.hpMax = values.level * 32.7;
  234. var gainStats = classes.stats[this.obj.class].gainStats;
  235. for (var s in gainStats) {
  236. this.addStat(s, gainStats[s]);
  237. }
  238. this.obj.spellbook.calcDps();
  239. this.syncer.queue('onGetDamage', {
  240. id: obj.id,
  241. event: true,
  242. text: 'level up'
  243. });
  244. syncO.level = (this.originalValues || this.values).level;
  245. this.calcXpMax();
  246. }
  247. if (didLevelUp) {
  248. var cellContents = obj.instance.physics.getCell(obj.x, obj.y);
  249. cellContents.forEach(function (c) {
  250. c.fireEvent('onCellPlayerLevelUp', obj);
  251. });
  252. obj.auth.doSave();
  253. }
  254. process.send({
  255. method: 'object',
  256. serverId: this.obj.serverId,
  257. obj: syncO
  258. });
  259. if (didLevelUp) {
  260. var maxLevel = this.obj.instance.zone.level[1]
  261. if (maxLevel < (this.originalValues || values).level) {
  262. this.rescale(maxLevel, false);
  263. } else {
  264. this.obj.syncer.setObject(true, 'stats', 'values', 'hpMax', values.hpMax);
  265. this.obj.syncer.setObject(true, 'stats', 'values', 'level', this.values.level);
  266. this.obj.syncer.setObject(true, 'stats', 'values', 'originalLevel', this.values.originalLevel);
  267. this.obj.syncer.setObject(false, 'stats', 'values', 'hpMax', values.hpMax);
  268. this.obj.syncer.setObject(false, 'stats', 'values', 'level', this.values.level);
  269. this.obj.syncer.setObject(true, 'stats', 'values', 'originalLevel', this.values.originalLevel);
  270. }
  271. }
  272. var originalValues = this.originalValues;
  273. if (originalValues) {
  274. originalValues.xp = values.xp;
  275. originalValues.xpMax = values.xpMax;
  276. originalValues.xpTotal = values.xpTotal;
  277. }
  278. },
  279. kill: function (target) {
  280. if (target.player)
  281. return;
  282. var level = target.stats.values.level;
  283. var mobDiffMult = 1;
  284. if (target.isRare)
  285. mobDiffMult = 2;
  286. else if (target.isChampion)
  287. mobDiffMult = 5;
  288. //Who should get xp?
  289. var aggroList = target.aggro.list;
  290. var hpMax = target.stats.values.hpMax;
  291. var aLen = aggroList.length;
  292. for (var i = 0; i < aLen; i++) {
  293. var a = aggroList[i];
  294. var dmg = a.damage;
  295. if (dmg <= 0)
  296. continue;
  297. var mult = 1;
  298. //How many party members contributed
  299. // Remember, maybe one of the aggro-ees might be a mob too
  300. var party = a.obj.social ? a.obj.social.party : null;
  301. if (party) {
  302. var partySize = aggroList.filter(function (f) {
  303. return ((a.damage > 0) && (party.indexOf(f.obj.serverId) > -1));
  304. }).length;
  305. partySize--;
  306. mult = (1 + (partySize * 0.1));
  307. }
  308. if ((a.obj.stats) && (!a.obj.follower)) {
  309. //Scale xp by source level so you can't just farm low level mobs (or get boosted on high level mobs).
  310. //Mobs that are farther then 10 levels from you, give no xp
  311. //We don't currently do this for quests/herb gathering
  312. var sourceLevel = a.obj.stats.values.level;
  313. var levelDelta = level - sourceLevel;
  314. var amount = null;
  315. if (Math.abs(levelDelta) <= 10)
  316. amount = ~~(((sourceLevel + levelDelta) * 10) * Math.pow(1 - (Math.abs(levelDelta) / 10), 2) * mult * mobDiffMult);
  317. else
  318. amount = 0;
  319. a.obj.stats.getXp(amount, this.obj, target);
  320. }
  321. a.obj.fireEvent('afterKillMob', target);
  322. }
  323. },
  324. die: function (source) {
  325. var obj = this.obj;
  326. var values = this.values;
  327. this.syncer.queue('onGetDamage', {
  328. id: obj.id,
  329. event: true,
  330. text: 'death'
  331. });
  332. obj.syncer.set(true, null, 'dead', true);
  333. var obj = obj;
  334. var syncO = obj.syncer.o;
  335. obj.hidden = true;
  336. obj.nonSelectable = true;
  337. syncO.hidden = true;
  338. syncO.nonSelectable = true;
  339. var xpLoss = ~~Math.min(values.xp, values.xpMax / 10);
  340. values.xp -= xpLoss;
  341. obj.syncer.setObject(true, 'stats', 'values', 'xp', values.xp);
  342. this.syncer.queue('onDeath', {
  343. source: source.name,
  344. xpLoss: xpLoss
  345. }, [obj.serverId]);
  346. obj.instance.syncer.queue('onGetObject', {
  347. x: obj.x,
  348. y: obj.y,
  349. components: [{
  350. type: 'attackAnimation',
  351. row: 0,
  352. col: 4
  353. }]
  354. });
  355. },
  356. respawn: function () {
  357. this.obj.syncer.set(true, null, 'dead', false);
  358. var obj = this.obj;
  359. var syncO = obj.syncer.o;
  360. this.obj.dead = false;
  361. var values = this.values;
  362. values.hp = values.hpMax;
  363. values.mana = values.manaMax;
  364. obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  365. obj.syncer.setObject(false, 'stats', 'values', 'mana', values.mana);
  366. obj.hidden = false;
  367. obj.nonSelectable = false;
  368. syncO.hidden = false;
  369. syncO.nonSelectable = false;
  370. process.send({
  371. method: 'object',
  372. serverId: this.obj.serverId,
  373. obj: {
  374. dead: false
  375. }
  376. });
  377. obj.instance.syncer.queue('onGetObject', {
  378. x: obj.x,
  379. y: obj.y,
  380. components: [{
  381. type: 'attackAnimation',
  382. row: 0,
  383. col: 4
  384. }]
  385. });
  386. this.obj.player.respawn();
  387. },
  388. takeDamage: function (damage, threatMult, source) {
  389. source.fireEvent('beforeDealDamage', damage, this.obj);
  390. this.obj.fireEvent('beforeTakeDamage', damage, source);
  391. //Maybe the attacker was stunned?
  392. if (damage.failed)
  393. return;
  394. //Maybe something else killed this mob already?
  395. if (this.obj.destroyed)
  396. return;
  397. var amount = damage.amount;
  398. if (amount > this.values.hp)
  399. amount = this.values.hp;
  400. damage.dealt = amount;
  401. this.values.hp -= amount;
  402. var recipients = [];
  403. if (this.obj.serverId != null)
  404. recipients.push(this.obj.serverId);
  405. if (source.serverId != null)
  406. recipients.push(source.serverId);
  407. if ((source.follower) && (source.follower.master.serverId))
  408. recipients.push(source.follower.master.serverId);
  409. if ((this.obj.follower) && (this.obj.follower.master.serverId))
  410. recipients.push(this.obj.follower.master.serverId);
  411. if (recipients.length > 0) {
  412. if ((!damage.blocked) && (!damage.dodged)) {
  413. this.syncer.queue('onGetDamage', {
  414. id: this.obj.id,
  415. source: source.id,
  416. crit: damage.crit,
  417. amount: amount
  418. }, recipients);
  419. } else {
  420. this.syncer.queue('onGetDamage', {
  421. id: this.obj.id,
  422. source: source.id,
  423. event: true,
  424. text: 'blocked'
  425. }, recipients);
  426. }
  427. }
  428. this.obj.aggro.tryEngage(source, amount, threatMult);
  429. var died = (this.values.hp <= 0);
  430. if (died) {
  431. var death = {
  432. success: true
  433. };
  434. this.obj.fireEvent('beforeDeath', death);
  435. if (death.success) {
  436. var deathEvent = {};
  437. var killSource = source;
  438. if (source.follower)
  439. killSource = source.follower.master;
  440. if (killSource.player)
  441. killSource.stats.kill(this.obj);
  442. this.obj.fireEvent('afterDeath', deathEvent);
  443. if (this.obj.player) {
  444. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  445. if (deathEvent.permadeath) {
  446. this.obj.auth.permadie();
  447. this.obj.instance.syncer.queue('onGetMessages', {
  448. messages: {
  449. class: 'color-redA',
  450. message: `(level ${this.values.level}) ${this.obj.name} has forever left the shores of the living.`
  451. }
  452. });
  453. this.syncer.queue('onPermadeath', {
  454. source: killSource.name
  455. }, [this.obj.serverId]);
  456. } else
  457. this.values.hp = 0;
  458. this.obj.player.die(killSource, deathEvent.permadeath);
  459. } else {
  460. this.obj.effects.die();
  461. if (this.obj.spellbook)
  462. this.obj.spellbook.die();
  463. this.obj.destroyed = true;
  464. var deathAnimation = _.getDeepProperty(animations, ['mobs', this.obj.sheetName, this.obj.cell, 'death']);
  465. if (deathAnimation) {
  466. this.obj.instance.syncer.queue('onGetObject', {
  467. x: this.obj.x,
  468. y: this.obj.y,
  469. components: [deathAnimation]
  470. });
  471. }
  472. if (this.obj.inventory) {
  473. var aggroList = this.obj.aggro.list;
  474. var aLen = aggroList.length;
  475. for (var i = 0; i < aLen; i++) {
  476. var a = aggroList[i];
  477. if ((!a.threat) || (a.obj.serverId == null))
  478. continue;
  479. this.obj.inventory.dropBag(a.obj.serverId, killSource);
  480. }
  481. }
  482. }
  483. }
  484. } else {
  485. source.aggro.tryEngage(this.obj, 0);
  486. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  487. }
  488. if (!damage.noEvents)
  489. source.fireEvent('afterDealDamage', damage, this.obj);
  490. },
  491. getHp: function (heal, source) {
  492. var amount = heal.amount;
  493. if (amount == 0)
  494. return;
  495. var threatMult = heal.threatMult;
  496. if (!heal.hasOwnProperty('threatMult'))
  497. threatMult = 1;
  498. var values = this.values;
  499. var hpMax = values.hpMax;
  500. if (values.hp >= hpMax)
  501. return;
  502. if (hpMax - values.hp < amount)
  503. amount = hpMax - values.hp;
  504. values.hp += amount;
  505. if (values.hp > hpMax)
  506. values.hp = hpMax;
  507. var recipients = [];
  508. if (this.obj.serverId != null)
  509. recipients.push(this.obj.serverId);
  510. if (source.serverId != null)
  511. recipients.push(source.serverId);
  512. if (recipients.length > 0) {
  513. this.syncer.queue('onGetDamage', {
  514. id: this.obj.id,
  515. source: source.id,
  516. heal: true,
  517. amount: amount,
  518. crit: heal.crit
  519. }, recipients);
  520. }
  521. //Add aggro to all our attackers
  522. var threat = amount * 0.4 * threatMult;
  523. var aggroList = this.obj.aggro.list;
  524. var aLen = aggroList.length;
  525. for (var i = 0; i < aLen; i++) {
  526. var a = aggroList[i].obj;
  527. a.aggro.tryEngage(source, threat);
  528. }
  529. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  530. },
  531. save: function () {
  532. if (this.sessionDuration) {
  533. this.stats.played = ~~(this.stats.played + this.sessionDuration);
  534. delete this.sessionDuration;
  535. }
  536. var values = extend(true, {}, this.originalValues || this.values);
  537. values.hp = this.values.hp;
  538. values.mana = this.values.mana;
  539. return {
  540. type: 'stats',
  541. values: values,
  542. stats: this.stats
  543. };
  544. },
  545. simplify: function (self) {
  546. var values = this.values;
  547. if (!self) {
  548. var result = {
  549. type: 'stats',
  550. values: {
  551. hp: values.hp,
  552. hpMax: values.hpMax,
  553. mana: values.mana,
  554. manaMax: values.manaMax,
  555. level: values.level
  556. }
  557. };
  558. return result
  559. }
  560. return {
  561. type: 'stats',
  562. values: values,
  563. stats: this.stats,
  564. vitScale: this.vitScale
  565. };
  566. },
  567. onLogin: function () {
  568. var stats = this.stats;
  569. var time = scheduler.getTime();
  570. stats.lastLogin = time;
  571. this.obj.instance.mail.getMail(this.obj.name);
  572. },
  573. rescale: function (level, isMob) {
  574. if (level > this.values.level)
  575. level = this.values.level;
  576. var sync = this.obj.syncer.setObject.bind(this.obj.syncer);
  577. var oldHp = this.values.hp;
  578. var oldXp = this.values.xp;
  579. var oldXpTotal = this.values.xpTotal;
  580. var oldXpMax = this.values.xpMax;
  581. if (!this.originalValues)
  582. this.originalValues = extend(true, {}, this.values);
  583. var oldValues = this.values;
  584. var newValues = extend(true, {}, baseStats);
  585. this.values = newValues;
  586. var gainStats = classes.stats[this.obj.class].gainStats;
  587. for (var s in gainStats) {
  588. this.addStat(s, (gainStats[s] * level));
  589. }
  590. newValues.level = level;
  591. newValues.originalLevel = (this.originalValues || oldValues).level;
  592. newValues.hpMax = level * 32.7;
  593. if (isMob)
  594. newValues.hpMax = ~~(newValues.hpMax * (level / 10));
  595. newValues.hp = oldHp;
  596. var resetHp = false;
  597. if (newValues.hp > newValues.hpMax) {
  598. resetHp = true;
  599. newValues.hp = newValues.hpMax;
  600. }
  601. newValues.xp = oldXp;
  602. newValues.xpMax = oldXpMax;
  603. newValues.xpTotal = oldXpTotal;
  604. var addStats = this.obj.equipment.rescale(level);
  605. for (var p in addStats) {
  606. var statName = p;
  607. this.addStat(statName, addStats[p]);
  608. }
  609. this.obj.passives.applyPassives();
  610. if (resetHp)
  611. newValues.hp = newValues.hpMax;
  612. this.obj.spellbook.calcDps();
  613. var publicStats = [
  614. 'hp',
  615. 'hpMax',
  616. 'mana',
  617. 'manaMax',
  618. 'level'
  619. ];
  620. for (var p in newValues) {
  621. sync(true, 'stats', 'values', p, newValues[p]);
  622. if (publicStats.indexOf(p) > -1)
  623. sync(false, 'stats', 'values', p, newValues[p]);
  624. }
  625. },
  626. getKillStreakCoefficient: function (mobName) {
  627. var killStreak = this.stats.mobKillStreaks[mobName];
  628. if (!killStreak)
  629. return 1;
  630. else
  631. return Math.max(0, (10000 - Math.pow(killStreak, 2)) / 10000);
  632. },
  633. canGetMobLoot: function (mob) {
  634. if (!mob.inventory.dailyDrops)
  635. return true;
  636. var lootStats = this.stats.lootStats[mob.name];
  637. var time = scheduler.getTime();
  638. if (!lootStats) {
  639. this.stats.lootStats[mob.name] = time;
  640. } else
  641. return ((lootStats.day != time.day), (lootStats.month != time.month));
  642. },
  643. events: {
  644. transferComplete: function () {
  645. var maxLevel = this.obj.instance.zone.level[1];
  646. if (maxLevel > this.obj.stats.values.level)
  647. maxLevel = this.obj.stats.values.level;
  648. this.obj.stats.rescale(maxLevel);
  649. },
  650. afterKillMob: function (mob) {
  651. var mobKillStreaks = this.stats.mobKillStreaks;
  652. var mobName = mob.name;
  653. if (!mobKillStreaks[mobName])
  654. mobKillStreaks.mobName = 0;
  655. if (mobKillStreaks[mobName] < 100)
  656. mobKillStreaks[mobName]++;
  657. for (var p in mobKillStreaks) {
  658. if (p == mobName)
  659. continue;
  660. mobKillStreaks[p]--;
  661. if (mobKillStreaks[p] <= 0)
  662. delete mobKillStreaks[p];
  663. }
  664. },
  665. beforeGetXp: function (event) {
  666. if ((!event.target.mob) && (!event.target.player))
  667. return;
  668. event.amount *= this.getKillStreakCoefficient(event.target.name);
  669. },
  670. beforeGenerateLoot: function (event) {
  671. if (!event.source.mob)
  672. return;
  673. event.chanceMultiplier *= this.getKillStreakCoefficient(event.source.name);
  674. if ((event.chanceMultiplier > 0) && (!this.canGetMobLoot(event.source)))
  675. event.chanceMultiplier = 0;
  676. },
  677. afterMove: function (event) {
  678. var mobKillStreaks = this.stats.mobKillStreaks;
  679. for (var p in mobKillStreaks) {
  680. mobKillStreaks[p] -= 0.085;
  681. if (mobKillStreaks[p] <= 0)
  682. delete mobKillStreaks[p];
  683. }
  684. }
  685. }
  686. };
  687. });