25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

870 lines
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. this.obj.equipment.unequipAttrRqrGear();
  196. },
  197. calcXpMax: function () {
  198. var level = (this.originalValues || this.values).level;
  199. this.values.xpMax = (level * 5) + ~~(level * 10 * Math.pow(level, 2.2));
  200. this.obj.syncer.setObject(true, 'stats', 'values', 'xpMax', this.values.xpMax);
  201. },
  202. //Source is the object that caused you to gain xp (mostly yourself)
  203. //Target is the source of the xp (a mob or quest)
  204. getXp: function (amount, source, target) {
  205. var obj = this.obj;
  206. var values = this.values;
  207. if ((this.originalValues || this.values).level == 20)
  208. return;
  209. var xpEvent = {
  210. source: source,
  211. target: target,
  212. amount: amount
  213. };
  214. this.obj.fireEvent('beforeGetXp', xpEvent);
  215. if (xpEvent.amount == 0)
  216. return;
  217. amount = ~~(xpEvent.amount * (1 + (values.xpIncrease / 100)));
  218. values.xpTotal = ~~(values.xpTotal + amount);
  219. values.xp = ~~(values.xp + amount);
  220. this.obj.syncer.setObject(true, 'stats', 'values', 'xp', values.xp);
  221. this.syncer.queue('onGetDamage', {
  222. id: obj.id,
  223. event: true,
  224. text: '+' + amount + ' xp'
  225. });
  226. var syncO = {};
  227. var didLevelUp = false;
  228. while (values.xp >= values.xpMax) {
  229. didLevelUp = true;
  230. values.xp -= values.xpMax;
  231. this.obj.syncer.setObject(true, 'stats', 'values', 'xp', values.xp);
  232. if (this.originalValues) {
  233. this.originalValues.level++;
  234. }
  235. if (values.originalLevel)
  236. values.originalLevel++;
  237. values.level++;
  238. this.obj.fireEvent('onLevelUp', (this.originalValues || this.values).level);
  239. if ((this.originalValues || this.values).level == 20)
  240. values.xp = 0;
  241. values.hpMax = values.level * 32.7;
  242. var gainStats = classes.stats[this.obj.class].gainStats;
  243. for (var s in gainStats) {
  244. this.addStat(s, gainStats[s]);
  245. }
  246. this.obj.spellbook.calcDps();
  247. this.syncer.queue('onGetDamage', {
  248. id: obj.id,
  249. event: true,
  250. text: 'level up'
  251. });
  252. syncO.level = (this.originalValues || this.values).level;
  253. this.calcXpMax();
  254. }
  255. if (didLevelUp) {
  256. var cellContents = obj.instance.physics.getCell(obj.x, obj.y);
  257. cellContents.forEach(function (c) {
  258. c.fireEvent('onCellPlayerLevelUp', obj);
  259. });
  260. obj.auth.doSave();
  261. }
  262. process.send({
  263. method: 'object',
  264. serverId: this.obj.serverId,
  265. obj: syncO
  266. });
  267. if (didLevelUp) {
  268. var maxLevel = this.obj.instance.zone.level[1]
  269. if (maxLevel < (this.originalValues || values).level) {
  270. this.rescale(maxLevel, false);
  271. } else {
  272. this.obj.syncer.setObject(true, 'stats', 'values', 'hpMax', values.hpMax);
  273. this.obj.syncer.setObject(true, 'stats', 'values', 'level', this.values.level);
  274. this.obj.syncer.setObject(true, 'stats', 'values', 'originalLevel', this.values.originalLevel);
  275. this.obj.syncer.setObject(false, 'stats', 'values', 'hpMax', values.hpMax);
  276. this.obj.syncer.setObject(false, 'stats', 'values', 'level', this.values.level);
  277. this.obj.syncer.setObject(true, 'stats', 'values', 'originalLevel', this.values.originalLevel);
  278. }
  279. }
  280. var originalValues = this.originalValues;
  281. if (originalValues) {
  282. originalValues.xp = values.xp;
  283. originalValues.xpMax = values.xpMax;
  284. originalValues.xpTotal = values.xpTotal;
  285. }
  286. },
  287. kill: function (target) {
  288. if (target.player)
  289. return;
  290. var level = target.stats.values.level;
  291. var mobDiffMult = 1;
  292. if (target.isRare)
  293. mobDiffMult = 2;
  294. else if (target.isChampion)
  295. mobDiffMult = 5;
  296. //Who should get xp?
  297. var aggroList = target.aggro.list;
  298. var hpMax = target.stats.values.hpMax;
  299. var aLen = aggroList.length;
  300. for (var i = 0; i < aLen; i++) {
  301. var a = aggroList[i];
  302. var dmg = a.damage;
  303. if (dmg <= 0)
  304. continue;
  305. var mult = 1;
  306. //How many party members contributed
  307. // Remember, maybe one of the aggro-ees might be a mob too
  308. var party = a.obj.social ? a.obj.social.party : null;
  309. if (party) {
  310. var partySize = aggroList.filter(function (f) {
  311. return ((a.damage > 0) && (party.indexOf(f.obj.serverId) > -1));
  312. }).length;
  313. partySize--;
  314. mult = (1 + (partySize * 0.1));
  315. }
  316. if ((a.obj.stats) && (!a.obj.follower)) {
  317. //Scale xp by source level so you can't just farm low level mobs (or get boosted on high level mobs).
  318. //Mobs that are farther then 10 levels from you, give no xp
  319. //We don't currently do this for quests/herb gathering
  320. var sourceLevel = a.obj.stats.values.level;
  321. var levelDelta = level - sourceLevel;
  322. var amount = null;
  323. if (Math.abs(levelDelta) <= 10)
  324. amount = ~~(((sourceLevel + levelDelta) * 10) * Math.pow(1 - (Math.abs(levelDelta) / 10), 2) * mult * mobDiffMult);
  325. else
  326. amount = 0;
  327. a.obj.stats.getXp(amount, this.obj, target);
  328. }
  329. a.obj.fireEvent('afterKillMob', target);
  330. }
  331. },
  332. die: function (source) {
  333. var obj = this.obj;
  334. var values = this.values;
  335. this.syncer.queue('onGetDamage', {
  336. id: obj.id,
  337. event: true,
  338. text: 'death'
  339. });
  340. obj.syncer.set(true, null, 'dead', true);
  341. var obj = obj;
  342. var syncO = obj.syncer.o;
  343. obj.hidden = true;
  344. obj.nonSelectable = true;
  345. syncO.hidden = true;
  346. syncO.nonSelectable = true;
  347. var xpLoss = ~~Math.min(values.xp, values.xpMax / 10);
  348. values.xp -= xpLoss;
  349. obj.syncer.setObject(true, 'stats', 'values', 'xp', values.xp);
  350. this.syncer.queue('onDeath', {
  351. source: source.name,
  352. xpLoss: xpLoss
  353. }, [obj.serverId]);
  354. obj.instance.syncer.queue('onGetObject', {
  355. x: obj.x,
  356. y: obj.y,
  357. components: [{
  358. type: 'attackAnimation',
  359. row: 0,
  360. col: 4
  361. }]
  362. });
  363. },
  364. respawn: function () {
  365. this.obj.syncer.set(true, null, 'dead', false);
  366. var obj = this.obj;
  367. var syncO = obj.syncer.o;
  368. this.obj.dead = false;
  369. var values = this.values;
  370. values.hp = values.hpMax;
  371. values.mana = values.manaMax;
  372. obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  373. obj.syncer.setObject(false, 'stats', 'values', 'mana', values.mana);
  374. obj.hidden = false;
  375. obj.nonSelectable = false;
  376. syncO.hidden = false;
  377. syncO.nonSelectable = false;
  378. process.send({
  379. method: 'object',
  380. serverId: this.obj.serverId,
  381. obj: {
  382. dead: false
  383. }
  384. });
  385. obj.instance.syncer.queue('onGetObject', {
  386. x: obj.x,
  387. y: obj.y,
  388. components: [{
  389. type: 'attackAnimation',
  390. row: 0,
  391. col: 4
  392. }]
  393. });
  394. this.obj.player.respawn();
  395. },
  396. takeDamage: function (damage, threatMult, source) {
  397. source.fireEvent('beforeDealDamage', damage, this.obj);
  398. this.obj.fireEvent('beforeTakeDamage', damage, source);
  399. //Maybe the attacker was stunned?
  400. if (damage.failed)
  401. return;
  402. //Maybe something else killed this mob already?
  403. if (this.obj.destroyed)
  404. return;
  405. var amount = damage.amount;
  406. if (amount > this.values.hp)
  407. amount = this.values.hp;
  408. damage.dealt = amount;
  409. this.values.hp -= amount;
  410. var recipients = [];
  411. if (this.obj.serverId != null)
  412. recipients.push(this.obj.serverId);
  413. if (source.serverId != null)
  414. recipients.push(source.serverId);
  415. if ((source.follower) && (source.follower.master.serverId))
  416. recipients.push(source.follower.master.serverId);
  417. if ((this.obj.follower) && (this.obj.follower.master.serverId))
  418. recipients.push(this.obj.follower.master.serverId);
  419. if (recipients.length > 0) {
  420. if ((!damage.blocked) && (!damage.dodged)) {
  421. this.syncer.queue('onGetDamage', {
  422. id: this.obj.id,
  423. source: source.id,
  424. crit: damage.crit,
  425. amount: amount
  426. }, recipients);
  427. } else {
  428. this.syncer.queue('onGetDamage', {
  429. id: this.obj.id,
  430. source: source.id,
  431. event: true,
  432. text: 'blocked'
  433. }, recipients);
  434. }
  435. }
  436. this.obj.aggro.tryEngage(source, amount, threatMult);
  437. var died = (this.values.hp <= 0);
  438. if (died) {
  439. var death = {
  440. success: true
  441. };
  442. this.obj.fireEvent('beforeDeath', death);
  443. if (death.success) {
  444. var deathEvent = {};
  445. var killSource = source;
  446. if (source.follower)
  447. killSource = source.follower.master;
  448. if (killSource.player)
  449. killSource.stats.kill(this.obj);
  450. this.obj.fireEvent('afterDeath', deathEvent);
  451. if (this.obj.player) {
  452. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  453. if (deathEvent.permadeath) {
  454. this.obj.auth.permadie();
  455. this.obj.instance.syncer.queue('onGetMessages', {
  456. messages: {
  457. class: 'color-redA',
  458. message: `(level ${this.values.level}) ${this.obj.name} has forever left the shores of the living.`
  459. }
  460. });
  461. this.syncer.queue('onPermadeath', {
  462. source: killSource.name
  463. }, [this.obj.serverId]);
  464. } else
  465. this.values.hp = 0;
  466. this.obj.player.die(killSource, deathEvent.permadeath);
  467. } else {
  468. this.obj.effects.die();
  469. if (this.obj.spellbook)
  470. this.obj.spellbook.die();
  471. this.obj.destroyed = true;
  472. var deathAnimation = _.getDeepProperty(animations, ['mobs', this.obj.sheetName, this.obj.cell, 'death']);
  473. if (deathAnimation) {
  474. this.obj.instance.syncer.queue('onGetObject', {
  475. x: this.obj.x,
  476. y: this.obj.y,
  477. components: [deathAnimation]
  478. });
  479. }
  480. if (this.obj.inventory) {
  481. var aggroList = this.obj.aggro.list;
  482. var aLen = aggroList.length;
  483. for (var i = 0; i < aLen; i++) {
  484. var a = aggroList[i];
  485. if ((!a.threat) || (a.obj.serverId == null))
  486. continue;
  487. this.obj.inventory.dropBag(a.obj.serverId, killSource);
  488. }
  489. }
  490. }
  491. }
  492. } else {
  493. source.aggro.tryEngage(this.obj, 0);
  494. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  495. }
  496. if (!damage.noEvents)
  497. source.fireEvent('afterDealDamage', damage, this.obj);
  498. },
  499. getHp: function (heal, source) {
  500. var amount = heal.amount;
  501. if (amount == 0)
  502. return;
  503. var threatMult = heal.threatMult;
  504. if (!heal.hasOwnProperty('threatMult'))
  505. threatMult = 1;
  506. var values = this.values;
  507. var hpMax = values.hpMax;
  508. if (values.hp >= hpMax)
  509. return;
  510. if (hpMax - values.hp < amount)
  511. amount = hpMax - values.hp;
  512. values.hp += amount;
  513. if (values.hp > hpMax)
  514. values.hp = hpMax;
  515. var recipients = [];
  516. if (this.obj.serverId != null)
  517. recipients.push(this.obj.serverId);
  518. if (source.serverId != null)
  519. recipients.push(source.serverId);
  520. if (recipients.length > 0) {
  521. this.syncer.queue('onGetDamage', {
  522. id: this.obj.id,
  523. source: source.id,
  524. heal: true,
  525. amount: amount,
  526. crit: heal.crit
  527. }, recipients);
  528. }
  529. //Add aggro to all our attackers
  530. var threat = amount * 0.4 * threatMult;
  531. var aggroList = this.obj.aggro.list;
  532. var aLen = aggroList.length;
  533. for (var i = 0; i < aLen; i++) {
  534. var a = aggroList[i].obj;
  535. a.aggro.tryEngage(source, threat);
  536. }
  537. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  538. },
  539. save: function () {
  540. if (this.sessionDuration) {
  541. this.stats.played = ~~(this.stats.played + this.sessionDuration);
  542. delete this.sessionDuration;
  543. }
  544. var values = extend(true, {}, this.originalValues || this.values);
  545. values.hp = this.values.hp;
  546. values.mana = this.values.mana;
  547. return {
  548. type: 'stats',
  549. values: values,
  550. stats: this.stats
  551. };
  552. },
  553. simplify: function (self) {
  554. var values = this.values;
  555. if (!self) {
  556. var result = {
  557. type: 'stats',
  558. values: {
  559. hp: values.hp,
  560. hpMax: values.hpMax,
  561. mana: values.mana,
  562. manaMax: values.manaMax,
  563. level: values.level
  564. }
  565. };
  566. return result
  567. }
  568. return {
  569. type: 'stats',
  570. values: values,
  571. originalValues: this.originalValues,
  572. stats: this.stats,
  573. vitScale: this.vitScale
  574. };
  575. },
  576. onLogin: function () {
  577. var stats = this.stats;
  578. var time = scheduler.getTime();
  579. stats.lastLogin = time;
  580. this.obj.instance.mail.getMail(this.obj.name);
  581. },
  582. rescale: function (level, isMob) {
  583. if (level > this.values.level)
  584. level = this.values.level;
  585. var sync = this.obj.syncer.setObject.bind(this.obj.syncer);
  586. var oldHp = this.values.hp;
  587. var oldXp = this.values.xp;
  588. var oldXpTotal = this.values.xpTotal;
  589. var oldXpMax = this.values.xpMax;
  590. if (!this.originalValues)
  591. this.originalValues = extend(true, {}, this.values);
  592. var oldValues = this.values;
  593. var newValues = extend(true, {}, baseStats);
  594. newValues.level = level;
  595. newValues.originalLevel = (this.originalValues || oldValues).level;
  596. this.values = newValues;
  597. var gainStats = classes.stats[this.obj.class].gainStats;
  598. for (var s in gainStats) {
  599. this.addStat(s, (gainStats[s] * level));
  600. }
  601. newValues.hpMax = level * 32.7;
  602. if (isMob)
  603. newValues.hpMax = ~~(newValues.hpMax * (level / 10));
  604. newValues.hp = oldHp;
  605. var resetHp = false;
  606. if (newValues.hp > newValues.hpMax) {
  607. resetHp = true;
  608. newValues.hp = newValues.hpMax;
  609. }
  610. newValues.xp = oldXp;
  611. newValues.xpMax = oldXpMax;
  612. newValues.xpTotal = oldXpTotal;
  613. var addStats = this.obj.equipment.rescale(level);
  614. for (var p in addStats) {
  615. var statName = p;
  616. this.addStat(statName, addStats[p]);
  617. }
  618. this.obj.passives.applyPassives();
  619. if (resetHp)
  620. newValues.hp = newValues.hpMax;
  621. this.obj.spellbook.calcDps();
  622. var publicStats = [
  623. 'hp',
  624. 'hpMax',
  625. 'mana',
  626. 'manaMax',
  627. 'level'
  628. ];
  629. for (var p in newValues) {
  630. sync(true, 'stats', 'values', p, newValues[p]);
  631. if (publicStats.indexOf(p) > -1)
  632. sync(false, 'stats', 'values', p, newValues[p]);
  633. }
  634. },
  635. getKillStreakCoefficient: function (mobName) {
  636. var killStreak = this.stats.mobKillStreaks[mobName];
  637. if (!killStreak)
  638. return 1;
  639. else
  640. return Math.max(0, (10000 - Math.pow(killStreak, 2)) / 10000);
  641. },
  642. canGetMobLoot: function (mob) {
  643. if (!mob.inventory.dailyDrops)
  644. return true;
  645. var lootStats = this.stats.lootStats[mob.name];
  646. var time = scheduler.getTime();
  647. if (!lootStats) {
  648. this.stats.lootStats[mob.name] = time;
  649. } else
  650. return ((lootStats.day != time.day), (lootStats.month != time.month));
  651. },
  652. events: {
  653. transferComplete: function () {
  654. var maxLevel = this.obj.instance.zone.level[1];
  655. if (maxLevel > this.obj.stats.values.level)
  656. maxLevel = this.obj.stats.values.level;
  657. this.obj.stats.rescale(maxLevel);
  658. },
  659. afterKillMob: function (mob) {
  660. var mobKillStreaks = this.stats.mobKillStreaks;
  661. var mobName = mob.name;
  662. if (!mobKillStreaks[mobName])
  663. mobKillStreaks.mobName = 0;
  664. if (mobKillStreaks[mobName] < 100)
  665. mobKillStreaks[mobName]++;
  666. for (var p in mobKillStreaks) {
  667. if (p == mobName)
  668. continue;
  669. mobKillStreaks[p]--;
  670. if (mobKillStreaks[p] <= 0)
  671. delete mobKillStreaks[p];
  672. }
  673. },
  674. beforeGetXp: function (event) {
  675. if ((!event.target.mob) && (!event.target.player))
  676. return;
  677. event.amount *= this.getKillStreakCoefficient(event.target.name);
  678. },
  679. beforeGenerateLoot: function (event) {
  680. if (!event.source.mob)
  681. return;
  682. event.chanceMultiplier *= this.getKillStreakCoefficient(event.source.name);
  683. if ((event.chanceMultiplier > 0) && (!this.canGetMobLoot(event.source)))
  684. event.chanceMultiplier = 0;
  685. },
  686. afterMove: function (event) {
  687. var mobKillStreaks = this.stats.mobKillStreaks;
  688. for (var p in mobKillStreaks) {
  689. mobKillStreaks[p] -= 0.085;
  690. if (mobKillStreaks[p] <= 0)
  691. delete mobKillStreaks[p];
  692. }
  693. }
  694. }
  695. };
  696. });