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 @@
\ 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))