@@ -245,4 +245,87 @@ body { | |||
} | |||
.color-tealB { | |||
color: @tealB !important;} | |||
color: @tealB !important; | |||
} | |||
[class^="ui"] .renderItem { | |||
width: 72px; | |||
height: 72px; | |||
float: left; | |||
position: relative; | |||
cursor: pointer; | |||
box-sizing: border-box; | |||
margin: 4px; | |||
background-color: @blackD; | |||
&.hover { | |||
background-color: fade(@blueA, 10%); | |||
} | |||
&.dragging { | |||
position: absolute; | |||
opacity: 0.5; | |||
pointer-events: none; | |||
background-color: transparent; | |||
.icon { | |||
filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
-moz-filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
} | |||
} | |||
.quantity { | |||
left: 6px; | |||
bottom: 3px; | |||
position: absolute; | |||
color: @white; | |||
filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
} | |||
.icon { | |||
width: 64px; | |||
height: 64px; | |||
position: absolute; | |||
left: 4px; | |||
top: 4px; | |||
} | |||
&.eq { | |||
.quantity { | |||
color: @yellow; | |||
} | |||
} | |||
&.new { | |||
.quantity { | |||
color: @green; | |||
} | |||
} | |||
&:hover { | |||
.icon { | |||
filter: brightness(160%); | |||
-moz-filter: brightness(160%); | |||
} | |||
} | |||
} |
@@ -32,7 +32,6 @@ define([ | |||
'events', | |||
'progressBar', | |||
'stash', | |||
'smithing', | |||
'talk', | |||
'trade', | |||
'overlay', | |||
@@ -151,7 +150,6 @@ define([ | |||
'passives', | |||
'quests', | |||
'reputation', | |||
'smithing', | |||
'stash' | |||
].map(m => 'ui/templates/' + m + '/' + m), this.afterPreload.bind(this)); | |||
}, | |||
@@ -1,5 +1,5 @@ | |||
const tplItem = ` | |||
<div class="item"> | |||
<div class="renderItem item"> | |||
<div class="icon"></div> | |||
<div class="quantity"></div> | |||
</div> | |||
@@ -155,31 +155,18 @@ define([ | |||
}); | |||
}, | |||
openAugmentUi: function (item) { | |||
events.emit('onSetSmithItem', { | |||
item: item | |||
}); | |||
}, | |||
showContext: function (item, e) { | |||
let menuItems = { | |||
unequip: { | |||
text: 'unequip', | |||
callback: this.unequipItem.bind(this, item) | |||
}, | |||
augment: { | |||
text: 'craft', | |||
callback: this.openAugmentUi.bind(this, item) | |||
} | |||
} | |||
}; | |||
let config = []; | |||
config.push(menuItems.unequip); | |||
if (item.slot) | |||
config.push(menuItems.augment); | |||
events.emit('onContextMenu', config, e); | |||
e.preventDefault(); | |||
@@ -293,10 +293,6 @@ define([ | |||
text: 'equip', | |||
callback: this.performItemAction.bind(this, item, 'equip') | |||
}, | |||
augment: { | |||
text: 'craft', | |||
callback: this.openAugmentUi.bind(this, item) | |||
}, | |||
mail: { | |||
text: 'mail', | |||
callback: this.openMailUi.bind(this, item) | |||
@@ -336,11 +332,6 @@ define([ | |||
ctxConfig.push(menuItems.equip); | |||
if (!item.eq) | |||
ctxConfig.push(menuItems.divider); | |||
if (!item.eq) { | |||
ctxConfig.push(menuItems.augment); | |||
ctxConfig.push(menuItems.divider); | |||
} | |||
} | |||
if ((!item.eq) && (!item.active)) { | |||
@@ -354,9 +345,6 @@ define([ | |||
if ((!item.material) && (!item.noSalvage)) | |||
ctxConfig.push(menuItems.salvage); | |||
} | |||
if (!item.noDestroy) | |||
ctxConfig.push(menuItems.destroy); | |||
} | |||
if (item.quantity > 1 && !item.quest) | |||
@@ -367,6 +355,11 @@ define([ | |||
ctxConfig.push(menuItems.link); | |||
if (!item.eq && !item.active && !item.noDestroy) { | |||
ctxConfig.push(menuItems.divider); | |||
ctxConfig.push(menuItems.destroy); | |||
} | |||
if (isMobile) | |||
this.hideTooltip(null, this.hoverItem); | |||
@@ -568,12 +561,6 @@ define([ | |||
}); | |||
}, | |||
openAugmentUi: function (item) { | |||
events.emit('onSetSmithItem', { | |||
item: item | |||
}); | |||
}, | |||
openMailUi: function (item) { | |||
events.emit('onSetMailItem', { | |||
item: item | |||
@@ -1,343 +0,0 @@ | |||
define([ | |||
'js/system/events', | |||
'js/system/client', | |||
'html!ui/templates/smithing/template', | |||
'css!ui/templates/smithing/styles', | |||
'html!/ui/templates/smithing/templateItem', | |||
'js/misc/statTranslations' | |||
], function ( | |||
events, | |||
client, | |||
template, | |||
styles, | |||
templateItem, | |||
statTranslations | |||
) { | |||
return { | |||
tpl: template, | |||
centered: true, | |||
modal: true, | |||
hasClose: true, | |||
eventCloseInv: null, | |||
eventClickInv: null, | |||
hoverItem: null, | |||
item: null, | |||
waiting: false, | |||
action: 'augment', | |||
postRender: function () { | |||
this.onEvent('onShowSmithing', this.toggle.bind(this)); | |||
this.onEvent('onKeyDown', this.onKey.bind(this, true)); | |||
this.onEvent('onKeyUp', this.onKey.bind(this, false)); | |||
this.find('.item-picker').on('click', this.openInventory.bind(this)); | |||
this.find('.actionButton').on('click', this.smith.bind(this)); | |||
//If we don't listen to these events, they'll be queued | |||
this.onEvent('onHideInventory', () => {}); | |||
this.onEvent('beforeInventoryClickItem', () => {}); | |||
this.onEvent('onGetItems', this.onGetItems.bind(this)); | |||
this.onEvent('onSetSmithItem', this.onHideInventory.bind(this)); | |||
this.find('.col-btn').on('click', this.clickAction.bind(this)); | |||
}, | |||
clickAction: function (e) { | |||
let el = $(e.target); | |||
this.find('.col-btn').removeClass('selected'); | |||
let action = el.attr('action'); | |||
let changed = (action !== this.action); | |||
this.action = action; | |||
el.addClass('selected'); | |||
if (this.item && changed) | |||
this.getMaterials(this.item); | |||
}, | |||
smith: function () { | |||
this.setDisabled(true); | |||
client.request({ | |||
cpn: 'player', | |||
method: 'performAction', | |||
data: { | |||
cpn: 'inventory', | |||
method: 'enchantItem', | |||
data: { | |||
itemId: this.item.id, | |||
action: this.action | |||
} | |||
}, | |||
callback: this.onSmith.bind(this, this.item) | |||
}); | |||
}, | |||
onSmith: function (item, result) { | |||
this.setDisabled(false); | |||
let msg = { | |||
msg: 'Item Enhancement Succeeded', | |||
type: 'success', | |||
zIndex: 9999999, | |||
top: 100 | |||
}; | |||
if (this.action === 'reroll') | |||
msg.msg = 'Item Reroll Succeeded'; | |||
else if (this.action === 'relevel') | |||
msg.msg = 'Item Relevel Succeeded'; | |||
else if (this.action === 'reslot') | |||
msg.msg = 'Item Reslot Succeeded'; | |||
result.addStatMsgs.forEach(a => { | |||
let statName = statTranslations.translate(a.stat); | |||
msg.msg += `<br />${(a.value > 0) ? '+' : ''}${a.value} ${statName}`; | |||
}); | |||
events.emit('onGetAnnouncement', msg); | |||
if (result.item) | |||
this.item = result.item; | |||
this.getMaterials(this.item); | |||
let augment = this.find('[action="augment"]').addClass('disabled'); | |||
if ((result.item.power || 0) < 3) | |||
augment.removeClass('disabled'); | |||
else if (this.action === 'augment') | |||
this.find('[action="reroll"]').click(); | |||
}, | |||
openInventory: function () { | |||
this.waiting = true; | |||
this.eventCloseInv = this.onEvent('onHideInventory', this.onHideInventory.bind(this)); | |||
this.eventClickInv = this.onEvent('beforeInventoryClickItem', this.onHideInventory.bind(this)); | |||
events.emit('onShowInventory'); | |||
}, | |||
onHideInventory: function (msg) { | |||
if (msg) | |||
msg.success = false; | |||
this.waiting = false; | |||
if (!msg || !msg.item) { | |||
this.offEvent(this.eventCloseInv); | |||
this.offEvent(this.eventClickInv); | |||
return; | |||
} else if (!msg.item.slot || msg.item.noAugment) { | |||
let resultMsg = { | |||
msg: 'Incorrect Item Type', | |||
type: 'failure', | |||
zIndex: 9999999, | |||
top: 180 | |||
}; | |||
events.emit('onGetAnnouncement', resultMsg); | |||
return; | |||
} | |||
this.find('.selected').removeClass('selected'); | |||
this.find('[action="augment"]').addClass('selected'); | |||
this.action = 'augment'; | |||
let augment = this.find('[action="augment"]').addClass('disabled'); | |||
if ((msg.item.power || 0) < 3) | |||
augment.removeClass('disabled'); | |||
let reforge = this.find('[action="reforge"]').addClass('disabled'); | |||
if (msg.item.spell) | |||
reforge.removeClass('disabled'); | |||
let reslot = this.find('[action="reslot"]').addClass('disabled'); | |||
if (!msg.item.effects && msg.item.slot !== 'tool') | |||
reslot.removeClass('disabled'); | |||
let relevel = this.find('[action="relevel"]').addClass('disabled'); | |||
if (msg.item.slot !== 'tool') | |||
relevel.removeClass('disabled'); | |||
this.offEvent(this.eventClickInv); | |||
$('.uiInventory').data('ui').hide(); | |||
this.show(); | |||
this.el.show(); | |||
this.shown = true; | |||
msg.success = false; | |||
if (!msg || !msg.item || !msg.item.slot) | |||
return; | |||
this.item = msg.item; | |||
this.getMaterials(msg.item); | |||
}, | |||
getMaterials: function (item) { | |||
this.setDisabled(true); | |||
client.request({ | |||
cpn: 'player', | |||
method: 'performAction', | |||
data: { | |||
cpn: 'inventory', | |||
method: 'getEnchantMaterials', | |||
data: { | |||
itemId: item.id, | |||
action: this.action | |||
} | |||
}, | |||
callback: this.onGetMaterials.bind(this, item) | |||
}); | |||
}, | |||
onGetMaterials: function (item, result) { | |||
this.find('.item').remove(); | |||
this.drawItem(this.find('.item-picker'), item); | |||
this.find('.actionButton').removeClass('disabled').addClass('disabled'); | |||
if (result.materials) { | |||
let material = result.materials[0]; | |||
if (material) { | |||
let hasMaterials = window.player.inventory.items.find(i => i.name === material.name); | |||
if (hasMaterials) { | |||
material.quantityText = hasMaterials.quantity + '/' + material.quantity; | |||
hasMaterials = hasMaterials.quantity >= material.quantity; | |||
} else { | |||
if (!material.quantityText) | |||
material.quantityText = ''; | |||
material.quantityText += '0/' + material.quantity; | |||
} | |||
if (hasMaterials) | |||
this.find('.actionButton').removeClass('disabled'); | |||
this.drawItem(this.find('.material'), material, !hasMaterials); | |||
} | |||
} | |||
this.setDisabled(false); | |||
}, | |||
onGetItems: function (items) { | |||
let elMaterial = this.find('.material .item'); | |||
if (!elMaterial.length) | |||
return; | |||
let itemMaterial = elMaterial.data('item'); | |||
let elQuantity = elMaterial.find('.quantity'); | |||
let invMaterial = items.find(i => i.name === itemMaterial.name) || { quantity: 0 }; | |||
let currentText = elQuantity.html().split('/'); | |||
let newText = invMaterial.quantity + '/' + currentText[1]; | |||
elQuantity.html(newText); | |||
let elButton = this.find('.actionButton').removeClass('disabled'); | |||
if (invMaterial.quantity < currentText[1]) { | |||
elButton.addClass('disabled'); | |||
elQuantity.addClass('red'); | |||
} else { | |||
elButton.removeClass('disabled'); | |||
elQuantity.removeClass('red'); | |||
} | |||
}, | |||
drawItem: function (container, item, redQuantity) { | |||
container.find('.icon').hide(); | |||
let imgX = -item.sprite[0] * 64; | |||
let imgY = -item.sprite[1] * 64; | |||
let spritesheet = item.spritesheet || '../../../images/items.png'; | |||
if (item.material) | |||
spritesheet = '../../../images/materials.png'; | |||
else if (item.quest) | |||
spritesheet = '../../../images/questItems.png'; | |||
else if (item.type === 'consumable') | |||
spritesheet = '../../../images/consumables.png'; | |||
let el = $(templateItem) | |||
.appendTo(container); | |||
el | |||
.data('item', item) | |||
.on('mousemove', this.onHover.bind(this, el, item)) | |||
.on('mouseleave', this.hideTooltip.bind(this, el, item)) | |||
.find('.icon') | |||
.css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px'); | |||
if (item.quantity) { | |||
let quantityText = item.quantityText; | |||
el.find('.quantity').html(quantityText); | |||
if (redQuantity) | |||
el.find('.quantity').addClass('red'); | |||
} | |||
}, | |||
onHover: function (el, item, e) { | |||
if (item) | |||
this.hoverItem = item; | |||
else | |||
item = this.hoverItem; | |||
let ttPos = null; | |||
if (el) { | |||
ttPos = { | |||
x: ~~(e.clientX + 32), | |||
y: ~~(e.clientY) | |||
}; | |||
} | |||
events.emit('onShowItemTooltip', item, ttPos, true); | |||
}, | |||
hideTooltip: function (el, item, e) { | |||
events.emit('onHideItemTooltip', this.hoverItem); | |||
this.hoverItem = null; | |||
}, | |||
beforeHide: function () { | |||
if (this.waiting) | |||
return; | |||
this.item = null; | |||
this.offEvent(this.eventCloseInv); | |||
this.offEvent(this.eventClickInv); | |||
}, | |||
toggle: function () { | |||
this.shown = !this.el.is(':visible'); | |||
if (this.shown) { | |||
this.find('.item').remove(); | |||
this.find('.icon').show(); | |||
this.find('.actionButton').removeClass('disabled').addClass('disabled'); | |||
this.show(); | |||
} else | |||
this.hide(); | |||
}, | |||
onKey: function (isDown, key) { | |||
if (isDown && key === 'm') | |||
this.toggle(); | |||
else if (key === 'shift' && this.hoverItem) | |||
this.onHover(); | |||
} | |||
}; | |||
}); |
@@ -1,186 +0,0 @@ | |||
@import "../../../css/colors.less"; | |||
.uiSmithing { | |||
display: none; | |||
background-color: @blackB; | |||
border: 5px solid @blackB; | |||
text-align: center; | |||
> .heading { | |||
color: @blueA; | |||
width: 100%; | |||
height: 36px; | |||
background-color: @blackB; | |||
.heading-text { | |||
padding-top: 8px; | |||
margin: auto; | |||
} | |||
} | |||
.bottom { | |||
height: 170px; | |||
background-color: @blackC; | |||
padding: 10px; | |||
} | |||
.col { | |||
float: left; | |||
margin-right: 10px; | |||
&:last-child { | |||
margin-right: 0px; | |||
} | |||
.heading { | |||
width: 80px; | |||
height: 16px; | |||
color: @white; | |||
text-align: center; | |||
margin-bottom: 10px; | |||
} | |||
.content { | |||
width: 80px; | |||
height: 80px; | |||
background-color: @blackD; | |||
&.chance { | |||
padding-top: 32px; | |||
color: @white; | |||
text-align: center; | |||
} | |||
&.item-picker, | |||
&.actionButton { | |||
cursor: pointer; | |||
&:hover { | |||
background-color: @blackC; | |||
} | |||
} | |||
&.item-picker { | |||
> .icon { | |||
margin: 8px; | |||
display: inline-block; | |||
width: 64px; | |||
height: 64px; | |||
background: url('../../../images/uiIcons.png') -256px -64px; | |||
} | |||
} | |||
&.actionButton { | |||
padding: 8px; | |||
.icon { | |||
width: 64px; | |||
height: 64px; | |||
background: url('../../../images/uiIcons.png') -192px -64px; | |||
} | |||
} | |||
.item { | |||
width: 100%; | |||
height: 100%; | |||
float: left; | |||
position: relative; | |||
cursor: pointer; | |||
box-sizing: border-box; | |||
.quantity { | |||
left: 6px; | |||
bottom: 3px; | |||
position: absolute; | |||
color: @white; | |||
filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
-moz-filter: drop-shadow(0px -2px 0px @blackD) | |||
drop-shadow(0px 2px 0px @blackD) | |||
drop-shadow(2px 0px 0px @blackD) | |||
drop-shadow(-2px 0px 0px @blackD); | |||
&.red { | |||
color: @red; | |||
} | |||
} | |||
.icon { | |||
width: 64px; | |||
height: 64px; | |||
position: absolute; | |||
left: 8px; | |||
top: 8px; | |||
filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
-moz-filter: brightness(100%) | |||
drop-shadow(0px -4px 0px @blackD) | |||
drop-shadow(0px 4px 0px @blackD) | |||
drop-shadow(4px 0px 0px @blackD) | |||
drop-shadow(-4px 0px 0px @blackD); | |||
} | |||
} | |||
} | |||
&:first-child { | |||
.heading { | |||
width: 190px; | |||
} | |||
.content { | |||
width: 190px; | |||
background-color: transparent; | |||
.col-btn { | |||
height: calc((100% - 10px) / 2); | |||
width: 100%; | |||
color: #f2f5f5; | |||
text-align: center; | |||
padding-top: 10px; | |||
background-color: @blackB; | |||
margin-bottom: 10px; | |||
cursor: pointer; | |||
&.selected { | |||
color: @orangeA; | |||
} | |||
&:hover { | |||
background-color: @blackA; | |||
} | |||
&.col-half { | |||
width: calc((100% - 10px) / 2); | |||
float: left; | |||
&:nth-child(2n + 1) { | |||
margin-right: 10px; | |||
} | |||
} | |||
&:not(.col-half) { | |||
clear: both; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,34 +0,0 @@ | |||
<div class="uiSmithing"> | |||
<div class="heading"> | |||
<div class="heading-text">craft item</div> | |||
</div> | |||
<div class="bottom"> | |||
<div class="col"> | |||
<div class="heading">action</div> | |||
<div class="content"> | |||
<div class="col-btn col-half selected" action="augment">augment</div> | |||
<div class="col-btn col-half" action="scour">scour</div> | |||
<div class="col-btn col-half" action="reroll">reroll</div> | |||
<div class="col-btn col-half" action="relevel">relevel</div> | |||
<div class="col-btn col-half" action="reslot">reslot</div> | |||
<div class="col-btn col-half" action="reforge">reforge</div> | |||
</div> | |||
</div> | |||
<div class="col"> | |||
<div class="heading">item</div> | |||
<div class="content item-picker"> | |||
<div class="icon"></div> | |||
</div> | |||
</div> | |||
<div class="col"> | |||
<div class="heading">material</div> | |||
<div class="content material"></div> | |||
</div> | |||
<div class="col"> | |||
<div class="heading"></div> | |||
<div class="content actionButton disabled"> | |||
<div class="icon"></div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -1,4 +0,0 @@ | |||
<div class="item"> | |||
<div class="icon"></div> | |||
<div class="quantity"></div> | |||
</div> |
@@ -2,17 +2,20 @@ | |||
.uiWorkbench { | |||
display: none; | |||
width: 720px; | |||
height: 385px; | |||
border: 5px solid fade(@blackB, 90%); | |||
text-align: center; | |||
width: 827px; | |||
height: 447px; | |||
border: 5px solid @blackB; | |||
color: @white; | |||
position: relative; | |||
z-index: 2; | |||
> .heading { | |||
color: @blueA; | |||
> .heading, | |||
> .itemPicker > .heading { | |||
color: @orangeA; | |||
width: 100%; | |||
height: 36px; | |||
background-color: fade(@blackB, 90%); | |||
background-color: @blackB; | |||
text-align: center; | |||
.heading-text { | |||
padding-top: 8px; | |||
@@ -21,14 +24,15 @@ | |||
} | |||
.bottom { | |||
background-color: fade(@blackC, 90%); | |||
> .bottom { | |||
background-color: @blackC; | |||
height: calc(100% - 36px); | |||
width: 100%; | |||
.heading { | |||
color: @blueB; | |||
margin-bottom: 10px; | |||
text-align: center; | |||
} | |||
.left, | |||
@@ -44,14 +48,18 @@ | |||
.list { | |||
height: calc(100% - 25px); | |||
overflow-y: auto; | |||
display: flex; | |||
flex-direction: column; | |||
.item { | |||
width: 100%; | |||
padding: 5px 0px; | |||
padding: 5px 10px; | |||
cursor: pointer; | |||
color: @grayB; | |||
&.selected { | |||
background-color: @blackB; | |||
color: @white; | |||
} | |||
&:hover { | |||
@@ -66,6 +74,9 @@ | |||
.right { | |||
width: calc(100% - 300px); | |||
display: flex; | |||
flex-direction: column; | |||
justify-content: space-between; | |||
> * { | |||
width: 100%; | |||
@@ -73,17 +84,24 @@ | |||
.info { | |||
height: calc(100% - 100px - 35px); | |||
flex: 1; | |||
.title { | |||
color: @orangeA; | |||
color: @blueB; | |||
padding-bottom: 10px; | |||
text-align: center; | |||
} | |||
.description { | |||
color: @grayB; | |||
text-align: justify; | |||
} | |||
} | |||
.materialList { | |||
height: 100px; | |||
visibility: hidden; | |||
margin-bottom: 20px; | |||
.material { | |||
&.need { | |||
@@ -94,6 +112,24 @@ | |||
} | |||
.needItems { | |||
display: none; | |||
margin-bottom: 20px; | |||
flex-direction: column; | |||
.title { | |||
color: @blueB; | |||
padding-bottom: 10px; | |||
text-align: center; | |||
} | |||
.list { | |||
display: flex; | |||
justify-content: space-around; | |||
} | |||
} | |||
.buttons { | |||
height: 40px; | |||
@@ -123,6 +159,24 @@ | |||
} | |||
> .itemPicker { | |||
display: none; | |||
position: absolute; | |||
left: 0px; | |||
top: 0px; | |||
width: 100%; | |||
height: 100%; | |||
background-color: @blackC; | |||
flex-direction: column; | |||
.list { | |||
display: flex; | |||
flex-wrap: wrap; | |||
overflow-y: auto; | |||
} | |||
} | |||
} | |||
.mobile .uiWorkbench { | |||
@@ -1,6 +1,6 @@ | |||
<div class="uiWorkbench"> | |||
<div class="heading"> | |||
<div class="heading-text">Workbench</div> | |||
<div class="mainHeading heading-text">Workbench</div> | |||
</div> | |||
<div class="bottom"> | |||
<div class="left"> | |||
@@ -15,6 +15,10 @@ | |||
<div class="title"></div> | |||
<div class="description"></div> | |||
</div> | |||
<div class="needItems"> | |||
<div class="title">Pick Items</div> | |||
<div class="list"></div> | |||
</div> | |||
<div class="materialList"> | |||
<div class="heading">Requires:</div> | |||
</div> | |||
@@ -24,4 +28,12 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<div class="itemPicker"> | |||
<div class="heading"> | |||
<div class="heading-text"></div> | |||
</div> | |||
<div class="list"> | |||
</div> | |||
</div> | |||
</div> |
@@ -2,12 +2,16 @@ define([ | |||
'js/system/events', | |||
'js/system/client', | |||
'html!ui/templates/workbench/template', | |||
'css!ui/templates/workbench/styles' | |||
'css!ui/templates/workbench/styles', | |||
'ui/shared/renderItem', | |||
'js/misc/statTranslations' | |||
], function ( | |||
events, | |||
client, | |||
template, | |||
styles | |||
styles, | |||
renderItem, | |||
statTranslations | |||
) { | |||
return { | |||
tpl: template, | |||
@@ -22,9 +26,18 @@ define([ | |||
recipes: null, | |||
currentRecipe: null, | |||
selectedNeedItems: null, | |||
hoverItem: null, | |||
hoverEl: null, | |||
postRender: function () { | |||
this.onEvent('onOpenWorkbench', this.onOpenWorkbench.bind(this)); | |||
this.onEvent('onCloseWorkbench', this.hide.bind(this)); | |||
this.onEvent('onGetItems', this.onGetItems.bind(this)); | |||
this.onEvent('onKeyDown', this.onKeyDown.bind(this)); | |||
this.onEvent('onKeyUp', this.onKeyUp.bind(this)); | |||
this.on('.btnCraft', 'click', this.craft.bind(this)); | |||
this.on('.btnCancel', 'click', this.hide.bind(this)); | |||
@@ -32,17 +45,29 @@ define([ | |||
onOpenWorkbench: function (msg) { | |||
this.workbenchId = msg.workbenchId; | |||
this.find('.heading-text').html(msg.name); | |||
this.find('.mainHeading').html(msg.name); | |||
this.find('.itemPicker').hide(); | |||
this.find('.needItems').hide(); | |||
this.renderRecipes(msg.recipes); | |||
this.show(); | |||
}, | |||
//Redraw items if they change | |||
onGetItems: function (items) { | |||
if (!this.currentRecipe) | |||
return; | |||
const { currentRecipe: { needItems } } = this; | |||
this.buildNeedItemBoxes(needItems, true); | |||
}, | |||
renderRecipes: function (recipes) { | |||
this.recipes = recipes; | |||
let container = this.find('.list').empty(); | |||
let container = this.find('.left .list').empty(); | |||
recipes.forEach(function (r) { | |||
let el = $('<div class="item">' + r + '</div>') | |||
@@ -64,18 +89,20 @@ define([ | |||
cpn: 'workbench', | |||
method: 'getRecipe', | |||
data: { | |||
name: recipeName | |||
name: recipeName | |||
} | |||
}, | |||
callback: this.onGetRecipe.bind(this) | |||
callback: this.onGetRecipe.bind(this, false) | |||
}); | |||
}, | |||
onGetRecipe: function (recipe) { | |||
onGetRecipe: function (persistNeedItems, recipe) { | |||
const { name: recipeName, description, materials, needItems } = recipe; | |||
this.currentRecipe = recipe; | |||
this.find('.title').html(recipe.name); | |||
this.find('.description').html(recipe.description); | |||
this.find('.info .title').html(recipeName); | |||
this.find('.description').html(description); | |||
this.find('.materialList .material').remove(); | |||
@@ -84,13 +111,16 @@ define([ | |||
visibility: 'visible' | |||
}); | |||
let canCraft = true; | |||
let canCraft = !!materials.length; | |||
materials.forEach(m => { | |||
const { needQuantity, nameLike, name: materialName, haveQuantity, noHaveEnough } = m; | |||
recipe.materials.forEach(function (m) { | |||
let el = $('<div class="material">' + m.quantity + 'x ' + (m.nameLike || m.name) + '</div>') | |||
const materialText = `${needQuantity}x ${(nameLike || materialName)} (${haveQuantity})`; | |||
let el = $(`<div class="material">${materialText}</div>`) | |||
.appendTo(container); | |||
if (m.need) { | |||
if (noHaveEnough) { | |||
canCraft = false; | |||
el.addClass('need'); | |||
} | |||
@@ -103,10 +133,138 @@ define([ | |||
this.find('.btnCraft') | |||
.addClass('disabled'); | |||
} | |||
//If there are no materials, the selected items aren't valid | |||
this.find('.materialList').show(); | |||
if (!materials.length) { | |||
this.find('.materialList').hide(); | |||
persistNeedItems = false; | |||
} | |||
this.buildNeedItemBoxes(needItems, persistNeedItems); | |||
}, | |||
buildNeedItemBoxes: function (needItems = [], persistNeedItems) { | |||
if (!persistNeedItems) { | |||
this.selectedNeedItems = new Array(needItems.length); | |||
this.selectedNeedItems.fill(null); | |||
} | |||
const container = this.find('.needItems').hide(); | |||
const list = container.find('.list').empty(); | |||
if (!needItems.length) | |||
return; | |||
container.css({ display: 'flex' }); | |||
needItems.forEach((n, i) => this.buildNeedItemBox(list, n, i)); | |||
}, | |||
buildNeedItemBox: function (container, needItem, needItemIndex) { | |||
const item = this.selectedNeedItems[needItemIndex]; | |||
const el = renderItem(container, item); | |||
el | |||
.on('mousemove', this.toggleTooltip.bind(this, true, el, needItem, item)) | |||
.on('mouseleave', this.toggleTooltip.bind(this, false, el, needItem, item)) | |||
.on('click', this.toggleItemPicker.bind(this, true, needItem, needItemIndex)); | |||
}, | |||
toggleItemPicker: function (show, needItem, needItemIndex) { | |||
const container = this.find('.itemPicker').hide(); | |||
if (!show) | |||
return; | |||
const { allowedItemIds } = needItem; | |||
container | |||
.css({ display: 'flex' }) | |||
.find('.heading-text').html(needItem.info); | |||
const list = container.find('.list').empty(); | |||
const items = window.player.inventory.items | |||
.filter(item => { | |||
const isValidItem = allowedItemIds.find(f => f === item.id); | |||
return isValidItem; | |||
}); | |||
items.forEach(item => { | |||
const el = renderItem(list, item); | |||
el | |||
.on('click', this.onSelectItem.bind(this, item, needItemIndex)) | |||
.on('mousemove', this.toggleTooltip.bind(this, true, el, null, item)) | |||
.on('mouseleave', this.toggleTooltip.bind(this, false, el, null, item)); | |||
}); | |||
}, | |||
onSelectItem: function (item, needItemIndex) { | |||
this.selectedNeedItems[needItemIndex] = item; | |||
const { currentRecipe: { needItems } } = this; | |||
this.buildNeedItemBoxes(needItems, true); | |||
const allItemsSelected = this.selectedNeedItems.every(i => !!i); | |||
if (allItemsSelected && this.currentRecipe.dynamicMaterials) { | |||
const pickedItemIds = this.selectedNeedItems.map(i => i.id); | |||
client.request({ | |||
cpn: 'player', | |||
method: 'performAction', | |||
data: { | |||
targetId: this.workbenchId, | |||
cpn: 'workbench', | |||
method: 'getRecipe', | |||
data: { | |||
name: this.currentRecipe.name, | |||
pickedItemIds | |||
} | |||
}, | |||
callback: this.onGetRecipe.bind(this, true) | |||
}); | |||
} | |||
this.find('.itemPicker').hide(); | |||
}, | |||
toggleTooltip: function (show, el, needItem, item, e) { | |||
if (item) { | |||
this.hoverItem = show ? item : null; | |||
this.hoverEl = show ? el : null; | |||
} | |||
let pos = null; | |||
if (e) { | |||
const { clientX, clientY } = e; | |||
pos = { | |||
x: clientX + 25, | |||
y: clientY | |||
}; | |||
} | |||
if (item) { | |||
if (show) | |||
events.emit('onShowItemTooltip', item, pos, true); | |||
else | |||
events.emit('onHideItemTooltip', item); | |||
return; | |||
} | |||
if (show) | |||
events.emit('onShowTooltip', needItem.info, el[0], pos); | |||
else | |||
events.emit('onHideTooltip', el[0]); | |||
}, | |||
craft: function () { | |||
let selectedRecipe = this.find('.list .item.selected').html(); | |||
const selectedRecipe = this.find('.left .list .item.selected').html(); | |||
const pickedItemIds = this.selectedNeedItems | |||
.map(item => item.id); | |||
client.request({ | |||
cpn: 'player', | |||
@@ -116,15 +274,32 @@ define([ | |||
cpn: 'workbench', | |||
method: 'craft', | |||
data: { | |||
name: selectedRecipe | |||
name: selectedRecipe, | |||
pickedItemIds | |||
} | |||
}, | |||
callback: this.onCraft.bind(this) | |||
}); | |||
}, | |||
onCraft: function (recipe) { | |||
this.onGetRecipe(recipe); | |||
onCraft: function ({ recipe, resultMsg }) { | |||
this.onGetRecipe(true, recipe); | |||
if (resultMsg) { | |||
const { msg: baseMsg, addStatMsgs = [] } = resultMsg; | |||
let msg = baseMsg; | |||
addStatMsgs.forEach(a => { | |||
const statName = statTranslations.translate(a.stat); | |||
msg += `<br />${(a.value > 0) ? '+' : ''}${a.value} ${statName}`; | |||
}); | |||
events.emit('onGetAnnouncement', { | |||
msg, | |||
top: 150 | |||
}); | |||
} | |||
}, | |||
onAfterShow: function () { | |||
@@ -133,7 +308,7 @@ define([ | |||
clear: function () { | |||
this.find('.left .list .selected').removeClass('selected'); | |||
this.find('.title').html(''); | |||
this.find('.info .title').html(''); | |||
this.find('.description').html(''); | |||
this.find('.materialList .material').remove(); | |||
this.find('.materialList') | |||
@@ -141,6 +316,16 @@ define([ | |||
visibility: 'hidden' | |||
}); | |||
this.find('.btnCraft').addClass('disabled'); | |||
}, | |||
onKeyDown: function (key) { | |||
if (key === 'shift' && this.hoverItem) | |||
this.toggleTooltip(true, this.hoverEl, null, this.hoverItem); | |||
}, | |||
onKeyUp: function (key) { | |||
if (key === 'shift' && this.hoverItem) | |||
this.toggleTooltip(true, this.hoverEl, null, this.hoverItem); | |||
} | |||
}; | |||
}); |
@@ -1,6 +1,5 @@ | |||
let generator = require('../items/generator'); | |||
let salvager = require('../items/salvager'); | |||
let enchanter = require('../items/enchanter'); | |||
let classes = require('../config/spirits'); | |||
let mtx = require('../mtx/mtx'); | |||
let factions = require('../config/factions'); | |||
@@ -10,8 +9,6 @@ const events = require('../misc/events'); | |||
const { isItemStackable } = require('./inventory/helpers'); | |||
const transactions = require('../security/transactions'); | |||
const { applyItemStats } = require('./equipment/helpers'); | |||
const getItem = require('./inventory/getItem'); | |||
const dropBag = require('./inventory/dropBag'); | |||
const useItem = require('./inventory/useItem'); | |||
@@ -151,45 +148,6 @@ module.exports = { | |||
} | |||
}, | |||
enchantItem: function (msg) { | |||
const { itemId, action } = msg; | |||
const item = this.findItem(itemId); | |||
if (!item) | |||
return; | |||
const { eq, slot, power, noAugment } = item; | |||
if (!slot || noAugment || (action === 'scour' && !power)) { | |||
this.resolveCallback(msg); | |||
return; | |||
} | |||
const obj = this.obj; | |||
if (eq) { | |||
applyItemStats(obj, item, false); | |||
enchanter.enchant(obj, item, msg); | |||
applyItemStats(obj, item, true); | |||
if (item.slot !== slot) | |||
obj.equipment.unequip(itemId); | |||
else | |||
obj.spellbook.calcDps(); | |||
} else | |||
enchanter.enchant(obj, item, msg); | |||
obj.equipment.unequipAttrRqrGear(); | |||
}, | |||
getEnchantMaterials: function (msg) { | |||
let result = []; | |||
let item = this.findItem(msg.itemId); | |||
if ((item) && (item.slot)) | |||
result = enchanter.getEnchantMaterials(item, msg.action); | |||
this.resolveCallback(msg, result); | |||
}, | |||
learnAbility: function (itemId, runeSlot) { | |||
if (itemId.has('itemId')) { | |||
let msg = itemId; | |||
@@ -1,6 +1,12 @@ | |||
const recipes = require('../config/recipes/recipes'); | |||
const generator = require('../items/generator'); | |||
const { applyItemStats } = require('./equipment/helpers'); | |||
const buildRecipe = require('./workbench/buildRecipe'); | |||
const buildMaterials = require('./workbench/buildMaterials'); | |||
const buildPickedItems = require('./workbench/buildPickedItems'); | |||
module.exports = { | |||
type: 'workbench', | |||
@@ -98,93 +104,98 @@ module.exports = { | |||
}, [obj.serverId]); | |||
}, | |||
buildRecipe: function (crafter, recipeName) { | |||
let recipe = recipes.getRecipe(this.craftType, recipeName); | |||
if (!recipe) | |||
return; | |||
const items = crafter.inventory.items; | |||
let sendRecipe = extend({}, recipe); | |||
(sendRecipe.materials || []).forEach(function (m) { | |||
m.need = !items.some(i => ( | |||
( | |||
i.name === m.name || | |||
i.name.indexOf(m.nameLike) > -1 | |||
) && | |||
( | |||
m.quantity === 1 || | |||
i.quantity >= m.quantity | |||
) | |||
)); | |||
}); | |||
return sendRecipe; | |||
}, | |||
getRecipe: function (msg) { | |||
let obj = this.obj.instance.objects.objects.find(o => o.serverId === msg.sourceId); | |||
if ((!obj) || (!obj.player)) | |||
return; | |||
const sendRecipe = this.buildRecipe(obj, msg.name); | |||
const sendRecipe = buildRecipe(this.craftType, obj, msg); | |||
this.resolveCallback(msg, sendRecipe); | |||
}, | |||
craft: function (msg) { | |||
let obj = this.obj.instance.objects.objects.find(o => o.serverId === msg.sourceId); | |||
if ((!obj) || (!obj.player)) | |||
const { craftType, obj: { instance: { objects: { objects } } } } = this; | |||
const { name: recipeName, sourceId } = msg; | |||
const crafter = objects.find(o => o.serverId === sourceId); | |||
if (!crafter || !crafter.player) | |||
return; | |||
let recipe = recipes.getRecipe(this.craftType, msg.name); | |||
const recipe = recipes.getRecipe(craftType, recipeName); | |||
if (!recipe) | |||
return; | |||
const items = obj.inventory.items; | |||
let canCraft = recipe.materials.every(m => (items.some(i => ( | |||
( | |||
i.name === m.name || | |||
i.name.indexOf(m.nameLike) > -1 | |||
) && | |||
( | |||
m.quantity === 1 || | |||
i.quantity >= m.quantity | |||
) | |||
)))); | |||
const { needItems } = recipe; | |||
const { syncer, inventory, equipment, spellbook } = crafter; | |||
const materials = buildMaterials(crafter, recipe, msg); | |||
const pickedItems = buildPickedItems(crafter, recipe, msg); | |||
const canCraft = ( | |||
!materials.some(m => m.noHaveEnough) && | |||
pickedItems.length === needItems.length && | |||
!pickedItems.some(i => !i) | |||
); | |||
if (!canCraft) | |||
return; | |||
recipe.materials.forEach(m => { | |||
let findItem = obj.inventory.items.find(f => ( | |||
f.name === m.name || | |||
f.name.indexOf(m.nameLike) > -1 | |||
)); | |||
obj.inventory.destroyItem(findItem.id, m.quantity); | |||
}); | |||
let outputItems = recipe.item ? [ recipe.item ] : recipe.items; | |||
outputItems.forEach(itemBpt => { | |||
let item = null; | |||
if (itemBpt.generate) | |||
item = generator.generate(itemBpt); | |||
else | |||
item = extend({}, itemBpt); | |||
if (item.description) | |||
item.description += `<br /><br />(Crafted by ${obj.name})`; | |||
else | |||
item.description = `<br /><br />(Crafted by ${obj.name})`; | |||
const quantity = item.quantity; | |||
if (quantity && quantity.push) | |||
item.quantity = quantity[0] + ~~(Math.random() * (quantity[1] - quantity[0])); | |||
obj.inventory.getItem(item); | |||
materials.forEach(m => inventory.destroyItem(m.id, m.needQuantity)); | |||
let resultMsg = null; | |||
if (recipe.craftAction) { | |||
pickedItems.forEach(p => { | |||
if (p.eq) | |||
applyItemStats(crafter, p, false); | |||
}); | |||
const oldSlots = pickedItems.map(p => p.slot); | |||
resultMsg = recipe.craftAction(crafter, pickedItems); | |||
pickedItems.forEach((p, i) => { | |||
if (!p.eq) | |||
return; | |||
applyItemStats(crafter, p, true); | |||
if (p.slot !== oldSlots[i]) | |||
equipment.unequip(p.id); | |||
spellbook.calcDps(); | |||
pickedItems.forEach(item => syncer.setArray(true, 'inventory', 'getItems', inventory.simplifyItem(item))); | |||
}); | |||
equipment.unequipAttrRqrGear(); | |||
} | |||
if (recipe.item || recipe.items) { | |||
const outputItems = recipe.item ? [ recipe.item ] : recipe.items; | |||
outputItems.forEach(itemBpt => { | |||
let item = null; | |||
if (itemBpt.generate) | |||
item = generator.generate(itemBpt); | |||
else | |||
item = extend({}, itemBpt); | |||
if (item.description) | |||
item.description += `<br /><br />(Crafted by ${crafter.name})`; | |||
else | |||
item.description = `<br /><br />(Crafted by ${crafter.name})`; | |||
const quantity = item.quantity; | |||
if (quantity && quantity.push) | |||
item.quantity = quantity[0] + ~~(Math.random() * (quantity[1] - quantity[0])); | |||
crafter.inventory.getItem(item); | |||
}); | |||
} | |||
this.resolveCallback(msg, { | |||
resultMsg, | |||
recipe: buildRecipe(craftType, crafter, msg) | |||
}); | |||
this.resolveCallback(msg, this.buildRecipe(obj, msg.name)); | |||
}, | |||
resolveCallback: function (msg, result) { | |||
@@ -0,0 +1,57 @@ | |||
const buildPickedItems = require('./buildPickedItems'); | |||
module.exports = (crafter, recipe, msg) => { | |||
const { inventory: { items } } = crafter; | |||
const { materialGenerator, materials, needItems = [] } = recipe; | |||
const { pickedItemIds = [] } = msg; | |||
const pickedItems = buildPickedItems(crafter, recipe, msg); | |||
const allPickedItemsSet = ( | |||
pickedItemIds.length === needItems.length && | |||
!pickedItems.some(i => !i) | |||
); | |||
if (!allPickedItemsSet) | |||
return []; | |||
let useMaterials = materials; | |||
if (materialGenerator) | |||
useMaterials = materialGenerator(crafter, pickedItems); | |||
const result = useMaterials.map(m => { | |||
const { name, nameLike, quantity } = m; | |||
const haveMaterial = items.find(i => ( | |||
i.name === name || | |||
i.name.includes(nameLike) | |||
)); | |||
const noHaveEnough = ( | |||
haveMaterial && | |||
( | |||
haveMaterial.quantity === 1 || | |||
haveMaterial.quantity < quantity | |||
) | |||
); | |||
const id = haveMaterial ? haveMaterial.id : null; | |||
const haveQuantity = haveMaterial ? (haveMaterial.quantity || 1) : 0; | |||
const needQuantity = quantity; | |||
const material = { | |||
id, | |||
name, | |||
nameLike, | |||
haveQuantity, | |||
needQuantity, | |||
noHaveEnough | |||
}; | |||
return material; | |||
}); | |||
return result; | |||
}; |
@@ -0,0 +1,29 @@ | |||
module.exports = ({ inventory: { items } }, { needItems }) => { | |||
if (!needItems) | |||
return null; | |||
const result = needItems.map(n => { | |||
const { info, withProps = [], withoutProps = [], checks = [] } = n; | |||
const allowedItemIds = items | |||
.filter(item => { | |||
const isValidItem = ( | |||
withProps.every(p => item.has(p)) && | |||
withoutProps.every(p => !item.has(p)) && | |||
checks.every(c => c(item)) | |||
); | |||
return isValidItem; | |||
}) | |||
.map(item => item.id); | |||
const needItem = { | |||
info, | |||
allowedItemIds | |||
}; | |||
return needItem; | |||
}); | |||
return result; | |||
}; |
@@ -0,0 +1,19 @@ | |||
const buildNeedItems = require('./buildNeedItems'); | |||
module.exports = (crafter, recipe, { pickedItemIds = [] }) => { | |||
const needItems = buildNeedItems(crafter, recipe); | |||
const { inventory: { items } } = crafter; | |||
const result = pickedItemIds.map((pickedId, i) => { | |||
const item = items.find(f => f.id === pickedId); | |||
const isItemValid = needItems[i].allowedItemIds.includes(item.id); | |||
if (!isItemValid) | |||
return null; | |||
return item; | |||
}); | |||
return result; | |||
}; |
@@ -0,0 +1,30 @@ | |||
const recipes = require('../../config/recipes/recipes'); | |||
const buildMaterials = require('./buildMaterials'); | |||
const buildNeedItems = require('./buildNeedItems'); | |||
const buildBase = (crafter, { name, description }) => { | |||
return { | |||
name, | |||
description | |||
}; | |||
}; | |||
module.exports = (craftType, crafter, msg) => { | |||
const recipe = recipes.getRecipe(craftType, msg.name); | |||
if (!recipe) | |||
return; | |||
const result = buildBase(crafter, recipe); | |||
const needItems = buildNeedItems(crafter, recipe); | |||
if (needItems) | |||
result.needItems = needItems; | |||
if (recipe.materialGenerator || recipe.needItems) | |||
result.dynamicMaterials = true; | |||
result.materials = buildMaterials(crafter, recipe, msg); | |||
return result; | |||
}; |
@@ -18,14 +18,6 @@ module.exports = { | |||
type: 'fish', | |||
quantity: [6, 12] | |||
}, | |||
vikardoor: { | |||
properties: { | |||
cpnDoor: { | |||
locked: true, | |||
key: 'vikar' | |||
} | |||
} | |||
}, | |||
shopestrid: { | |||
properties: { | |||
cpnNotice: { | |||
@@ -523,6 +515,61 @@ module.exports = { | |||
type: 'cooking' | |||
} | |||
} | |||
}, | |||
'enchanting shrine': { | |||
components: { | |||
cpnParticles: { | |||
simplify: function () { | |||
return { | |||
type: 'particles', | |||
blueprint: { | |||
color: { | |||
start: ['48edff', 'fc66f7'], | |||
end: ['393268', '42548d'] | |||
}, | |||
scale: { | |||
start: { | |||
min: 2, | |||
max: 10 | |||
}, | |||
end: { | |||
min: 0, | |||
max: 2 | |||
} | |||
}, | |||
speed: { | |||
start: { | |||
min: 4, | |||
max: 16 | |||
}, | |||
end: { | |||
min: 2, | |||
max: 8 | |||
} | |||
}, | |||
lifetime: { | |||
min: 1, | |||
max: 4 | |||
}, | |||
randomScale: true, | |||
randomSpeed: true, | |||
chance: 0.2, | |||
randomColor: true, | |||
spawnType: 'rect', | |||
spawnRect: { | |||
x: -15, | |||
y: -28, | |||
w: 30, | |||
h: 8 | |||
} | |||
} | |||
}; | |||
} | |||
}, | |||
cpnWorkbench: { | |||
type: 'enchanting' | |||
} | |||
} | |||
} | |||
}, | |||
mobs: { | |||
@@ -0,0 +1,90 @@ | |||
const calculateAugmentMaterials = require('./enchanting/calculateAugmentMaterials'); | |||
const reroll = require('./enchanting/craftActions/reroll'); | |||
const relevel = require('./enchanting/craftActions/relevel'); | |||
const augment = require('./enchanting/craftActions/augment'); | |||
const reslot = require('./enchanting/craftActions/reslot'); | |||
const reforge = require('./enchanting/craftActions/reforge'); | |||
const scour = require('./enchanting/craftActions/scour'); | |||
module.exports = [{ | |||
name: 'Augment', | |||
description: 'Adds a random stat to an item. Items can hold a maximum of three augments.', | |||
materialGenerator: calculateAugmentMaterials, | |||
craftAction: augment, | |||
needItems: [{ | |||
info: 'Pick an item to augment', | |||
withProps: ['slot'], | |||
withoutProps: ['noAugment'], | |||
checks: [ | |||
item => { | |||
return !item.power || item.power < 3; | |||
} | |||
] | |||
}] | |||
}, { | |||
name: 'Reroll', | |||
description: 'Rerolls an item\'s implicit and explicit stats. Augmentations are not affected.', | |||
materials: [{ | |||
name: 'Unstable Idol', | |||
quantity: 1 | |||
}], | |||
needItems: [{ | |||
info: 'Pick an item to reroll', | |||
withProps: ['slot'], | |||
withoutProps: ['noAugment'] | |||
}], | |||
craftAction: reroll | |||
}, { | |||
name: 'Increase Level', | |||
description: 'Adds [1 - 3] to an item\'s required level. Items with higher levels yield better stats when rerolled.', | |||
materials: [{ | |||
name: 'Ascendant Idol', | |||
quantity: 1 | |||
}], | |||
needItems: [{ | |||
info: 'Pick an item to reroll', | |||
withProps: ['slot'], | |||
withoutProps: ['noAugment'] | |||
}], | |||
craftAction: relevel | |||
}, { | |||
name: 'Reslot', | |||
description: 'Reforms the item into a random new item that retains the source item\'s quality and stat types.', | |||
materials: [{ | |||
name: 'Dragon-Glass Idol', | |||
quantity: 1 | |||
}], | |||
needItems: [{ | |||
info: 'Pick an item to reroll', | |||
withProps: ['slot'], | |||
withoutProps: ['noAugment', 'effects'] | |||
}], | |||
craftAction: reslot | |||
}, { | |||
name: 'Reforge Weapon', | |||
description: 'Rerolls a weapon\'s damage range.', | |||
materials: [{ | |||
name: 'Bone Idol', | |||
quantity: 1 | |||
}], | |||
needItems: [{ | |||
info: 'Pick an item to reroll', | |||
withProps: ['slot', 'spell'], | |||
withoutProps: ['noAugment'] | |||
}], | |||
craftAction: reforge | |||
}, { | |||
name: 'Scour', | |||
description: 'Rerolls a weapon\'s damage range.', | |||
materials: [{ | |||
name: 'Bone Idol', | |||
quantity: 1 | |||
}], | |||
needItems: [{ | |||
info: 'Pick an item to reroll', | |||
withProps: ['slot', 'power'], | |||
withoutProps: ['noAugment'] | |||
}], | |||
craftAction: scour | |||
}]; |
@@ -0,0 +1,17 @@ | |||
const salvager = require('../../../items/salvager'); | |||
module.exports = (obj, [item]) => { | |||
let powerLevel = item.power || 0; | |||
let mult = null; | |||
if (powerLevel < 3) | |||
mult = [5, 10, 20][powerLevel]; | |||
else | |||
return; | |||
const result = salvager.salvage(item, true); | |||
result.forEach(r => { | |||
r.quantity = Math.max(1, ~~(r.quantity * mult)); | |||
}); | |||
return result; | |||
}; |
@@ -0,0 +1,17 @@ | |||
let generatorStats = require('../../../../items/generators/stats'); | |||
module.exports = (obj, [item]) => { | |||
let newPower = (item.power || 0) + 1; | |||
if (newPower > 3) | |||
return; | |||
item.power = newPower; | |||
const result = { msg: 'Augment successful', addStatMsgs: [] }; | |||
generatorStats.generate(item, { | |||
statCount: 1 | |||
}, result); | |||
return result; | |||
}; |
@@ -0,0 +1,17 @@ | |||
let generatorSpells = require('../../../../items/generators/spellbook'); | |||
module.exports = (obj, [item]) => { | |||
if (!item.spell) | |||
return; | |||
let spellName = item.spell.name.toLowerCase(); | |||
let oldSpell = item.spell; | |||
delete item.spell; | |||
generatorSpells.generate(item, { | |||
spellName: spellName | |||
}); | |||
item.spell = extend(oldSpell, item.spell); | |||
return { msg: 'Reforge successful' }; | |||
}; |
@@ -0,0 +1,18 @@ | |||
module.exports = (obj, [item]) => { | |||
if (item.slot === 'tool') | |||
return; | |||
let offset = 1 + ~~(Math.random() * 2); | |||
const maxLevel = consts.maxLevel; | |||
if (!item.originalLevel) | |||
item.level = Math.min(maxLevel, item.level + offset); | |||
else { | |||
offset = Math.min(maxLevel - item.originalLevel, offset); | |||
item.originalLevel = Math.min(maxLevel, item.originalLevel + offset); | |||
item.level = Math.min(maxLevel, item.level + offset); | |||
} | |||
return { msg: 'Relevel successful' }; | |||
}; |
@@ -0,0 +1,58 @@ | |||
let generatorStats = require('../../../../items/generators/stats'); | |||
let generatorSlots = require('../../../../items/generators/slots'); | |||
let generatorTypes = require('../../../../items/generators/types'); | |||
module.exports = (obj, [item]) => { | |||
const enchantedStats = item.enchantedStats; | |||
const implicitStats = item.implicitStats; | |||
delete item.enchantedStats; | |||
delete item.implicitStats; | |||
if ((item.stats) && (item.stats.lvlRequire)) { | |||
item.level = Math.min(consts.maxLevel, item.level + item.stats.lvlRequire); | |||
delete item.originalLevel; | |||
} | |||
item.stats = {}; | |||
let bpt = { | |||
slot: item.slot, | |||
type: item.type, | |||
sprite: item.sprite, | |||
spritesheet: item.spritesheet | |||
}; | |||
generatorSlots.generate(item, bpt); | |||
generatorTypes.generate(item, bpt); | |||
generatorStats.generate(item, bpt); | |||
for (let p in enchantedStats) { | |||
if (!item.stats[p]) | |||
item.stats[p] = 0; | |||
item.stats[p] += enchantedStats[p]; | |||
if (p === 'lvlRequire') { | |||
if (!item.originalLevel) | |||
item.originalLevel = item.level; | |||
item.level -= enchantedStats[p]; | |||
if (item.level < 1) | |||
item.level = 1; | |||
} | |||
} | |||
item.enchantedStats = enchantedStats || null; | |||
//Some items have special implicits (different stats than their types imply) | |||
// We add the old one back in if this is the case. Ideally we'd like to reroll | |||
// these but that'd be a pretty big hack. We'll solve this one day | |||
if ( | |||
item.implicitStats && | |||
implicitStats && | |||
item.implicitStats[0] && | |||
implicitStats[0] && | |||
item.implicitStats[0].stat !== implicitStats[0].stat | |||
) | |||
item.implicitStats = implicitStats; | |||
return { msg: 'Reroll successful' }; | |||
}; |
@@ -0,0 +1,34 @@ | |||
let configSlots = require('../../../../items/config/slots'); | |||
let generator = require('../../../../items/generator'); | |||
module.exports = (obj, [item]) => { | |||
if (item.effects || item.slot === 'tool') | |||
return; | |||
if (item.originalLevel) | |||
item.level = item.originalLevel; | |||
delete item.enchantedStats; | |||
let possibleStats = Object.keys(item.stats || {}); | |||
let newItem = generator.generate({ | |||
slot: configSlots.getRandomSlot(item.slot), | |||
level: item.level, | |||
quality: item.quality, | |||
stats: possibleStats, | |||
limitSlotStats: true | |||
}); | |||
delete item.spritesheet; | |||
delete item.stats; | |||
delete item.spell; | |||
delete item.implicitStats; | |||
delete item.power; | |||
delete item.range; | |||
delete item.requires; | |||
extend(item, newItem); | |||
return { msg: 'Reslot successful' }; | |||
}; |
@@ -0,0 +1,31 @@ | |||
module.exports = (obj, [item]) => { | |||
if (!item.power) | |||
return; | |||
const result = { msg: 'Scour successful', addStatMsgs: [] }; | |||
for (let p in item.enchantedStats) { | |||
let value = item.enchantedStats[p]; | |||
if (item.stats[p]) { | |||
result.addStatMsgs.push({ | |||
stat: p, | |||
value: -value | |||
}); | |||
item.stats[p] -= value; | |||
if (item.stats[p] <= 0) | |||
delete item.stats[p]; | |||
if (p === 'lvlRequire') { | |||
item.level = Math.min(consts.maxLevel, item.level + value); | |||
delete item.originalLevel; | |||
} | |||
} | |||
} | |||
delete item.enchantedStats; | |||
delete item.power; | |||
return result; | |||
}; |
@@ -3,17 +3,13 @@ let events = require('../../misc/events'); | |||
const recipesAlchemy = require('./alchemy'); | |||
const recipesCooking = require('./cooking'); | |||
const recipesEtching = require('./etching'); | |||
const recipesEnchanting = require('./enchanting'); | |||
let recipes = { | |||
alchemy: [ | |||
...recipesAlchemy | |||
], | |||
cooking: [ | |||
...recipesCooking | |||
], | |||
etching: [ | |||
...recipesEtching | |||
] | |||
alchemy: [ ...recipesAlchemy ], | |||
cooking: [ ...recipesCooking ], | |||
etching: [ ...recipesEtching ], | |||
enchanting: [ ...recipesEnchanting ] | |||
}; | |||
module.exports = { | |||
@@ -22,7 +18,11 @@ module.exports = { | |||
}, | |||
getList: function (type, unlocked) { | |||
return (recipes[type] || []) | |||
const useRecipes = recipes[type]; | |||
if (!useRecipes) | |||
return []; | |||
return useRecipes | |||
.filter(r => { | |||
let hasUnlocked = (r.default !== false); | |||
if (!hasUnlocked) | |||
@@ -1,245 +0,0 @@ | |||
let generatorStats = require('./generators/stats'); | |||
let generatorSlots = require('./generators/slots'); | |||
let generatorTypes = require('./generators/types'); | |||
let generatorSpells = require('./generators/spellbook'); | |||
let salvager = require('./salvager'); | |||
let configCurrencies = require('./config/currencies'); | |||
let configSlots = require('./config/slots'); | |||
let generator = require('./generator'); | |||
const reroll = (item, msg) => { | |||
const enchantedStats = item.enchantedStats; | |||
const implicitStats = item.implicitStats; | |||
delete item.enchantedStats; | |||
delete item.implicitStats; | |||
delete msg.addStatMsgs; | |||
if ((item.stats) && (item.stats.lvlRequire)) { | |||
item.level = Math.min(consts.maxLevel, item.level + item.stats.lvlRequire); | |||
delete item.originalLevel; | |||
} | |||
item.stats = {}; | |||
let bpt = { | |||
slot: item.slot, | |||
type: item.type, | |||
sprite: item.sprite, | |||
spritesheet: item.spritesheet | |||
}; | |||
generatorSlots.generate(item, bpt); | |||
generatorTypes.generate(item, bpt); | |||
generatorStats.generate(item, bpt); | |||
for (let p in enchantedStats) { | |||
if (!item.stats[p]) | |||
item.stats[p] = 0; | |||
item.stats[p] += enchantedStats[p]; | |||
if (p === 'lvlRequire') { | |||
if (!item.originalLevel) | |||
item.originalLevel = item.level; | |||
item.level -= enchantedStats[p]; | |||
if (item.level < 1) | |||
item.level = 1; | |||
} | |||
} | |||
item.enchantedStats = enchantedStats || null; | |||
//Some items have special implicits (different stats than their types imply) | |||
// We add the old one back in if this is the case. Ideally we'd like to reroll | |||
// these but that'd be a pretty big hack. We'll solve this one day | |||
if ( | |||
item.implicitStats && | |||
implicitStats && | |||
item.implicitStats[0] && | |||
implicitStats[0] && | |||
item.implicitStats[0].stat !== implicitStats[0].stat | |||
) | |||
item.implicitStats = implicitStats; | |||
}; | |||
const relevel = item => { | |||
if (item.slot === 'tool') | |||
return; | |||
let offset = 1 + ~~(Math.random() * 2); | |||
const maxLevel = consts.maxLevel; | |||
if (!item.originalLevel) | |||
item.level = Math.min(maxLevel, item.level + offset); | |||
else { | |||
offset = Math.min(maxLevel - item.originalLevel, offset); | |||
item.originalLevel = Math.min(maxLevel, item.originalLevel + offset); | |||
item.level = Math.min(maxLevel, item.level + offset); | |||
} | |||
}; | |||
const reslot = (item, msg) => { | |||
if (item.effects || item.slot === 'tool') | |||
return; | |||
if (item.originalLevel) | |||
item.level = item.originalLevel; | |||
delete item.enchantedStats; | |||
delete msg.addStatMsgs; | |||
let possibleStats = Object.keys(item.stats || {}); | |||
let newItem = generator.generate({ | |||
slot: configSlots.getRandomSlot(item.slot), | |||
level: item.level, | |||
quality: item.quality, | |||
stats: possibleStats, | |||
limitSlotStats: true | |||
}); | |||
delete item.spritesheet; | |||
delete item.stats; | |||
delete item.spell; | |||
delete item.implicitStats; | |||
delete item.power; | |||
delete item.range; | |||
delete item.requires; | |||
extend(item, newItem); | |||
}; | |||
const reforge = item => { | |||
if (!item.spell) | |||
return; | |||
let spellName = item.spell.name.toLowerCase(); | |||
let oldSpell = item.spell; | |||
delete item.spell; | |||
generatorSpells.generate(item, { | |||
spellName: spellName | |||
}); | |||
item.spell = extend(oldSpell, item.spell); | |||
}; | |||
const scour = (item, result) => { | |||
if (!item.power) | |||
return; | |||
for (let p in item.enchantedStats) { | |||
let value = item.enchantedStats[p]; | |||
if (item.stats[p]) { | |||
result.addStatMsgs.push({ | |||
stat: p, | |||
value: -value | |||
}); | |||
item.stats[p] -= value; | |||
if (item.stats[p] <= 0) | |||
delete item.stats[p]; | |||
if (p === 'lvlRequire') { | |||
item.level = Math.min(consts.maxLevel, item.level + value); | |||
delete item.originalLevel; | |||
} | |||
} | |||
} | |||
delete item.enchantedStats; | |||
delete item.power; | |||
}; | |||
const augment = (item, inventory, result, msg) => { | |||
let newPower = (item.power || 0) + 1; | |||
if (newPower > 3) { | |||
inventory.resolveCallback(msg); | |||
return; | |||
} | |||
item.power = newPower; | |||
generatorStats.generate(item, { | |||
statCount: 1 | |||
}, result); | |||
}; | |||
module.exports = { | |||
enchant: function (obj, item, msg) { | |||
let inventory = obj.inventory; | |||
let config = this.getEnchantMaterials(item, msg.action); | |||
let success = true; | |||
config.materials.forEach(function (m) { | |||
let hasMaterial = inventory.items.find(i => i.name === m.name); | |||
if (hasMaterial) | |||
hasMaterial = hasMaterial.quantity >= m.quantity; | |||
if (!hasMaterial) | |||
success = false; | |||
}); | |||
if (!success) { | |||
inventory.resolveCallback(msg); | |||
return; | |||
} | |||
let result = { | |||
item: item, | |||
addStatMsgs: [] | |||
}; | |||
config.materials.forEach(function (m) { | |||
let invMaterial = inventory.items.find(i => i.name === m.name); | |||
inventory.destroyItem(invMaterial.id, m.quantity); | |||
}); | |||
if (msg.action === 'reroll') | |||
reroll(item, msg); | |||
else if (msg.action === 'relevel') | |||
relevel(item); | |||
else if (msg.action === 'reslot') | |||
reslot(item, msg); | |||
else if (msg.action === 'reforge') | |||
reforge(item); | |||
else if (msg.action === 'scour') | |||
scour(item, result); | |||
else | |||
augment(item, inventory, result, msg); | |||
obj.syncer.setArray(true, 'inventory', 'getItems', inventory.simplifyItem(item)); | |||
inventory.resolveCallback(msg, result); | |||
}, | |||
getEnchantMaterials: function (item, action) { | |||
let result = null; | |||
if (action === 'reroll') | |||
result = [configCurrencies.getCurrencyFromAction('reroll')]; | |||
else if (action === 'relevel') | |||
result = [configCurrencies.getCurrencyFromAction('relevel')]; | |||
else if (action === 'reslot') | |||
result = [configCurrencies.getCurrencyFromAction('reslot')]; | |||
else if (action === 'reforge') | |||
result = [configCurrencies.getCurrencyFromAction('reforge')]; | |||
else if (action === 'scour') | |||
result = [configCurrencies.getCurrencyFromAction('scour')]; | |||
else { | |||
let powerLevel = item.power || 0; | |||
let mult = null; | |||
if (powerLevel < 3) | |||
mult = [5, 10, 20][powerLevel]; | |||
else | |||
return; | |||
result = salvager.salvage(item, true); | |||
result.forEach(r => { | |||
r.quantity = Math.max(1, ~~(r.quantity * mult)); | |||
}); | |||
} | |||
return { | |||
materials: result | |||
}; | |||
} | |||
}; |
@@ -10,7 +10,7 @@ const routerConfig = { | |||
dialogue: ['talk'], | |||
gatherer: ['gather'], | |||
quests: ['complete'], | |||
inventory: ['combineStacks', 'splitStack', 'activateMtx', 'useItem', 'moveItem', 'enchantItem', 'getEnchantMaterials', 'learnAbility', 'unlearnAbility', 'dropItem', 'destroyItem', 'salvageItem', 'stashItem', 'mailItem', 'sortInventory'], | |||
inventory: ['combineStacks', 'splitStack', 'activateMtx', 'useItem', 'moveItem', 'learnAbility', 'unlearnAbility', 'dropItem', 'destroyItem', 'salvageItem', 'stashItem', 'mailItem', 'sortInventory'], | |||
equipment: ['equip', 'unequip', 'setQuickSlot', 'useQuickSlot', 'inspect'], | |||
stash: ['withdraw', 'open'], | |||
trade: ['buySell'], | |||