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

458 lines
10 KiB

  1. define([
  2. ], function(
  3. ) {
  4. return {
  5. type: 'stats',
  6. values: {
  7. mana: 10,
  8. manaMax: 10,
  9. hp: 5,
  10. hpMax: 5,
  11. xpTotal: 0,
  12. xp: 0,
  13. xpMax: 0,
  14. level: 1,
  15. str: 0,
  16. int: 0,
  17. dex: 0,
  18. magicFind: 0,
  19. regenHp: 0,
  20. regenMana: 10,
  21. addCritChance: 0,
  22. critChance: 5,
  23. armor: 0,
  24. dmgPercent: 0,
  25. elementArcanePercent: 0,
  26. elementFrostPercent: 0,
  27. elementFirePercent: 0,
  28. elementHolyPercent: 0,
  29. elementPhysicalPercent: 0,
  30. elementPoisonPercent: 0,
  31. elementArcaneResist: 0,
  32. elementFrostResist: 0,
  33. elementFireResist: 0,
  34. elementHolyResist: 0,
  35. elementPhysicalResist: 0,
  36. elementPoisonResist: 0,
  37. elementAllResist: 0,
  38. sprintChance: 0,
  39. xpIncrease: 0
  40. },
  41. vitScale: 10,
  42. syncer: null,
  43. stats: {
  44. logins: 0,
  45. played: 0
  46. },
  47. dead: false,
  48. init: function(blueprint) {
  49. this.syncer = this.obj.instance.syncer;
  50. var values = (blueprint || {}).values || {};
  51. for (var v in values) {
  52. this.values[v] = values[v];
  53. }
  54. this.calcXpMax();
  55. },
  56. resetHp: function() {
  57. var values = this.values;
  58. values.hp = values.hpMax;
  59. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  60. },
  61. update: function() {
  62. if ((this.obj.mob) || (this.dead))
  63. return;
  64. var regen = {
  65. success: true
  66. };
  67. this.obj.fireEvent('beforeRegen', regen);
  68. if (!regen.success)
  69. return;
  70. var values = this.values;
  71. var isInCombat = (this.obj.aggro.list.length > 0);
  72. var regenHp = 0;
  73. var regenMana = 0;
  74. regenMana = (values.manaMax / 200) + (values.regenMana / 200);
  75. if (!isInCombat)
  76. regenHp = values.hpMax / 100;
  77. else
  78. regenHp = values.regenHp * 0.3;
  79. if (values.hp < values.hpMax) {
  80. values.hp += regenHp;
  81. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  82. }
  83. if (values.hp > values.hpMax) {
  84. values.hp = values.hpMax;
  85. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  86. }
  87. if (values.mana < values.manaMax) {
  88. values.mana += regenMana;
  89. //Show others what mana is?
  90. var onlySelf = true;
  91. if (this.obj.player)
  92. onlySelf = false;
  93. this.obj.syncer.setObject(onlySelf, 'stats', 'values', 'mana', values.mana);
  94. }
  95. if (values.mana > values.manaMax) {
  96. values.mana = values.manaMax;
  97. if (this.obj.player)
  98. onlySelf = false;
  99. this.obj.syncer.setObject(onlySelf, 'stats', 'values', 'mana', values.mana);
  100. }
  101. },
  102. addStat: function(stat, value) {
  103. this.values[stat] += value;
  104. var sendOnlyToSelf = (['hp', 'hpMax', 'mana', 'manaMax'].indexOf(stat) == -1);
  105. this.obj.syncer.setObject(sendOnlyToSelf, 'stats', 'values', stat, this.values[stat]);
  106. if (stat == 'addCritChance') {
  107. this.values.critChance += (0.05 * value);
  108. this.obj.syncer.setObject(true, 'stats', 'values', 'critChance', this.values.critChance);
  109. } else if (stat == 'vit') {
  110. this.values.hpMax += (value * this.vitScale);
  111. this.obj.syncer.setObject(true, 'stats', 'values', 'hpMax', this.values.hpMax);
  112. } else if (stat == 'allAttributes') {
  113. ['int', 'str', 'dex'].forEach(function(s) {
  114. this.values[s] += value;
  115. this.obj.syncer.setObject(true, 'stats', 'values', s, this.values[s]);
  116. }, this);
  117. }
  118. },
  119. calcXpMax: function() {
  120. var level = this.values.level;
  121. this.values.xpMax = ~~(level * 10 * Math.pow(level, 1.75));
  122. this.obj.syncer.setObject(true, 'stats', 'values', 'xpMax', this.values.xpMax);
  123. },
  124. getXp: function(amount) {
  125. amount = ~~(amount * (1 + (this.values.xpIncrease / 100)));
  126. this.values.xpTotal = ~~(this.values.xpTotal + amount);
  127. this.values.xp = ~~(this.values.xp + amount);
  128. this.syncer.queue('onGetDamage', {
  129. id: this.obj.id,
  130. event: true,
  131. text: '+' + amount + ' xp'
  132. });
  133. var syncO = {};
  134. var didLevelUp = false;
  135. while (this.values.xp >= this.values.xpMax) {
  136. didLevelUp = true;
  137. this.values.xp -= this.values.xpMax;
  138. this.values.level++;
  139. this.values.hpMax += 40;
  140. this.syncer.queue('onGetDamage', {
  141. id: this.obj.id,
  142. event: true,
  143. text: 'level up'
  144. });
  145. this.obj.syncer.setObject(true, 'stats', 'values', 'level', this.values.level);
  146. this.obj.syncer.setObject(true, 'stats', 'values', 'hpMax', this.values.hpMax);
  147. syncO.level = this.values.level;
  148. this.calcXpMax();
  149. }
  150. if (didLevelUp)
  151. this.obj.auth.doSave();
  152. this.obj.syncer.setObject(true, 'stats', 'values', 'xp', this.values.xp);
  153. process.send({
  154. method: 'object',
  155. serverId: this.obj.serverId,
  156. obj: syncO
  157. });
  158. },
  159. kill: function(target) {
  160. var level = target.stats.values.level;
  161. //Who should get xp?
  162. var aggroList = target.aggro.list;
  163. var hpMax = target.stats.values.hpMax;
  164. var aLen = aggroList.length;
  165. for (var i = 0; i < aLen; i++) {
  166. var a = aggroList[i];
  167. var dmg = a.damage;
  168. if (dmg <= 0)
  169. continue;
  170. var mult = 1;
  171. //How many party members contributed
  172. // Remember, maybe one of the aggro-ees might be a mob too
  173. var party = a.obj.social ? a.obj.social.party : null;
  174. if (party) {
  175. var partySize = aggroList.filter(function(f) {
  176. return ((a.damage > 0) && (party.indexOf(f.obj.serverId) > -1));
  177. }).length;
  178. partySize--;
  179. mult = (1 + (partySize * 0.1));
  180. }
  181. if (a.obj.stats) {
  182. //Scale xp by source level so you can't just farm low level mobs (or get boosted on high level mobs).
  183. //Mobs that are farther then 10 levels from you, give no xp
  184. //We don't currently do this for quests/herb gathering
  185. var sourceLevel = a.obj.stats.values.level;
  186. var levelDelta = level - sourceLevel;
  187. var amount = level * 10 * mult;
  188. if (Math.abs(levelDelta) <= 10)
  189. amount = ~~(((sourceLevel + levelDelta) * 10) * Math.pow(1 - (Math.abs(levelDelta) / 10), 2) * mult);
  190. else
  191. amount = 0;
  192. a.obj.stats.getXp(amount, this.obj);
  193. }
  194. a.obj.fireEvent('afterKillMob', target);
  195. }
  196. target.fireEvent('afterDeath');
  197. },
  198. die: function(source) {
  199. this.values.hp = this.values.hpMax;
  200. this.values.mana = this.values.manaMax;
  201. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  202. this.obj.syncer.setObject(false, 'stats', 'values', 'mana', this.values.mana);
  203. this.syncer.queue('onGetDamage', {
  204. id: this.obj.id,
  205. event: true,
  206. text: 'death'
  207. });
  208. this.syncer.queue('onDeath', {
  209. x: this.obj.x,
  210. y: this.obj.y,
  211. source: source.name
  212. }, [this.obj.serverId]);
  213. },
  214. takeDamage: function(damage, threatMult, source) {
  215. source.fireEvent('beforeDealDamage', damage, this.obj);
  216. this.obj.fireEvent('beforeTakeDamage', damage, source);
  217. //Maybe the attacker was stunned?
  218. if (damage.failed)
  219. return;
  220. //Maybe something else killed this mob already?
  221. if (this.obj.destroyed)
  222. return;
  223. var amount = damage.amount;
  224. if (amount > this.values.hp)
  225. amount = this.values.hp;
  226. this.values.hp -= amount;
  227. var recipients = [];
  228. if (this.obj.serverId != null)
  229. recipients.push(this.obj.serverId);
  230. if (source.serverId != null)
  231. recipients.push(source.serverId);
  232. if (recipients.length > 0) {
  233. this.syncer.queue('onGetDamage', {
  234. id: this.obj.id,
  235. source: source.id,
  236. crit: damage.crit,
  237. amount: amount
  238. }, recipients);
  239. }
  240. this.obj.aggro.tryEngage(source, amount, threatMult);
  241. var died = (this.values.hp <= 0);
  242. if (died) {
  243. var death = {
  244. success: true
  245. };
  246. this.obj.fireEvent('beforeDeath', death);
  247. if (death.success) {
  248. var deathEvent = {};
  249. if (source.player)
  250. source.stats.kill(this.obj);
  251. else
  252. this.obj.fireEvent('afterDeath', deathEvent);
  253. if (this.obj.player) {
  254. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  255. if (deathEvent.permadeath) {
  256. this.obj.auth.permadie();
  257. this.syncer.queue('onPermadeath', {
  258. source: source.name
  259. }, [this.obj.serverId]);
  260. } else
  261. this.values.hp = 0;
  262. this.obj.player.die(source, deathEvent.permadeath);
  263. } else {
  264. this.obj.effects.die();
  265. if (this.obj.spellbook)
  266. this.obj.spellbook.die();
  267. this.obj.destroyed = true;
  268. if (this.obj.inventory) {
  269. var aggroList = this.obj.aggro.list;
  270. var aLen = aggroList.length;
  271. var done = [];
  272. for (var i = 0; i < aLen; i++) {
  273. var a = aggroList[i].obj;
  274. if (done.some(d => d == a.serverId))
  275. continue;
  276. if ((a.social) && (a.social.party)) {
  277. a.social.party.forEach(function(p) {
  278. if (done.some(d => d == p))
  279. return;
  280. this.obj.inventory.dropBag(p, source);
  281. done.push(p);
  282. }, this);
  283. } else {
  284. if (a.serverId == null)
  285. continue;
  286. this.obj.inventory.dropBag(a.serverId, source);
  287. done.push(a.serverId);
  288. }
  289. }
  290. }
  291. }
  292. }
  293. } else {
  294. source.aggro.tryEngage(this.obj, 0);
  295. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp);
  296. }
  297. source.fireEvent('afterDealDamage', damage, this.obj);
  298. },
  299. getHp: function(heal, source) {
  300. var values = this.values;
  301. var hpMax = values.hpMax;
  302. if (values.hp >= hpMax)
  303. return;
  304. var amount = heal.amount;
  305. if (hpMax - values.hp < amount)
  306. amount = hpMax - values.hp;
  307. values.hp += amount;
  308. if (values.hp > hpMax)
  309. values.hp = hpMax;
  310. var recipients = [];
  311. if (this.obj.serverId != null)
  312. recipients.push(this.obj.serverId);
  313. if (source.serverId != null)
  314. recipients.push(source.serverId);
  315. if (recipients.length > 0) {
  316. this.syncer.queue('onGetDamage', {
  317. id: this.obj.id,
  318. source: source.id,
  319. heal: true,
  320. amount: amount,
  321. crit: heal.crit
  322. }, recipients);
  323. }
  324. //Add aggro to all our attackers
  325. var threat = amount * 0.4;
  326. var aggroList = this.obj.aggro.list;
  327. var aLen = aggroList.length;
  328. for (var i = 0; i < aLen; i++) {
  329. var a = aggroList[i].obj;
  330. a.aggro.tryEngage(source, threat);
  331. }
  332. this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp);
  333. },
  334. save: function() {
  335. if (this.sessionDuration) {
  336. this.stats.played = ~~(this.stats.played + this.sessionDuration);
  337. delete this.sessionDuration;
  338. }
  339. return {
  340. type: 'stats',
  341. values: this.values,
  342. stats: this.stats
  343. };
  344. },
  345. simplify: function(self) {
  346. var values = this.values;
  347. if (!self) {
  348. var result = {
  349. type: 'stats',
  350. values: {
  351. hp: values.hp,
  352. hpMax: values.hpMax,
  353. mana: values.mana,
  354. manaMax: values.manaMax,
  355. level: values.level
  356. }
  357. };
  358. return result
  359. }
  360. return {
  361. type: 'stats',
  362. values: values,
  363. stats: this.stats,
  364. vitScale: this.vitScale
  365. };
  366. }
  367. };
  368. });