define([ ], function( ) { return { type: 'stats', values: { mana: 10, manaMax: 10, hp: 5, hpMax: 5, xpTotal: 0, xp: 0, xpMax: 0, level: 1, str: 0, int: 0, dex: 0, magicFind: 0, regenHp: 0, regenMana: 10, addCritChance: 0, critChance: 5, armor: 0, dmgPercent: 0, elementArcanePercent: 0, elementFrostPercent: 0, elementFirePercent: 0, elementHolyPercent: 0, elementPhysicalPercent: 0, elementPoisonPercent: 0, elementArcaneResist: 0, elementFrostResist: 0, elementFireResist: 0, elementHolyResist: 0, elementPhysicalResist: 0, elementPoisonResist: 0, elementAllResist: 0, sprintChance: 0, xpIncrease: 0 }, vitScale: 10, syncer: null, stats: { logins: 0, played: 0 }, dead: false, init: function(blueprint) { this.syncer = this.obj.instance.syncer; var values = (blueprint || {}).values || {}; for (var v in values) { this.values[v] = values[v]; } this.calcXpMax(); }, resetHp: function() { var values = this.values; values.hp = values.hpMax; this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp); }, update: function() { if ((this.obj.mob) || (this.dead)) return; var regen = { success: true }; this.obj.fireEvent('beforeRegen', regen); if (!regen.success) return; var values = this.values; var isInCombat = (this.obj.aggro.list.length > 0); var regenHp = 0; var regenMana = 0; regenMana = (values.manaMax / 200) + (values.regenMana / 200); if (!isInCombat) regenHp = values.hpMax / 100; else regenHp = values.regenHp * 0.3; if (values.hp < values.hpMax) { values.hp += regenHp; this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp); } if (values.hp > values.hpMax) { values.hp = values.hpMax; this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp); } if (values.mana < values.manaMax) { values.mana += regenMana; //Show others what mana is? var onlySelf = true; if (this.obj.player) onlySelf = false; this.obj.syncer.setObject(onlySelf, 'stats', 'values', 'mana', values.mana); } if (values.mana > values.manaMax) { values.mana = values.manaMax; if (this.obj.player) onlySelf = false; this.obj.syncer.setObject(onlySelf, 'stats', 'values', 'mana', values.mana); } }, addStat: function(stat, value) { this.values[stat] += value; var sendOnlyToSelf = (['hp', 'hpMax', 'mana', 'manaMax'].indexOf(stat) == -1); this.obj.syncer.setObject(sendOnlyToSelf, 'stats', 'values', stat, this.values[stat]); if (stat == 'addCritChance') { this.values.critChance += (0.05 * value); this.obj.syncer.setObject(true, 'stats', 'values', 'critChance', this.values.critChance); } else if (stat == 'vit') { this.values.hpMax += (value * this.vitScale); this.obj.syncer.setObject(true, 'stats', 'values', 'hpMax', this.values.hpMax); } else if (stat == 'allAttributes') { ['int', 'str', 'dex'].forEach(function(s) { this.values[s] += value; this.obj.syncer.setObject(true, 'stats', 'values', s, this.values[s]); }, this); } }, calcXpMax: function() { var level = this.values.level; this.values.xpMax = ~~(level * 10 * Math.pow(level, 1.75)); this.obj.syncer.setObject(true, 'stats', 'values', 'xpMax', this.values.xpMax); }, getXp: function(amount) { amount = ~~(amount * (1 + (this.values.xpIncrease / 100))); this.values.xpTotal = ~~(this.values.xpTotal + amount); this.values.xp = ~~(this.values.xp + amount); this.syncer.queue('onGetDamage', { id: this.obj.id, event: true, text: '+' + amount + ' xp' }); var syncO = {}; var didLevelUp = false; while (this.values.xp >= this.values.xpMax) { didLevelUp = true; this.values.xp -= this.values.xpMax; this.values.level++; this.values.hpMax += 40; this.syncer.queue('onGetDamage', { id: this.obj.id, event: true, text: 'level up' }); this.obj.syncer.setObject(true, 'stats', 'values', 'level', this.values.level); this.obj.syncer.setObject(true, 'stats', 'values', 'hpMax', this.values.hpMax); syncO.level = this.values.level; this.calcXpMax(); } if (didLevelUp) this.obj.auth.doSave(); this.obj.syncer.setObject(true, 'stats', 'values', 'xp', this.values.xp); process.send({ method: 'object', serverId: this.obj.serverId, obj: syncO }); }, kill: function(target) { var level = target.stats.values.level; //Who should get xp? var aggroList = target.aggro.list; var hpMax = target.stats.values.hpMax; var aLen = aggroList.length; for (var i = 0; i < aLen; i++) { var a = aggroList[i]; var dmg = a.damage; if (dmg <= 0) continue; var mult = 1; //How many party members contributed // Remember, maybe one of the aggro-ees might be a mob too var party = a.obj.social ? a.obj.social.party : null; if (party) { var partySize = aggroList.filter(function(f) { return ((a.damage > 0) && (party.indexOf(f.obj.serverId) > -1)); }).length; partySize--; mult = (1 + (partySize * 0.1)); } if (a.obj.stats) { //Scale xp by source level so you can't just farm low level mobs (or get boosted on high level mobs). //Mobs that are farther then 10 levels from you, give no xp //We don't currently do this for quests/herb gathering var sourceLevel = a.obj.stats.values.level; var levelDelta = level - sourceLevel; var amount = level * 10 * mult; if (Math.abs(levelDelta) <= 10) amount = ~~(((sourceLevel + levelDelta) * 10) * Math.pow(1 - (Math.abs(levelDelta) / 10), 2) * mult); a.obj.stats.getXp(amount, this.obj); } a.obj.fireEvent('afterKillMob', target); } target.fireEvent('afterDeath'); }, die: function(source) { this.values.hp = this.values.hpMax; this.values.mana = this.values.manaMax; this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp); this.obj.syncer.setObject(false, 'stats', 'values', 'mana', this.values.mana); this.syncer.queue('onGetDamage', { id: this.obj.id, event: true, text: 'death' }); this.syncer.queue('onDeath', { x: this.obj.x, y: this.obj.y, source: source.name }, [this.obj.serverId]); }, takeDamage: function(damage, threatMult, source) { source.fireEvent('beforeDealDamage', damage, this.obj); this.obj.fireEvent('beforeTakeDamage', damage, source); //Maybe the attacker was stunned? if (damage.failed) return; //Maybe something else killed this mob already? if (this.obj.destroyed) return; var amount = damage.amount; if (amount > this.values.hp) amount = this.values.hp; this.values.hp -= amount; var recipients = []; if (this.obj.serverId != null) recipients.push(this.obj.serverId); if (source.serverId != null) recipients.push(source.serverId); if (recipients.length > 0) { this.syncer.queue('onGetDamage', { id: this.obj.id, source: source.id, crit: damage.crit, amount: amount }, recipients); } this.obj.aggro.tryEngage(source, amount, threatMult); var died = (this.values.hp <= 0); if (died) { var death = { success: true }; this.obj.fireEvent('beforeDeath', death); if (death.success) { var deathEvent = {}; if (source.player) source.stats.kill(this.obj); else this.obj.fireEvent('afterDeath', deathEvent); if (this.obj.player) { this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp); if (deathEvent.permadeath) { this.obj.auth.permadie(); this.syncer.queue('onPermadeath', { source: source.name }, [this.obj.serverId]); } else this.values.hp = 0; this.obj.player.die(source, deathEvent.permadeath); } else { this.obj.effects.die(); if (this.obj.spellbook) this.obj.spellbook.die(); this.obj.destroyed = true; if (this.obj.inventory) { var aggroList = this.obj.aggro.list; var aLen = aggroList.length; var done = []; for (var i = 0; i < aLen; i++) { var a = aggroList[i].obj; if (done.some(d => d == a.serverId)) continue; if ((a.social) && (a.social.party)) { a.social.party.forEach(function(p) { if (done.some(d => d == p)) return; this.obj.inventory.dropBag(p, source); done.push(p); }, this); } else { if (a.serverId == null) continue; this.obj.inventory.dropBag(a.serverId, source); done.push(a.serverId); } } } } } } else { source.aggro.tryEngage(this.obj, 0); this.obj.syncer.setObject(false, 'stats', 'values', 'hp', this.values.hp); } source.fireEvent('afterDealDamage', damage, this.obj); }, getHp: function(heal, source) { var values = this.values; var hpMax = values.hpMax; if (values.hp >= hpMax) return; var amount = heal.amount; if (hpMax - values.hp < amount) amount = hpMax - values.hp; values.hp += amount; if (values.hp > hpMax) values.hp = hpMax; var recipients = []; if (this.obj.serverId != null) recipients.push(this.obj.serverId); if (source.serverId != null) recipients.push(source.serverId); if (recipients.length > 0) { this.syncer.queue('onGetDamage', { id: this.obj.id, source: source.id, heal: true, amount: amount, crit: heal.crit }, recipients); } //Add aggro to all our attackers var threat = amount * 0.4; var aggroList = this.obj.aggro.list; var aLen = aggroList.length; for (var i = 0; i < aLen; i++) { var a = aggroList[i].obj; a.aggro.tryEngage(source, threat); } this.obj.syncer.setObject(false, 'stats', 'values', 'hp', values.hp); }, save: function() { if (this.sessionDuration) { this.stats.played = ~~(this.stats.played + this.sessionDuration); delete this.sessionDuration; } return { type: 'stats', values: this.values, stats: this.stats }; }, simplify: function(self) { var values = this.values; if (!self) { var result = { type: 'stats', values: { hp: values.hp, hpMax: values.hpMax, mana: values.mana, manaMax: values.manaMax, level: values.level } }; return result } return { type: 'stats', values: values, stats: this.stats, vitScale: this.vitScale }; } }; });