diff --git a/src/client/images/uiIcons.png b/src/client/images/uiIcons.png index bef1cb05..f36e8cee 100644 Binary files a/src/client/images/uiIcons.png and b/src/client/images/uiIcons.png differ diff --git a/src/client/images/uiIcons.pyxel b/src/client/images/uiIcons.pyxel index 1466d6ee..5809c3ee 100644 Binary files a/src/client/images/uiIcons.pyxel and b/src/client/images/uiIcons.pyxel differ diff --git a/src/client/js/components/chatter.js b/src/client/js/components/chatter.js index aae9b989..108d4eed 100644 --- a/src/client/js/components/chatter.js +++ b/src/client/js/components/chatter.js @@ -56,6 +56,16 @@ define([ obj.chatSprite.visible = true; this.cd = this.cdMax; + }, + + destroy: function() { + var chatSprite = this.obj.chatSprite; + if (!chatSprite) + return; + + renderer.destroyObject({ + sprite: chatSprite + }); } }; }); \ No newline at end of file diff --git a/src/client/js/components/keyboardMover.js b/src/client/js/components/keyboardMover.js index 8ccdeca8..c9a2e96d 100644 --- a/src/client/js/components/keyboardMover.js +++ b/src/client/js/components/keyboardMover.js @@ -10,7 +10,6 @@ define([ return { type: 'keyboardMover', - path: [], moveCd: 0, moveCdMax: 8, direction: { diff --git a/src/client/js/components/pather.js b/src/client/js/components/pather.js index fcbe060e..522e6752 100644 --- a/src/client/js/components/pather.js +++ b/src/client/js/components/pather.js @@ -13,7 +13,8 @@ define([ path: [], - pathColor: 'rgba(255, 255, 255, 0.5)', + pathColor: '0x48edff', + pathAlpha: 0.2, pathPos: { x: 0, @@ -50,7 +51,8 @@ define([ y: y, sprite: renderer.buildRectangle({ layerName: 'effects', - alpha: 0.2, + color: this.pathColor, + alpha: this.pathAlpha, x: (x * scale) + scaleMult, y: (y * scale) + scaleMult, w: scale - (scaleMult * 2), diff --git a/src/client/js/components/spellbook.js b/src/client/js/components/spellbook.js index 080e9548..3c863d15 100644 --- a/src/client/js/components/spellbook.js +++ b/src/client/js/components/spellbook.js @@ -72,10 +72,14 @@ define([ if (blueprint.getSpells) { blueprint.getSpells.forEach(function(s) { - if (this.spells.find(function(spell) { + var existIndex = this.spells.firstIndex(function(spell) { return (spell.id == s.id); - })) + }); + + if (existIndex > -1) { + this.spells.splice(existIndex, 1, s); return; + } if (this.spells.length - 1 >= s.id) this.spells.splice(s.id, 0, s); diff --git a/src/client/js/input.js b/src/client/js/input.js index b7d7e34f..e5b00f95 100644 --- a/src/client/js/input.js +++ b/src/client/js/input.js @@ -22,6 +22,7 @@ define([ '9': 'tab', '13': 'enter', '16': 'shift', + '17': 'ctrl', '27': 'esc', '37': 'left', '38': 'up', diff --git a/src/client/js/misc/statTranslations.js b/src/client/js/misc/statTranslations.js new file mode 100644 index 00000000..00f6d92a --- /dev/null +++ b/src/client/js/misc/statTranslations.js @@ -0,0 +1,47 @@ +define([ + +], function( + +) { + var stats = { + 'vit': 'vitality', + 'hpMax': 'vitality', + 'regenHp': 'health regeneration', + 'manaMax': 'maximum mana', + 'regenMana': 'mana regeneration', + 'str': 'strength', + 'int': 'intellect', + 'dex': 'dexterity', + 'armor': 'armor', + 'addCritChance': 'increased crit chance', + 'magicFind': 'magic find', + 'sprintChance': 'sprint chance', + 'dmgPercent': 'to all damage', + 'allAttributes': 'to all attributes', + 'xpIncrease': 'additional xp per kill', + + 'elementArcanePercent': 'increased arcane damage', + 'elementFrostPercent': 'increased frost damage', + 'elementFirePercent': 'increased fire damage', + 'elementHolyPercent': 'increased holy damage', + 'elementPhysicalPercent': 'increased physical damage', + 'elementPoisonPercent': 'increased poison damage', + + 'elementArcaneResist': 'arcane resistance', + 'elementFrostResist': 'frost resistance', + 'elementFireResist': 'fire resistance', + 'elementHolyResist': 'holy resistance', + 'elementPhysicalResist': 'physical resistance', + 'elementPoisonResist': 'poison resistance', + 'elementAllResist': 'all resistance', + + //This stat is used for gambling when you can't see the stats + 'stats': 'stats' + }; + + return { + translate: function(stat) { + return stats[stat]; + } + }; +}); \ No newline at end of file diff --git a/src/client/js/renderer.js b/src/client/js/renderer.js index 198b7ac3..3d71f0b3 100644 --- a/src/client/js/renderer.js +++ b/src/client/js/renderer.js @@ -181,8 +181,10 @@ define([ }, onResize: function() { - this.width = $('body').width(); - this.height = $('body').height(); + var zoom = window.devicePixelRatio; + + this.width = $('body').width() * zoom; + this.height = $('body').height() * zoom; this.renderer.resize(this.width, this.height); if (window.player) { diff --git a/src/client/ui/templates/equipment/equipment.js b/src/client/ui/templates/equipment/equipment.js index 7e7714b8..c3437b6f 100644 --- a/src/client/ui/templates/equipment/equipment.js +++ b/src/client/ui/templates/equipment/equipment.js @@ -90,10 +90,11 @@ define([ this.find('[slot]') .removeData('item') - .addClass('empty') + .addClass('empty show-default-icon') .find('.icon') .off() - .css('background', '') + .css('background-image', '') + .css('background-position', '') .on('click', this.buildSlot.bind(this)); items @@ -121,7 +122,7 @@ define([ var elSlot = this.find('[slot="' + slot + '"]'); elSlot .data('item', item) - .removeClass('empty') + .removeClass('empty show-default-icon') .find('.icon') .css('background', 'url("' + spritesheet + '") ' + imgX + 'px ' + imgY + 'px') .off() diff --git a/src/client/ui/templates/equipment/styles.less b/src/client/ui/templates/equipment/styles.less index b3be7ec0..a7bbc404 100644 --- a/src/client/ui/templates/equipment/styles.less +++ b/src/client/ui/templates/equipment/styles.less @@ -77,6 +77,25 @@ margin-bottom: 0px; } + &.show-default-icon { + .icon { + opacity: 0.5; + background-image: url('../../../images/uiIcons.png') !important; + } + + &[slot="head"] .icon { background-position: -0px -448px; } + &[slot="neck"] .icon { background-position: -64px -448px; } + &[slot="chest"] .icon { background-position: -128px -448px; } + &[slot="hands"] .icon { background-position: -192px -448px; } + &[slot="finger"] .icon { background-position: -256px -448px; } + &[slot="waist"] .icon { background-position: -320px -448px; } + &[slot="legs"] .icon { background-position: -384px -448px; } + &[slot="feet"] .icon { background-position: -448px -448px; } + &[slot="trinket"] .icon { background-position: -448px -384px; } + &[slot="twoHanded"] .icon { background-position: -384px -384px; } + &[slot^="rune"] .icon { background-position: -320px -384px; } + } + .icon { height: 100%; } diff --git a/src/client/ui/templates/help/template.html b/src/client/ui/templates/help/template.html index 2a6f6d38..1eee3fa2 100644 --- a/src/client/ui/templates/help/template.html +++ b/src/client/ui/templates/help/template.html @@ -1,5 +1,6 @@
Movement:
WASD / Arrow Keys
+
Cancel Movement:
Esc
Combat:
Hover on an enemy and press 1 to enable auto-attack
Chat:
Press Enter to open the chat window
Inventory:
Press i to open your inventory
diff --git a/src/client/ui/templates/inventory/inventory.js b/src/client/ui/templates/inventory/inventory.js index 99f8b80f..f83bec0f 100644 --- a/src/client/ui/templates/inventory/inventory.js +++ b/src/client/ui/templates/inventory/inventory.js @@ -47,6 +47,7 @@ define([ items: [], shiftDown: false, + ctrlDown: false, dragItem: null, dragEl: null, @@ -127,7 +128,7 @@ define([ itemEl .data('item', item) - //.on('click', this.onClick.bind(this, itemEl, item)) + .on('click', this.onClick.bind(this, item)) .on('mousedown', this.onMouseDown.bind(this, itemEl, item, true)) .on('mouseup', this.onMouseDown.bind(this, null, null, false)) .on('mousemove', this.onHover.bind(this, itemEl, item)) @@ -150,6 +151,20 @@ define([ } }, + onClick: function(item) { + if (!this.ctrlDown) + return; + + client.request({ + cpn: 'social', + method: 'chat', + data: { + message: '{' + item.name + '}', + item: item + } + }); + }, + onMouseDown: function(el, item, down, e) { if (e.button != 0) return; @@ -467,6 +482,8 @@ define([ if (this.hoverItem) this.onHover(); } + else if (key == 'ctrl') + this.ctrlDown = true; }, onKeyUp: function(key) { if (key == 'shift') { @@ -474,6 +491,8 @@ define([ if (this.hoverItem) this.onHover(); } + else if (key == 'ctrl') + this.ctrlDown = false; } }; }); \ No newline at end of file diff --git a/src/client/ui/templates/messages/messages.js b/src/client/ui/templates/messages/messages.js index d6360594..caea15de 100644 --- a/src/client/ui/templates/messages/messages.js +++ b/src/client/ui/templates/messages/messages.js @@ -17,12 +17,12 @@ define([ messages: [], maxTtl: 500, + shiftDown: false, + hoverItem: null, + hoverFilter: false, postRender: function() { - //HACK: Write a global manager - //setInterval(this.fade.bind(this), 100); - this.onEvent('onGetMessages', this.onGetMessages.bind(this)); this.onEvent('onDoWhisper', this.onDoWhisper.bind(this)); @@ -53,9 +53,8 @@ define([ }, onKeyDown: function(key, state) { - if (key == 'enter') { + if (key == 'enter') this.toggle(true); - } }, onDoWhisper: function(charName) { @@ -75,7 +74,13 @@ define([ var container = this.find('.list'); messages.forEach(function(m) { - var el = $('
' + m.message + '
') + var message = m.message; + if (m.item) { + var source = message.split(':')[0] + ': '; + message = source + '' + message.replace(source, '') + ''; + } + + var el = $('
' + message + '
') .appendTo(container); if (m.type != null) @@ -83,6 +88,12 @@ define([ else el.addClass('info'); + if (m.item) { + el.find('span') + .on('mousemove', this.showItemTooltip.bind(this, el, m.item)) + .on('mouseleave', this.hideItemTooltip.bind(this)); + } + this.messages.push({ ttl: this.maxTtl, el: el @@ -92,6 +103,35 @@ define([ container.scrollTop(9999999); }, + hideItemTooltip: function() { + if (this.dragEl) { + this.hoverCell = null; + return; + } + + events.emit('onHideItemTooltip', this.hoverItem); + this.hoverItem = null; + }, + showItemTooltip: function(el, item, e) { + if (item) + this.hoverItem = item; + else + item = this.hoverItem; + + if (!item) + return; + + var ttPos = null; + if (el) { + ttPos = { + x: ~~(e.clientX + 32), + y: ~~(e.clientY) + }; + } + + events.emit('onShowItemTooltip', item, ttPos, null, true); + }, + update: function() { return; var maxTtl = this.maxTtl; diff --git a/src/client/ui/templates/messages/styles.less b/src/client/ui/templates/messages/styles.less index e7af65a9..5a185235 100644 --- a/src/client/ui/templates/messages/styles.less +++ b/src/client/ui/templates/messages/styles.less @@ -14,6 +14,8 @@ pointer-events: none; &.typing { + pointer-events: all; + .el.message { display: block; } diff --git a/src/client/ui/templates/party/party.js b/src/client/ui/templates/party/party.js index 49c24768..475c7429 100644 --- a/src/client/ui/templates/party/party.js +++ b/src/client/ui/templates/party/party.js @@ -100,6 +100,10 @@ define([ }, onGetParty: function(party) { + // Destroy invite frame if you join a party + if (this.invite) + this.destroyInvite(); + var container = this.find('.party .list') .empty(); @@ -179,10 +183,12 @@ define([ }, destroyInvite: function(e) { - if ($(e.target).hasClass('btnAccept')) - this.acceptInvite(); - else - this.declineInvite(); + if (e) { + if ($(e.target).hasClass('btnAccept')) + this.acceptInvite(); + else + this.declineInvite(); + } this.invite.el.remove(); this.invite = null; diff --git a/src/client/ui/templates/smithing/smithing.js b/src/client/ui/templates/smithing/smithing.js index e2994310..cd220f57 100644 --- a/src/client/ui/templates/smithing/smithing.js +++ b/src/client/ui/templates/smithing/smithing.js @@ -3,13 +3,15 @@ define([ 'js/system/client', 'html!ui/templates/smithing/template', 'css!ui/templates/smithing/styles', - 'html!/ui/templates/smithing/templateItem' + 'html!/ui/templates/smithing/templateItem', + 'js/misc/statTranslations' ], function( events, client, template, styles, - templateItem + templateItem, + statTranslations ) { return { tpl: template, @@ -89,7 +91,7 @@ define([ } result.addStatMsgs.forEach(function(a) { - msg.msg += '
' + a; + msg.msg += '
' + ((a.value > 0) ? '+' : '') + a.value + ' ' + statTranslations.translate(a.stat); }); events.emit('onGetAnnouncement', msg); diff --git a/src/client/ui/templates/spells/styles.less b/src/client/ui/templates/spells/styles.less index a576258e..9e30986d 100644 --- a/src/client/ui/templates/spells/styles.less +++ b/src/client/ui/templates/spells/styles.less @@ -48,6 +48,7 @@ bottom: -6px; color: @white; font-size: 18px; + z-index: 2; text-shadow: 2px 2px 0 #2d2136, -2px -2px 0 #2d2136, 2px -2px 0 #2d2136, diff --git a/src/client/ui/templates/talk/talk.js b/src/client/ui/templates/talk/talk.js index 596c9994..21e606ee 100644 --- a/src/client/ui/templates/talk/talk.js +++ b/src/client/ui/templates/talk/talk.js @@ -18,6 +18,11 @@ define([ postRender: function() { this.onEvent('onGetTalk', this.onGetTalk.bind(this)); + this.onEvent('onRezone', this.onRezone.bind(this)); + }, + + onRezone: function() { + this.hide(); }, onGetTalk: function(dialogue) { diff --git a/src/client/ui/templates/target/target.js b/src/client/ui/templates/target/target.js index 76dee48a..ad501ccd 100644 --- a/src/client/ui/templates/target/target.js +++ b/src/client/ui/templates/target/target.js @@ -13,6 +13,7 @@ define([ target: null, lastHp: null, lastMana: null, + lastLevel: null, postRender: function() { this.onEvent('onSetTarget', this.onSetTarget.bind(this)); @@ -50,6 +51,7 @@ define([ if (!this.target) { this.lastHp = null; this.lastMana = null; + this.lastLevel = null; this.el.hide(); } else { var el = this.el; @@ -91,6 +93,16 @@ define([ var stats = target.stats.values; + if (stats.level != this.lastLevel) { + this.el.find('.infoLevel') + .html('(' + stats.level + ')') + .removeClass('high-level'); + + var crushing = (stats.level - 5 >= window.player.stats.level); + if (crushing) + this.el.find('.infoLevel').addClass('high-level'); + } + if (stats.hp != this.lastHp) { this.buildBar(0, stats.hp, stats.hpMax); this.lastHp = stats.hp; diff --git a/src/client/ui/templates/tooltipItem/tooltipItem.js b/src/client/ui/templates/tooltipItem/tooltipItem.js index f7157c13..1f3f279d 100644 --- a/src/client/ui/templates/tooltipItem/tooltipItem.js +++ b/src/client/ui/templates/tooltipItem/tooltipItem.js @@ -2,12 +2,14 @@ define([ 'js/system/events', 'css!ui/templates/tooltipItem/styles', 'html!ui/templates/tooltipItem/template', - 'html!ui/templates/tooltipItem/templateTooltip' + 'html!ui/templates/tooltipItem/templateTooltip', + 'js/misc/statTranslations' ], function( events, styles, template, - tplTooltip + tplTooltip, + statTranslations ) { return { tpl: template, @@ -59,7 +61,7 @@ define([ stats = Object.keys(tempStats) .map(function(s) { - var statName = this.mapStat(s); + var statName = statTranslations.translate(s); var value = tempStats[s]; if (['addCritChance', 'sprintChance', 'dmgPercent', 'xpIncrease'].indexOf(s) > -1) @@ -216,44 +218,6 @@ define([ if (!canAfford) this.tooltip.find('.worth').addClass('no-afford'); - }, - - mapStat: function(stat) { - return { - 'vit': 'vitality', - 'hpMax': 'vitality', - 'regenHp': 'health regeneration', - 'manaMax': 'maximum mana', - 'regenMana': 'mana regeneration', - 'str': 'strength', - 'int': 'intellect', - 'dex': 'dexterity', - 'armor': 'armor', - 'addCritChance': 'increased crit chance', - 'magicFind': 'magic find', - 'sprintChance': 'sprint chance', - 'dmgPercent': 'to all damage', - 'allAttributes': 'to all attributes', - 'xpIncrease': 'additional xp per kill', - - 'elementArcanePercent': 'increased arcane damage', - 'elementFrostPercent': 'increased frost damage', - 'elementFirePercent': 'increased fire damage', - 'elementHolyPercent': 'increased holy damage', - 'elementPhysicalPercent': 'increased physical damage', - 'elementPoisonPercent': 'increased poison damage', - - 'elementArcaneResist': 'arcane resistance', - 'elementFrostResist': 'frost resistance', - 'elementFireResist': 'fire resistance', - 'elementHolyResist': 'holy resistance', - 'elementPhysicalResist': 'physical resistance', - 'elementPoisonResist': 'poison resistance', - 'elementAllResist': 'all resistance', - - //This stat is used for gambling when you can't see the stats - 'stats': 'stats' - }[stat]; } }; }); \ No newline at end of file diff --git a/src/client/ui/templates/trade/template.html b/src/client/ui/templates/trade/template.html index 97d46e58..07bfd98b 100644 --- a/src/client/ui/templates/trade/template.html +++ b/src/client/ui/templates/trade/template.html @@ -1,3 +1,10 @@
+
+
trade
+
+
+
+
+
\ No newline at end of file diff --git a/src/client/ui/templates/trade/trade.js b/src/client/ui/templates/trade/trade.js index 3aebcea8..33e0b0ff 100644 --- a/src/client/ui/templates/trade/trade.js +++ b/src/client/ui/templates/trade/trade.js @@ -23,6 +23,8 @@ define([ }, onGetTradeList: function(itemList, action) { + this.find('.heading-text').html(action); + var uiInventory = $('.uiInventory').data('ui'); var container = this.el.find('.grid') @@ -54,8 +56,14 @@ define([ return 0; }); - var iLen = buyItems.length; + var iLen = Math.max(buyItems.length, 50); for (var i = 0; i < iLen; i++) { + if (!buyItems[i]) { + $(tplItem).appendTo(container); + + continue; + } + var item = $.extend(true, {}, buyItems[i]); item.worth = ~~(itemList.markup * item.worth); @@ -74,7 +82,7 @@ define([ } var imgX = (-item.sprite[0] * size) + offset; - var imgY = (-item.sprite[1] * size) + offset + 4; + var imgY = (-item.sprite[1] * size) + offset; var itemEl = $(tplItem) .appendTo(container); diff --git a/src/server/components/auth.js b/src/server/components/auth.js index 6b7e8ef8..d41848b5 100644 --- a/src/server/components/auth.js +++ b/src/server/components/auth.js @@ -3,13 +3,15 @@ define([ 'misc/messages', 'security/connections', 'leaderboard/leaderboard', - 'config/skins' + 'config/skins', + 'misc/profanities' ], function( io, messages, connections, leaderboard, - skins + skins, + profanities ) { return { type: 'auth', @@ -252,6 +254,11 @@ define([ } } + if (!profanities.isClean(credentials.username)) { + msg.callback(messages.login.invalid); + return; + } + io.get({ ent: credentials.username, field: 'login', @@ -295,6 +302,11 @@ define([ return; } + if (!profanities.isClean(data.name)) { + msg.callback(messages.login.invalid); + return; + } + io.get({ ent: data.name, field: 'character', diff --git a/src/server/components/dialogue.js b/src/server/components/dialogue.js index 090b7648..991e3a94 100644 --- a/src/server/components/dialogue.js +++ b/src/server/components/dialogue.js @@ -26,7 +26,7 @@ define([ return false; } else if (targetName != null) { - target = this.obj.instance.objects.objects.find(o => o.name.toLowerCase() == targetName); + target = this.obj.instance.objects.objects.find(o => ((o.name) && (o.name.toLowerCase() == targetName))); if (!target) return false; } @@ -95,8 +95,18 @@ define([ if (stateConfig.cpn) { var cpn = sourceObj[stateConfig.cpn]; - cpn[stateConfig.method].apply(cpn, stateConfig.args); - return; + var newArgs = extend(true, [], stateConfig.args); + newArgs.push(this.obj); + var result = cpn[stateConfig.method].apply(cpn, newArgs); + + if (stateConfig.goto) { + if (result) + return this.getState(sourceObj, stateConfig.goto.success); + else + return this.getState(sourceObj, stateConfig.goto.failure); + } + else + return null; } var result = { @@ -158,6 +168,26 @@ define([ return { type: 'dialogue' }; + }, + + //These don't belong here, but I can't figure out where to put them right now + //They are actions that can be performed while chatting with someone + teleport: function(msg) { + this.obj.syncer.set(true, 'dialogue', 'state', null); + + var portal = extend(true, {}, require('./components/portal'), msg); + portal.collisionEnter(this.obj); + }, + + getItem: function(msg, source) { + var inventory = this.obj.inventory; + var exists = inventory.items.find(i => (i.name == msg.item.name)); + if (!exists) { + inventory.getItem(msg.item); + return true; + } + else + return false; } }; }); \ No newline at end of file diff --git a/src/server/components/inventory.js b/src/server/components/inventory.js index 42ccdc64..04cd1ff2 100644 --- a/src/server/components/inventory.js +++ b/src/server/components/inventory.js @@ -153,9 +153,10 @@ define([ var stash = this.obj.stash; if (!stash.active) return; - + + var clonedItem = extend(true, {}, item); this.destroyItem(id); - stash.deposit(item); + stash.deposit(clonedItem); }, salvageItem: function(id) { @@ -376,6 +377,9 @@ define([ if (this.obj.player) delete item.fromMob; + //Store the quantity to send to the player + var quantity = item.quantity; + //Material? var exists = false; if ((item.material) || (item.quest)) { @@ -437,8 +441,8 @@ define([ var messages = []; var msg = item.name; - if (item.quantity) - msg += ' x' + item.quantity; + if (quantity) + msg += ' x' + quantity; messages.push({ class: 'q' + item.quality, message: 'loot (' + msg + ')', diff --git a/src/server/components/quests.js b/src/server/components/quests.js index e6f397da..6641815d 100644 --- a/src/server/components/quests.js +++ b/src/server/components/quests.js @@ -30,7 +30,12 @@ define([ quest.active = (this.obj.zoneName == quest.zoneName); this.quests.push(quest); - quest.init(hideMessage); + if (!quest.init(hideMessage)) { + this.quests.spliceWhere(q => (q == quest)); + return false; + } + else + return true; }, complete: function(id) { diff --git a/src/server/components/reputation.js b/src/server/components/reputation.js index 3b24c072..13355690 100644 --- a/src/server/components/reputation.js +++ b/src/server/components/reputation.js @@ -81,6 +81,8 @@ define([ if (t.rep > rep) break; + else if (i == tLen - 1) + tier = i; } if (tier < 0) diff --git a/src/server/components/social.js b/src/server/components/social.js index d7b5c2fc..0b84ed3a 100644 --- a/src/server/components/social.js +++ b/src/server/components/social.js @@ -130,6 +130,7 @@ define([ messages: [{ class: msgStyle, message: prefix + charname + ': ' + msg.data.message, + item: msg.data.item, type: 'chat' }] } @@ -181,7 +182,10 @@ define([ this.updatePartyOnThread(); } - this.party.push(sourceId); + // Only add if not yet in party + if (!this.party.find(id => (id === sourceId))) + this.party.push(sourceId); + this.updatePartyOnThread(); this.party.forEach(function(p) { diff --git a/src/server/components/stats.js b/src/server/components/stats.js index 159c1438..1d18a3e4 100644 --- a/src/server/components/stats.js +++ b/src/server/components/stats.js @@ -205,7 +205,6 @@ define([ kill: function(target) { var level = target.stats.values.level; - var inc = level * 10; //Who should get xp? var aggroList = target.aggro.list; @@ -217,21 +216,30 @@ define([ if (dmg <= 0) continue; - var get = inc; + 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 mult = aggroList.filter(function(f) { + var partySize = aggroList.filter(function(f) { return ((a.damage > 0) && (party.indexOf(f.obj.serverId) > -1)); }).length; - mult--; - get *= (1 + (mult * 0.1)); - get = ~~get; + partySize--; + mult = (1 + (partySize * 0.1)); } - if (a.obj.stats) - a.obj.stats.getXp(inc); + 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); diff --git a/src/server/config/factions/gaekatla.js b/src/server/config/factions/gaekatla.js index 72199f48..4a38fd7e 100644 --- a/src/server/config/factions/gaekatla.js +++ b/src/server/config/factions/gaekatla.js @@ -52,9 +52,9 @@ define([ afterKillMob: function(item, mob) { var effect = item.effects.find(e => (e.factionId == 'gaekatla')); - //var roll = Math.random() * 100; - //if (roll >= this.chance) - // return; + var roll = Math.random() * 100; + if (roll >= this.chance) + return; //Spawn a mob var mob = mob.instance.spawners.spawn({ diff --git a/src/server/config/maps/city/zone.js b/src/server/config/maps/city/zone.js index a0c4aad2..fdc5d64f 100644 --- a/src/server/config/maps/city/zone.js +++ b/src/server/config/maps/city/zone.js @@ -111,7 +111,7 @@ module.exports = { }, faction: { id: 'gaekatla', - tier: 1 + tier: 6 }, markup: { buy: 0.25, diff --git a/src/server/config/quests/questBuilder.js b/src/server/config/quests/questBuilder.js index 8e3d80c0..7e0a7007 100644 --- a/src/server/config/quests/questBuilder.js +++ b/src/server/config/quests/questBuilder.js @@ -61,7 +61,8 @@ define([ quest.obj = obj; quest.zoneName = zoneName; - oQuests.obtain(quest, !!template); + if (!oQuests.obtain(quest, !!template)) + this.obtain(obj, template); } }; }); \ No newline at end of file diff --git a/src/server/config/quests/templates/questGatherResource.js b/src/server/config/quests/templates/questGatherResource.js index 44f963c6..d0ccd21b 100644 --- a/src/server/config/quests/templates/questGatherResource.js +++ b/src/server/config/quests/templates/questGatherResource.js @@ -15,6 +15,8 @@ define([ } this.description = 'Gather ' + this.have + '/' + this.need + ' herbs'; + + return true; }, events: { diff --git a/src/server/config/quests/templates/questKillX.js b/src/server/config/quests/templates/questKillX.js index 9503822c..619a39f2 100644 --- a/src/server/config/quests/templates/questKillX.js +++ b/src/server/config/quests/templates/questKillX.js @@ -26,14 +26,22 @@ define([ if (!this.mobName) { var mobCounts = this.obj.instance.spawners.mobTypes; var keys = Object.keys(mobTypes).filter(function(m) { + var mobBlueprint = mobTypes[m]; + return ( (m != 'default') && ( - (mobTypes[m].attackable) || - (mobTypes[m].attackable == null) - ) + (mobBlueprint.attackable) || + (mobBlueprint.attackable == null) + ) && + (mobBlueprint.level <= ~~(this.obj.stats.values.level * 1.35)) ); - }); + }, this); + + //No level appropriate mobs found + if (keys.length == 0) + return false; + this.mobType = keys[~~(Math.random() * keys.length)]; var needMax = 8; this.mobName = this.mobType.replace(/\w\S*/g, function(txt) { @@ -45,6 +53,8 @@ define([ } this.description = 'Kill ' + this.have + '/' + this.need + ' ' + this.mobName; + + return true; }, events: { diff --git a/src/server/config/quests/templates/questLoot.js b/src/server/config/quests/templates/questLoot.js index d8a01b92..6b405bf1 100644 --- a/src/server/config/quests/templates/questLoot.js +++ b/src/server/config/quests/templates/questLoot.js @@ -17,6 +17,8 @@ define([ } this.description = 'Loot 1x ' + this.slotName + ' slot item'; + + return true; }, events: { diff --git a/src/server/config/quests/templates/questLootGen.js b/src/server/config/quests/templates/questLootGen.js index 3badb2a8..bd3893ff 100644 --- a/src/server/config/quests/templates/questLootGen.js +++ b/src/server/config/quests/templates/questLootGen.js @@ -18,8 +18,19 @@ define([ var mobTypes = this.obj.instance.spawners.zone.mobs; var mobCounts = this.obj.instance.spawners.mobTypes; var keys = Object.keys(mobTypes).filter(function(m) { - return ((m != 'default') && (mobTypes[m].questItem)); - }); + var mobBlueprint = mobTypes[m]; + + return ( + (m != 'default') && + (mobBlueprint.questItem) && + (mobBlueprint.level <= (this.obj.stats.values.level * 1.35)) + ); + }, this); + + //No level appropriate mobs found + if (keys.length == 0) + return false; + this.mobType = keys[~~(Math.random() * keys.length)]; var needMax = 8; this.mobName = this.mobType.replace(/\w\S*/g, function(txt) { @@ -33,6 +44,8 @@ define([ this.name = this.item.name + ' Gatherer'; this.description = 'Loot ' + this.have + '/' + this.need + ' ' + this.item.name + ' from ' + this.mobName; + + return true; }, oComplete: function() { diff --git a/src/server/config/quests/templates/questTemplate.js b/src/server/config/quests/templates/questTemplate.js index be294ed2..8018832f 100644 --- a/src/server/config/quests/templates/questTemplate.js +++ b/src/server/config/quests/templates/questTemplate.js @@ -5,7 +5,9 @@ define([ ) { return { init: function(hideMessage) { - this.build(); + if (!this.build()) + return false; + this.obj.syncer.setArray(true, 'quests', 'obtainQuests', this.simplify(true)); if (!hideMessage) { @@ -17,6 +19,8 @@ define([ }] }, [this.obj.serverId]); } + + return true; }, ready: function() { diff --git a/src/server/config/spells/spellHealingCircle.js b/src/server/config/spells/spellHealingCircle.js index ff2223ce..cb435fff 100644 --- a/src/server/config/spells/spellHealingCircle.js +++ b/src/server/config/spells/spellHealingCircle.js @@ -67,6 +67,7 @@ define([ duration: 70, targetGround: true, + needLos: true, cast: function(action) { var obj = this.obj; diff --git a/src/server/config/spells/spellWarnBlast.js b/src/server/config/spells/spellWarnBlast.js index fcadfc5c..e3827204 100644 --- a/src/server/config/spells/spellWarnBlast.js +++ b/src/server/config/spells/spellWarnBlast.js @@ -16,6 +16,8 @@ define([ statMult: 1, targetGround: true, + needLos: true, + damage: 10, delay: 10, diff --git a/src/server/items/enchanter.js b/src/server/items/enchanter.js index 1a84f957..60ca3b7d 100644 --- a/src/server/items/enchanter.js +++ b/src/server/items/enchanter.js @@ -45,7 +45,10 @@ define([ var value = item.enchantedStats[p]; if (item.stats[p]) { - result.addStatMsgs.push('-' + value + ' ' + p); + result.addStatMsgs.push({ + stat: p, + value: -value + }); item.stats[p] -= value; if (item.stats[p] <= 0) delete item.stats[p]; diff --git a/src/server/items/generators/stats.js b/src/server/items/generators/stats.js index be7f8d6c..23356613 100644 --- a/src/server/items/generators/stats.js +++ b/src/server/items/generators/stats.js @@ -245,8 +245,12 @@ define([ value = Math.ceil(random.norm(statBlueprint.min, statBlueprint.max)); if (blueprint.statCount) { - if (result) - result.addStatMsgs.push('+' + value + ' ' + stat); + if (result) { + result.addStatMsgs.push({ + stat: stat, + value: value + }); + } if (!item.enchantedStats) item.enchantedStats = {}; diff --git a/src/server/items/salvager.js b/src/server/items/salvager.js index 73209d55..fbd28ed4 100644 --- a/src/server/items/salvager.js +++ b/src/server/items/salvager.js @@ -120,7 +120,7 @@ define([ if (m.qualityMult) max *= (m.qualityMult * (item.quality + 1)); - var quantity = Math.ceil(random.norm(0, 1) * max); + var quantity = Math.ceil(random.norm(0, 1) * max) || 1; if (maxRoll) quantity = Math.ceil(max); diff --git a/src/server/misc/messages.js b/src/server/misc/messages.js index 93c6e182..448da9c8 100644 --- a/src/server/misc/messages.js +++ b/src/server/misc/messages.js @@ -5,6 +5,7 @@ define([ ) { return { login: { + invalid: 'invalid name chosen', exists: 'username exists, please try another', allFields: 'please complete all fields', illegal: 'illegal characters in username/password', diff --git a/src/server/misc/profanities.js b/src/server/misc/profanities.js new file mode 100644 index 00000000..3989b134 --- /dev/null +++ b/src/server/misc/profanities.js @@ -0,0 +1,233 @@ +define([ + +], function( + +) { + var config = [ + 'ahole', + 'anal', + 'anilingus', + 'anus', + 'areola', + 'ass', + 'b1tch', + 'ballsack', + 'bimbo', + 'bitch', + 'blowjob', + 'bollock', + 'boner', + 'boob', + 'breast', + 'breasts', + 'bukkake', + 'bullshit', + 'busty', + 'butt', + 'cameltoe', + 'carpetmuncher', + 'chinc', + 'chink', + 'chode', + 'climax', + 'clit', + 'cock', + 'coital', + 'condom', + 'coon', + 'crap', + 'cum', + 'cunilingus', + 'cunnilingus', + 'cunt', + 'dammit', + 'damn', + 'dick', + 'dike', + 'dildo', + 'dong', + 'douche', + 'dumbass', + 'dumbasses', + 'dyke', + 'ejaculate', + 'erection', + 'erotic', + 'fack', + 'fag', + 'fart', + 'felch', + 'fellate', + 'fellatio', + 'feltch', + 'fisting', + 'fondle', + 'foreskin', + 'fubar', + 'fuck', + 'fuk', + 'gay', + 'goatse', + 'godamn', + 'goddammit', + 'goddamn', + 'goldenshower', + 'gonad', + 'gspot', + 'gtfo', + 'handjob', + 'hardon', + 'hell', + 'herpes', + 'hitler', + 'hiv', + 'homo', + 'hooker', + 'hooter', + 'horny', + 'hump', + 'hymen', + 'incest', + 'jap', + 'jerkoff', + 'jism', + 'jiz', + 'kinky', + 'kkk', + 'labia', + 'lech', + 'lesbian', + 'lesbo', + 'lezbian', + 'lezbo', + 'lube', + 'masterbat', + 'masturbat', + 'menstruat', + 'muff', + 'nad', + 'naked', + 'nazi', + 'negro', + 'nigga', + 'nigger', + 'nipple', + 'nympho', + 'oral', + 'orgasm', + 'orgies', + 'orgy', + 'pantie', + 'panty', + 'pedo', + 'pee', + 'penetrat', + 'penial', + 'penile', + 'penis', + 'phalli', + 'phuck', + 'pimp', + 'piss', + 'pms', + 'poon', + 'porn', + 'prick', + 'prostitut', + 'pube', + 'pubic', + 'pubis', + 'puss', + 'pussies', + 'pussy', + 'puto', + 'queaf', + 'queef', + 'queer', + 'rape', + 'rapist', + 'rectal', + 'rectum', + 'rectus', + 'reich', + 'retard', + 'rimjob', + 'ritard', + 'rump', + 'schlong', + 'screw', + 'scrote', + 'scrotum', + 'semen', + 'sex', + 'shit', + 'skank', + 'slut', + 'smut', + 'snatch', + 'sodom', + 'sperm', + 'spunk', + 'stfu', + 'stiffy', + 'strip', + 'stroke', + 'stupid', + 'suck', + 'tampon', + 'tard', + 'teabag', + 'teat', + 'teste', + 'testicle', + 'testis', + 'thrust', + 'tit', + 'tramp', + 'transsex', + 'turd', + 'tush', + 'twat', + 'undies', + 'urinal', + 'urine', + 'uterus', + 'vag', + 'vagina', + 'viagra', + 'virgin', + 'vomit', + 'voyeur', + 'vulva', + 'wang', + 'wank', + 'weenie', + 'weewee', + 'weiner', + 'wench', + 'wetback', + 'whoralicious', + 'whore', + 'whoring', + 'wigger', + 'womb', + 'woody', + 'wtf', + 'xxx' + ]; + + var cLen = config.length; + + return { + isClean: function(text) { + var cb = text.indexOf.bind(text); + + for (var i = 0; i < cLen; i++) { + if (cb(config[i]) > -1) + return false; + } + + return true; + } + }; +}); \ No newline at end of file diff --git a/src/server/world/physics.js b/src/server/world/physics.js index 3fdf5c79..bd6ba7e3 100644 --- a/src/server/world/physics.js +++ b/src/server/world/physics.js @@ -203,7 +203,6 @@ define([ var cells = this.cells; var grid = this.graph.grid; - var result = []; for (var i = x1; i <= x2; i++) { var row = cells[i]; var gridRow = grid[i]; @@ -217,11 +216,20 @@ define([ x: i, y: j }; + } else { + //If the only contents are notices, we can still use it + var allNotices = !cell.some(c => !c.notice); + if (allNotices) { + return { + x: i, + y: j + }; + } } } } - return result; + return null; }, getPath: function(from, to) { @@ -270,7 +278,7 @@ define([ if (node) return node.isWall(); else - return false; + return true; }, isCellOpen: function(x, y) { if ((x < 0) || (y < 0) || (x >= this.width) | (y >= this.height))