const preact = require('preact'); const { connect } = require('preact-redux'); const reactStringReplace = require('react-string-replace'); const countBy = require('lodash/countBy'); const specThresholds = require('./vbox.info.thresholds'); const { INFO } = require('./../constants'); const { convertItem, removeTier } = require('../utils'); const { tutorialStage } = require('../tutorial.utils'); const shapes = require('./shapes'); const actions = require('../actions'); const addState = connect( function receiveState(state) { const { ws, info, itemInfo, instance, player, account, tutorial, vboxSelected, } = state; function sendVboxAccept(group, index) { document.activeElement.blur(); return ws.sendVboxAccept(instance.id, group, index); } function sendVboxCombine() { return ws.sendVboxCombine(instance.id, vboxSelected.stashSelect, vboxSelected.storeSelect); } return { ws, info, itemInfo, instance, player, account, tutorial, vboxSelected, sendVboxAccept, sendVboxCombine, }; }, function receiveDispatch(dispatch) { function setTutorialNull() { dispatch(actions.setTutorial(null)); } function setInfo(info) { dispatch(actions.setInfo(info)); } return { setTutorialNull, setInfo }; } ); class InfoComponent extends preact.Component { shouldComponentUpdate(newProps, newState) { if (newProps.vboxSelected !== this.props.vboxSelected) return true; if (newProps.vboxHighlight !== this.props.vboxHighlight) return true; if (newProps.tutorial !== this.props.tutorial) return true; // We don't care about info during tutorial if (newProps.tutorial && this.props.instance.time_control === 'Practice' && this.props.instance.rounds.length === 1) return false; if (newProps.info !== this.props.info) return true; if (newState.comboItem !== this.state.comboItem) return true; return false; } componentDidUpdate(prevProps) { // Catch case where mouse events don't properly clear state and info changed if (prevProps.info !== this.props.info && this.state.comboItem) this.setState({ comboItem: null }); } render(args) { const { // Variables that will change info, tutorial, vboxHighlight, vboxSelected, // Static player, // Only used for colour calcs which will be update if info changes ws, itemInfo, instance, // Only used for instance id // functions sendVboxAccept, sendVboxCombine, setTutorialNull, } = args; const { comboItem } = this.state; const { vbox } = player; const { stashSelect, storeSelect } = vboxSelected; const vboxCombo = () => { if (!(vboxHighlight && vboxHighlight.length === 0)) return false; // The selected items can't be combined with additional items therefore valid combo const stashItems = stashSelect.map(j => vbox.bound[j]); const shopItems = storeSelect.map(j => vbox.free[j[0]][j[1]]); const selectedItems = stashItems.concat(shopItems); const combinerCount = countBy(selectedItems, co => co); const comboItemObj = itemInfo.combos.find(combo => selectedItems.every(c => { if (!combo.components.includes(c)) return false; const comboCount = countBy(combo.components, co => co); if (combinerCount[c] > comboCount[c]) return false; return true; })); return comboItemObj.item; }; const combinerCombo = vboxCombo(); const checkVboxInfo = () => { if (combinerCombo) return combinerCombo; const stashBase = stashSelect.find(i => !(['Red', 'Blue', 'Green'].includes(vbox.bound[i]))); if (stashBase > -1) return vbox.bound[stashBase]; const storeBase = storeSelect.find(j => !(['Red', 'Blue', 'Green'].includes(vbox.free[j[0]][j[1]]))); if (storeBase) return vbox.free[storeBase[0]][storeBase[1]]; if (stashSelect.length > 0) return vbox.bound[stashSelect[0]]; if (storeSelect.length > 0) return vbox.free[storeSelect[0][0]][storeSelect[0][1]]; return false; }; const vboxInfo = (stashSelect.length > 0 || storeSelect.length > 0) ? checkVboxInfo() : false; function Info() { if (tutorial) { const tutorialStageInfo = tutorialStage(tutorial, ws, setTutorialNull, instance); if (tutorialStageInfo) return tutorialStageInfo; } function genItemInfo(item) { const fullInfo = itemInfo.items.find(i => i.item === item) || INFO[item]; const isSkill = fullInfo.skill; const isSpec = fullInfo.spec; const itemDescription = () => { const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat|LIFE|SPEED|POWER)/; const infoDescription = reactStringReplace(fullInfo.description, regEx, m => shapes[m]()); return
{reactStringReplace(infoDescription, '\n', () =>
)}
; }; if (isSkill || isSpec) { let infoName = fullInfo.item; while (infoName.includes('Plus')) infoName = infoName.replace('Plus', '+'); const itemSource = itemInfo.combos.filter(c => c.item === removeTier(fullInfo.item)); let itemSourceInfo = itemSource.length && !isSpec ? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}` : false; let header = null; if (!itemSource.length) header = isSkill ?

SKILL

:

SPEC

; if (itemSourceInfo) { while (itemSourceInfo.includes('Plus')) itemSourceInfo = itemSourceInfo.replace('Plus', '+'); const itemRegEx = /(Red|Blue|Green)/; itemSourceInfo = reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]()); } const cooldown = isSkill && fullInfo.cooldown ?
{fullInfo.cooldown} Turn delay
: null; const speed = isSkill ?
Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}%
: null; const thresholds = isSpec ? specThresholds(player, fullInfo, info) : null; return (

{infoName}

{header} {itemSourceInfo} {cooldown} {itemDescription()} {speed} {thresholds}
); } return (

{fullInfo.item}

{itemDescription()}
); } const stateFullInfo = comboItem || vboxInfo; if (stateFullInfo) return genItemInfo(stateFullInfo); if (!info) return false; if (info.includes('constructName')) { return (

{info.replace('constructName ', '')}

This is the name of your construct.
Names are randomly generated and are purely cosmetic.
You can change change your construct name in the RESHAPE tab outside of games.

); } if (info.includes('constructAvatar')) { return (

{info.replace('constructAvatar ', '')}

This is your construct avatar.
Avatars are randomly generated and are purely cosmetic.
You can change your construct avatar in the RESHAPE tab outside of games.

); } return genItemInfo(info); } const Combos = () => { if (tutorial && instance.time_control === 'Practice' && instance.rounds.length === 1) return false; if (!info && !vboxInfo) return false; const comboInfo = vboxInfo || info; const vboxCombos = itemInfo.combos.filter(c => c.components.includes(comboInfo)); if (vboxCombos.length > 6 || vboxCombos.length === 0) return false; const comboTable = vboxCombos.map((c, i) => { const mouseOver = e => { e.stopPropagation(); this.setState({ comboItem: c.item }); }; const componentTable = (c.components.some(ci => ['Red', 'Blue', 'Green'].includes(ci))) ? [
{convertItem(c.components[0])} {convertItem(c.components[1])}
,
{convertItem(c.components[2])}
] : c.components.map((u, j) =>
{convertItem(u)}
); return (
{convertItem(c.item)}
{componentTable}
); }); const comboList = comboTable.length > 0 ?
{comboTable}
: false; return (

COMBOS

Combine colours and items.
{comboList}
); }; const Combiner = () => { if (comboItem) return false; function vboxBuySelected() { if (!(storeSelect.length === 1 && stashSelect.length === 0)) return false; document.activeElement.blur(); sendVboxAccept(storeSelect[0][0], storeSelect[0][1]); return true; } let text = ''; let mouseEvent = false; if (combinerCombo) { const combinerComboText = combinerCombo.replace('Plus', '+'); let bits = 0; storeSelect.forEach(item => bits += item[0] + 1); text = bits ? `Buy ${combinerComboText} ${bits}b` : `Combine ${combinerComboText}`; if (vbox.bits >= bits) mouseEvent = sendVboxCombine; } else if (stashSelect.length === 0 && storeSelect.length === 1) { const item = storeSelect[0]; text = `Buy ${vbox.free[item[0]][item[1]]} ${item[0] + 1}b`; mouseEvent = vboxBuySelected; } else { return false; } return (
); }; return (
this.setState({ comboItem: null })}>
); } } module.exports = addState(InfoComponent);