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.
 
 
 

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