);
}
diff --git a/client/src/components/vbox.info.jsx b/client/src/components/vbox.info.jsx
new file mode 100644
index 00000000..d2eddbec
--- /dev/null
+++ b/client/src/components/vbox.info.jsx
@@ -0,0 +1,78 @@
+const preact = require('preact');
+const { connect } = require('preact-redux');
+
+const { tutorialStage } = require('../tutorial.utils');
+const { genItemInfo } = require('./vbox.utils');
+
+const addState = connect(
+ ({ info, player, tutorial, vboxInfo, itemInfo, instance, comboPreview }) => ({
+ info, player, tutorial, vboxInfo, itemInfo, instance, comboPreview,
+ }));
+
+
+class Info extends preact.Component {
+ shouldComponentUpdate(newProps) {
+ if (newProps.clearTutorial !== this.props.clearTutorial) return true;
+ if (newProps.info !== this.props.info) return true;
+ if (newProps.player !== this.props.player) return true;
+ if (newProps.tutorial !== this.props.tutorial) return true;
+ if (newProps.vboxInfo !== this.props.vboxInfo) return true;
+ if (newProps.itemInfo !== this.props.itemInfo) return true;
+ if (newProps.instance !== this.props.instance) return true;
+ if (newProps.comboPreview !== this.props.comboPreview) return true;
+ return false;
+ }
+
+ render(props) {
+ const {
+ // passed props
+ clearTutorial,
+ // connect state props
+ info,
+ player,
+ tutorial,
+ vboxInfo,
+ itemInfo,
+ instance,
+ comboPreview,
+ } = props;
+
+ // dispaly priority
+ // tutorial -> comboPreview -> vboxInfo -> info
+ if (tutorial) {
+ const tutorialStageInfo = tutorialStage(tutorial, clearTutorial, instance);
+ if (tutorialStageInfo) return tutorialStageInfo;
+ }
+ if (comboPreview) return genItemInfo(comboPreview, itemInfo, player);
+ if (vboxInfo) return genItemInfo(vboxInfo, itemInfo, player);
+
+ if (!info) return false;
+ if (info.includes('constructName')) {
+ return (
+
+ );
+ }
+
+ if (info.includes('constructAvatar')) {
+ return (
+
+ );
+ }
+
+ return genItemInfo(info, itemInfo, player, info);
+ }
+}
+
+module.exports = addState(Info);
diff --git a/client/src/components/info.thresholds.jsx b/client/src/components/vbox.info.thresholds.jsx
similarity index 95%
rename from client/src/components/info.thresholds.jsx
rename to client/src/components/vbox.info.thresholds.jsx
index 7136a855..9f259f0e 100644
--- a/client/src/components/info.thresholds.jsx
+++ b/client/src/components/vbox.info.thresholds.jsx
@@ -3,6 +3,7 @@ const range = require('lodash/range');
const shapes = require('./shapes');
function specThresholds(player, fullInfo, info) {
+ if (!info) return false;
let red = 0;
let blue = 0;
let green = 0;
@@ -87,9 +88,12 @@ function specThresholds(player, fullInfo, info) {
);
});
return (
-
- {thresholds}
+
+
);
}
diff --git a/client/src/components/vbox.stash.jsx b/client/src/components/vbox.stash.jsx
new file mode 100644
index 00000000..cec94686
--- /dev/null
+++ b/client/src/components/vbox.stash.jsx
@@ -0,0 +1,132 @@
+const preact = require('preact');
+const { connect } = require('preact-redux');
+
+const range = require('lodash/range');
+const without = require('lodash/without');
+
+const shapes = require('./shapes');
+const buttons = require('./buttons');
+const { removeTier } = require('../utils');
+
+const addState = connect(
+ ({ itemUnequip, vboxHighlight, vboxSelected }) => ({ itemUnequip, vboxHighlight, vboxSelected }));
+
+class stashElement extends preact.Component {
+ shouldComponentUpdate(newProps) {
+ if (newProps.sendItemUnequip !== this.props.sendItemUnequip) return true;
+ if (newProps.setInfo !== this.props.setInfo) return true;
+ if (newProps.setVboxSelected !== this.props.setVboxSelected) return true;
+ if (newProps.vbox !== this.props.vbox) return true;
+ if (newProps.vboxBuySelected !== this.props.vboxBuySelected) return true;
+ if (newProps.vboxHover !== this.props.vboxHover) return true;
+
+ if (newProps.itemUnequip !== this.props.itemUnequip) return true;
+ if (newProps.vboxHighlight !== this.props.vboxHighlight) return true;
+ if (newProps.vboxSelected !== this.props.vboxSelected) return true;
+ return false;
+ }
+
+ render(props) {
+ const {
+ // passed props
+ sendItemUnequip,
+ setInfo,
+ setVboxSelected,
+ vbox,
+ vboxBuySelected,
+ vboxHover,
+ // connect state props
+ itemUnequip,
+ vboxHighlight,
+ vboxSelected,
+ } = props;
+
+ const { storeSelect, stashSelect } = vboxSelected;
+
+ const vboxSelecting = storeSelect.length === 1 && stashSelect.length === 0;
+
+ function stashClick(e) {
+ e.stopPropagation();
+ if (itemUnequip.length) return sendItemUnequip(itemUnequip);
+ if (vboxSelecting) return vboxBuySelected();
+ return true;
+ }
+
+ function stashBtn(v, i) {
+ const stashHighlight = vboxSelecting || itemUnequip.length;
+
+ if (!v && v !== 0) {
+ const emptyInvClick = () => {
+ if (vboxSelecting) return vboxBuySelected();
+ return false;
+ };
+ return
;
+ }
+
+ const notValidCombo = vboxHighlight && !vboxHighlight.includes(v);
+
+ function onClick(type, e) {
+ e.stopPropagation();
+ const combinerContainsIndex = stashSelect.indexOf(i) > -1;
+ // removing
+ if (combinerContainsIndex) {
+ if (type === 'click') {
+ return setVboxSelected({ storeSelect, stashSelect: without(stashSelect, i) });
+ }
+ return true;
+ }
+
+ if (notValidCombo) {
+ setInfo(vbox.stash[i]);
+ return setVboxSelected({ storeSelect: [], stashSelect: [i] });
+ }
+
+ return setVboxSelected({ storeSelect, stashSelect: [...stashSelect, i] });
+ }
+
+ const highlighted = stashSelect.indexOf(i) > -1;
+ const border = buttons[removeTier(v)] ? buttons[removeTier(v)]() : '';
+ const classes = highlighted
+ ? 'highlight'
+ : `${border} ${notValidCombo ? 'fade' : ''}`;
+
+ const invObject = shapes[v] ? shapes[v]() : v;
+
+ return (
+
{
+ onClick('drag', ev);
+ ev.dataTransfer.setData('text', '');
+ }}>
+ vboxHover(e, v)}
+ onClick={e => e.stopPropagation()}
+ onMouseDown={e => onClick('click', e)}>
+ {invObject}
+
+
+ );
+ }
+
+ return (
+
e.stopPropagation()}
+ onDragOver={ev => ev.preventDefault()}
+ onDrop={stashClick}
+ >
+ {range(0, 6).map(i => stashBtn(vbox.stash[i], i.toString()))}
+
+ );
+ }
+}
+
+module.exports = addState(stashElement);
diff --git a/client/src/components/vbox.store.jsx b/client/src/components/vbox.store.jsx
new file mode 100644
index 00000000..f4d1878c
--- /dev/null
+++ b/client/src/components/vbox.store.jsx
@@ -0,0 +1,95 @@
+const preact = require('preact');
+const { connect } = require('preact-redux');
+const range = require('lodash/range');
+
+const shapes = require('./shapes');
+
+const addState = connect(({ vboxHighlight }) => ({ vboxHighlight }));
+
+class storeElement extends preact.Component {
+ shouldComponentUpdate(newProps) {
+ if (newProps.clearVboxSelected !== this.props.clearVboxSelected) return true;
+ if (newProps.setVboxSelected !== this.props.setVboxSelected) return true;
+ if (newProps.vbox !== this.props.vbox) return true;
+ if (newProps.vboxHighlight !== this.props.vboxHighlight) return true;
+ if (newProps.vboxHover !== this.props.vboxHover) return true;
+ if (newProps.vboxSelected !== this.props.vboxSelected) return true;
+ return false;
+ }
+
+ render(props) {
+ const {
+ // passed props
+ clearVboxSelected,
+ setVboxSelected,
+ vbox,
+ vboxHover,
+ vboxSelected,
+ // connect state props
+ vboxHighlight,
+ } = props;
+
+ const { storeSelect, stashSelect } = vboxSelected;
+
+ function availableBtn(v, group, index) {
+ if (!v) return
;
+ const selected = storeSelect.length && storeSelect.some(vs => vs[0] === group && vs[1] === index);
+
+ const notValidCombo = vboxHighlight && !vboxHighlight.includes(v);
+
+ function onClick(e) {
+ e.stopPropagation();
+ if (storeSelect.length && storeSelect.some(vs => vs[0] === group && vs[1] === index)) {
+ return setVboxSelected(
+ { storeSelect: storeSelect.filter(vs => !(vs[0] === group && vs[1] === index)), stashSelect }
+ );
+ }
+
+ if (!storeSelect.length && !stashSelect.length) {
+ return setVboxSelected({ storeSelect: [[group, index]], stashSelect });
+ }
+ if (notValidCombo) {
+ return setVboxSelected({ storeSelect: [[group, index]], stashSelect: [] });
+ }
+ return setVboxSelected({ storeSelect: [...storeSelect, [group, index]], stashSelect });
+ }
+
+
+ const classes = selected
+ ? `${v.toLowerCase()} highlight`
+ : `${v.toLowerCase()} ${notValidCombo ? 'fade' : ''}`;
+
+ const vboxObject = shapes[v] ? shapes[v]() : v;
+ const disabled = vbox.bits <= group;
+ return (
+
+ vboxHover(e, v)}
+ onMouseDown={onClick}
+ onClick={e => e.stopPropagation()}
+ > {vboxObject}
+
+
+ );
+ }
+
+ return (
+
e.stopPropagation()}>
+
+ {range(0, 6).map(i => availableBtn(vbox.store['Colours'][i], 'Colours', i.toString()))}
+
+
+ {range(0, 3).map(i => availableBtn(vbox.store['Skills'][i], 'Skills', i.toString()))}
+ {range(0, 3).map(i => availableBtn(vbox.store['Specs'][i], 'Specs', i.toString()))}
+
+
+ );
+ }
+}
+
+module.exports = addState(storeElement);
diff --git a/client/src/components/vbox.utils.jsx b/client/src/components/vbox.utils.jsx
new file mode 100644
index 00000000..4a4a90d1
--- /dev/null
+++ b/client/src/components/vbox.utils.jsx
@@ -0,0 +1,133 @@
+const preact = require('preact');
+const countBy = require('lodash/countBy');
+const forEach = require('lodash/forEach');
+const reactStringReplace = require('react-string-replace');
+
+const actions = require('../actions');
+const specThresholds = require('./vbox.info.thresholds');
+const { INFO } = require('./../constants');
+const { removeTier } = require('../utils');
+const shapes = require('./shapes');
+
+function setVboxState(dispatch, vboxSelected, state) {
+ const {
+ itemInfo,
+ itemUnequip,
+ vbox,
+ } = state;
+ const { storeSelect, stashSelect } = vboxSelected;
+
+ // default returns
+ let vboxCombiner = false;
+ let vboxHighlight = false;
+
+ if (storeSelect.length || stashSelect.length) {
+ vboxHighlight = [];
+ const stashItems = stashSelect.map(j => vbox.stash[j]);
+ const shopItems = storeSelect.map(j => vbox.store[j[0]][j[1]]);
+
+ const selectedItems = stashItems.concat(shopItems);
+ const itemCount = countBy(selectedItems, co => co);
+
+ itemInfo.combos.forEach(combo => {
+ const comboCount = countBy(combo.components, co => co);
+ const buyCount = countBy(combo.components, co => co);
+ const valid = selectedItems.every(c => {
+ if (!combo.components.includes(c)) return false;
+ if (itemCount[c] > comboCount[c]) return false;
+ buyCount[c] -= 1;
+ return true;
+ });
+ if (valid) {
+ const fullCombo = combo.components.every(c => itemCount[c] === comboCount[c]);
+ if (fullCombo) vboxCombiner = combo.item;
+
+ forEach(buyCount, (value, key) => {
+ if (value > 0 && !vboxHighlight.includes(key)) {
+ vboxHighlight.push(key);
+ }
+ });
+ }
+ });
+ }
+
+
+ const vboxInfo = () => {
+ if (vboxCombiner) return vboxCombiner;
+ if (itemUnequip.length) return itemUnequip[1];
+ const stashBase = stashSelect.find(i => !(['Red', 'Blue', 'Green'].includes(vbox.stash[i])));
+ if (stashBase > -1) return vbox.stash[stashBase];
+ const storeBase = storeSelect.find(j => !(['Red', 'Blue', 'Green'].includes(vbox.store[j[0]][j[1]])));
+ if (storeBase) return vbox.store[storeBase[0]][storeBase[1]];
+ if (stashSelect.length > 0) return vbox.stash[stashSelect[0]];
+ if (storeSelect.length > 0) return vbox.store[storeSelect[0][0]][storeSelect[0][1]];
+ return false;
+ };
+
+ dispatch(actions.setVboxInfo(vboxInfo()));
+ dispatch(actions.setVboxCombiner(vboxCombiner));
+ dispatch(actions.setVboxHighlight(vboxHighlight));
+}
+
+function genItemInfo(item, itemInfo, player) {
+ 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, item) : null;
+
+ return (
+
+
{infoName}
+ {header}
+ {itemSourceInfo}
+ {cooldown}
+ {itemDescription()}
+ {speed}
+ {thresholds}
+
+ );
+ }
+ return (
+
+
{fullInfo.item}
+ {itemDescription()}
+
+ );
+}
+
+function cost(group) {
+ if (group === 'Colours') return 1;
+ if (group === 'Skills') return 2;
+ if (group === 'Specs') return 3;
+};
+
+module.exports = { setVboxState, genItemInfo, cost };
diff --git a/client/src/constants.jsx b/client/src/constants.jsx
index 5a7a24a3..c6b6dfaf 100644
--- a/client/src/constants.jsx
+++ b/client/src/constants.jsx
@@ -3,7 +3,7 @@ const preact = require('preact');
const SOURCE_DURATION_MS = 1000; // Time for SOURCE ONLY
const TARGET_DELAY_MS = 500; // Used for Source + Target
const TARGET_DURATION_MS = 1500; // Time for TARGET ONLY
-const POST_SKILL_DURATION_MS = 1000; // Time for all POST
+const POST_SKILL_DURATION_MS = 1300; // Time for all POST
const SOURCE_AND_TARGET_TOTAL_DURATION = TARGET_DELAY_MS + TARGET_DURATION_MS; // SOURCE + TARGET time
module.exports = {
@@ -24,22 +24,18 @@ module.exports = {
},
INFO: {
- vbox: {
- item: 'VBOX',
- description:
ITEMS that are available to buy.
- The VBOX is refilled every round. Click REFILL at the bottom to purchase a refill.
,
+ store: {
+ item: 'STORE',
+ description:
Contains items that are available to buy.
+ The store is refilled every round. Click REFILL to purchase a refill for 2 bits.
,
},
- inventory: {
- item: 'INVENTORY',
+ stash: {
+ item: 'STASH',
description:
Holds ITEMS ITEMS carry over each round.
,
},
bits: {
item: 'BITS',
- description:
The VBOX currency.
- Colours - 1b
- Skills - 2b
- Specs - 3b
- At the beginning of each round you receive 30 bits.
,
+ description:
Currency to buy items. At the beginning of each round you receive 30 bits.
,
},
ready: {
item: 'READY',
@@ -49,10 +45,9 @@ module.exports = {
item: 'READY',
description: 'Ready for the game to begin. When all players are ready the first VBOX PHASE begins.',
},
- reclaim: {
- item: 'RECLAIM',
- description:
Reclaim items refunding the listed cost of the item.
- Click to enable and then click the item to reclaim.
,
+ Refund: {
+ item: 'Refund',
+ description: 'Refund the listed cost of a single selected item from the stash.',
},
refill: {
item: 'REFILL',
@@ -60,7 +55,7 @@ module.exports = {
},
constructSkills: {
item: 'SKILLS',
- description: 'Skills are used by constructs in the game phase.\nBase skills can be bought from the VBOX.\nEquip skills from the inventory. Double-click to unequip.',
+ description: 'Skills are used by constructs in the game phase.\nBase skills can be bought from the VBOX.\nEquip skills from the stash. Double-click to unequip.',
},
constructSpecs: {
item: 'SPECS',
diff --git a/client/src/events.jsx b/client/src/events.jsx
index 02c6af1e..f1b7e158 100644
--- a/client/src/events.jsx
+++ b/client/src/events.jsx
@@ -26,6 +26,12 @@ function registerEvents(store) {
return errorToast(msg);
}
+ function clearTutorial() {
+ store.dispatch(actions.setTutorial(null));
+ localStorage.setItem('tutorial-complete', true);
+ }
+
+
function setPing(ping) {
store.dispatch(actions.setPing(ping));
}
@@ -79,7 +85,7 @@ function registerEvents(store) {
const newRes = game.resolved.slice(currentGame.resolved.length);
return eachSeries(newRes, (r, cb) => {
if (!r.event) return cb();
- const timeout = animations.getTime(r.stages);
+ let timeout = animations.getTime(r.stages);
const anims = animations.getObjects(r, game, account);
const text = animations.getText(r);
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
@@ -87,10 +93,12 @@ function registerEvents(store) {
if (r.stages.includes('START_SKILL') && anims.animSource) {
store.dispatch(actions.setAnimSource(anims.animSource));
+ store.dispatch(actions.setAnimText(null));
}
if (r.stages.includes('END_SKILL') && anims.animTarget) {
store.dispatch(actions.setAnimTarget(anims.animTarget));
+ store.dispatch(actions.setAnimText(null));
if (animations.isCbAnim(anims.animSkill)) store.dispatch(actions.setAnimCb(cb));
}
@@ -101,8 +109,9 @@ function registerEvents(store) {
} else {
setTimeout(
() => store.dispatch(actions.setAnimText(text)),
- timeout - TIMES.POST_SKILL_DURATION_MS
+ timeout - TIMES.POST_SKILL_DURATION_MS - 700
);
+ timeout -= 700;
}
}
@@ -110,7 +119,7 @@ function registerEvents(store) {
store.dispatch(actions.setAnimSkill(null));
store.dispatch(actions.setAnimSource(null));
store.dispatch(actions.setAnimTarget(null));
- store.dispatch(actions.setAnimText(null));
+ // store.dispatch(actions.setAnimText(null));
store.dispatch(actions.setAnimFocus([]));
if (r.stages.includes('END_SKILL') && animations.isCbAnim(anims.animSkill)) return true;
return cb();
@@ -179,12 +188,13 @@ function registerEvents(store) {
}
function clearInstance() {
- store.dispatch(actions.setReclaiming(false));
store.dispatch(actions.setActiveSkill(null));
store.dispatch(actions.setInfo(null));
store.dispatch(actions.setItemUnequip([]));
- store.dispatch(actions.setVboxHighlight([]));
- store.dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
+ store.dispatch(actions.setVboxCombiner(null));
+ store.dispatch(actions.setVboxHighlight(null));
+ store.dispatch(actions.setVboxInfo(null));
+ store.dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
}
function setAccountInstances(v) {
@@ -294,6 +304,9 @@ function registerEvents(store) {
startDemo();
}
+ // store.subscribe(setInfo);
+ // store.on('SET_INFO', setInfo);
+
// events.on('SET_PLAYER', setInstance);
// events.on('SEND_SKILL', function skillActive(gameId, constructId, targetConstructId, skill) {
@@ -342,6 +355,7 @@ function registerEvents(store) {
clearInfo,
clearInstance,
clearMtxActive,
+ clearTutorial,
setAccount,
setAccountInstances,
setActiveItem,
diff --git a/client/src/keyboard.jsx b/client/src/keyboard.jsx
index bb525d47..d9cc79ca 100644
--- a/client/src/keyboard.jsx
+++ b/client/src/keyboard.jsx
@@ -6,12 +6,13 @@ function setupKeys(store) {
key.unbind('esc');
key('esc', () => document.activeElement.blur());
- key('esc', () => store.dispatch(actions.setReclaiming(false)));
key('esc', () => store.dispatch(actions.setActiveSkill(null)));
key('esc', () => store.dispatch(actions.setInfo(null)));
key('esc', () => store.dispatch(actions.setItemUnequip([])));
- key('esc', () => store.dispatch(actions.setVboxHighlight([])));
- key('esc', () => store.dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] })));
+ key('esc', () => store.dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] })));
+ key('esc', () => store.dispatch(actions.setVboxHighlight(null)));
+ key('esc', () => store.dispatch(actions.setVboxCombiner(null)));
+ key('esc', () => store.dispatch(actions.setVboxInfo(null)));
key('esc', () => store.dispatch(actions.setMtxActive(null)));
}
diff --git a/client/src/reducers.jsx b/client/src/reducers.jsx
index 1bda4dc7..ef43b31a 100644
--- a/client/src/reducers.jsx
+++ b/client/src/reducers.jsx
@@ -34,7 +34,6 @@ module.exports = {
gameEffectInfo: createReducer(null, 'SET_GAME_EFFECT_INFO'),
email: createReducer(null, 'SET_EMAIL'),
invite: createReducer(null, 'SET_INVITE'),
- info: createReducer(null, 'SET_INFO'),
instance: createReducer(null, 'SET_INSTANCE'),
instanceChat: createReducer(null, 'SET_INSTANCE_CHAT'),
instances: createReducer([], 'SET_INSTANCES'),
@@ -44,10 +43,12 @@ module.exports = {
nav: createReducer(null, 'SET_NAV'),
ping: createReducer(null, 'SET_PING'),
player: createReducer(null, 'SET_PLAYER'),
- reclaiming: createReducer(false, 'SET_RECLAIMING'),
shop: createReducer(false, 'SET_SHOP'),
pvp: createReducer(null, 'SET_PVP'),
+ info: createReducer(null, 'SET_INFO'),
+ comboPreview: createReducer(null, 'SET_COMBO_PREVIEW'),
+
subscription: createReducer(null, 'SET_SUBSCRIPTION'),
team: createReducer([], 'SET_TEAM'),
@@ -57,7 +58,10 @@ module.exports = {
tutorial: createReducer(1, 'SET_TUTORIAL'),
tutorialGame: createReducer(1, 'SET_TUTORIAL_GAME'),
- vboxSelected: createReducer({ shopSelect: [], stashSelect: [] }, 'SET_VBOX_SELECTED'),
+ vboxSelected: createReducer({ storeSelect: [], stashSelect: [] }, 'SET_VBOX_SELECTED'),
+ vboxCombiner: createReducer(null, 'SET_VBOX_COMBINER'),
+ vboxHighlight: createReducer(null, 'SET_VBOX_HIGHLIGHT'),
+ vboxInfo: createReducer(null, 'SET_VBOX_INFO'),
ws: createReducer(null, 'SET_WS'),
};
diff --git a/client/src/socket.jsx b/client/src/socket.jsx
index a9ec07ba..a99fe130 100644
--- a/client/src/socket.jsx
+++ b/client/src/socket.jsx
@@ -2,6 +2,7 @@ const toast = require('izitoast');
const cbor = require('borc');
const throttle = require('lodash/throttle');
+const groupBy = require('lodash/groupBy');
const SOCKET_URL =
`${window.location.protocol === 'https:' ? 'wss://' : 'ws://'}${window.location.host}/api/ws`;
@@ -77,13 +78,13 @@ function createSocket(events) {
send(['InstanceChat', { instance_id: instanceId, index }]);
}
- function sendVboxAccept(instanceId, group, index) {
- send(['VboxAccept', { instance_id: instanceId, group, index }]);
+ function sendVboxBuy(instanceId, group, index) {
+ send(['VboxBuy', { instance_id: instanceId, group, index }]);
events.clearInstance();
}
- function sendVboxAcceptEquip(instanceId, group, index, constructId) {
- send(['VboxAcceptEquip', { instance_id: instanceId, group, index, construct_id: constructId }]);
+ function sendVboxBuyEquip(instanceId, group, index, constructId) {
+ send(['VboxBuy', { instance_id: instanceId, group, index, construct_id: constructId }]);
events.clearInstance();
}
@@ -102,18 +103,20 @@ function createSocket(events) {
events.clearInstance();
}
- function sendVboxDiscard(instanceId) {
- send(['VboxDiscard', { instance_id: instanceId }]);
+ function sendVboxRefill(instanceId) {
+ send(['VboxRefill', { instance_id: instanceId }]);
events.clearInstance();
}
function sendVboxCombine(instanceId, invIndicies, vboxIndicies) {
- send(['VboxCombine', { instance_id: instanceId, inv_indices: invIndicies, vbox_indices: vboxIndicies }]);
+ const formatted = {};
+ vboxIndicies.forEach(p => formatted[p[0]] ? formatted[p[0]].push(p[1]) : formatted[p[0]] = [p[1]]);
+ send(['VboxCombine', { instance_id: instanceId, inv_indices: invIndicies, vbox_indices: formatted }]);
events.clearInstance();
}
- function sendVboxReclaim(instanceId, index) {
- send(['VboxReclaim', { instance_id: instanceId, index }]);
+ function sendVboxRefund(instanceId, index) {
+ send(['VboxRefund', { instance_id: instanceId, index }]);
events.clearInstance();
}
@@ -410,12 +413,12 @@ function createSocket(events) {
sendInstanceChat,
sendInstanceLeave,
- sendVboxAccept,
- sendVboxAcceptEquip,
+ sendVboxBuy,
+ sendVboxBuyEquip,
sendVboxApply,
- sendVboxReclaim,
+ sendVboxRefund,
sendVboxCombine,
- sendVboxDiscard,
+ sendVboxRefill,
sendVboxUnequip,
sendVboxUnequipApply,
diff --git a/client/src/tutorial.utils.jsx b/client/src/tutorial.utils.jsx
index a06cede2..740aa867 100644
--- a/client/src/tutorial.utils.jsx
+++ b/client/src/tutorial.utils.jsx
@@ -24,21 +24,26 @@ function tutorialVbox(player, store, tutorial) {
if (vbox.bits < 29) {
stage += 1;
} else {
- vbox.free[0] = vbox.free[0].slice(0, 2);
- vbox.free[1] = [];
- vbox.free[2] = [];
- vbox.bound.fill(null, 0, 3);
+ for (let i = 2; i < 6; i += 1) {
+ delete vbox.store.Colours[i];
+ }
+ vbox.store.Skills = {};
+ vbox.store.Specs = {};
+ delete vbox.stash[0];
+ delete vbox.stash[1];
+ delete vbox.stash[2];
}
}
if (stage === 2) {
- if (!(vbox.bound.slice(0, 3).every(i => i === 'Attack') && vbox.bound.length >= 3)) {
+ if (!(vbox.stash[0] === 'Attack' && vbox.stash[1] === 'Attack' && vbox.stash[2] === 'Attack')) {
stage += 1;
} else {
- vbox.free[0] = vbox.free[0].slice(0, 2);
- vbox.free[1] = [];
- vbox.free[2] = [];
- vbox.bound.fill(null, 1, 3);
+ vbox.store.Colours = {};
+ vbox.store.Skills = {};
+ vbox.store.Specs = {};
+ delete vbox.stash[0];
+ delete vbox.stash[1];
}
}
@@ -46,21 +51,24 @@ function tutorialVbox(player, store, tutorial) {
if (player.constructs[0].skills.length !== 0) {
stage += 1;
} else {
- vbox.free[0] = vbox.free[0].slice(0, 2);
- vbox.free[1] = [];
- vbox.free[2] = [];
- vbox.bound.fill(null, 0, 2);
+ vbox.store.Colours = {};
+ vbox.store.Skills = {};
+ vbox.store.Specs = {};
+ delete vbox.stash[0];
+ delete vbox.stash[1];
}
}
if (stage === 4) {
- if (!vbox.free[2][0] || vbox.bits < 24) {
+ if (!vbox.store.Specs[0] || vbox.bits < 24) {
stage += 1;
} else {
- vbox.free[0] = [];
- vbox.free[1] = [];
- vbox.free[2] = vbox.free[2].slice(0, 1);
- vbox.bound.fill(null, 0, 2);
+ vbox.store.Colours = {};
+ vbox.store.Skills = {};
+ delete vbox.store.Specs[1];
+ delete vbox.store.Specs[2];
+ delete vbox.stash[0];
+ delete vbox.stash[1];
}
}
@@ -68,10 +76,11 @@ function tutorialVbox(player, store, tutorial) {
if (player.constructs[0].specs.length !== 0) {
stage += 1;
} else {
- vbox.free[0] = [];
- vbox.free[1] = [];
- vbox.free[2] = vbox.free[2].slice(0, 1);
- vbox.bound.fill(null, 0, 2);
+ vbox.store.Colours = {};
+ vbox.store.Skills = {};
+ vbox.store.Specs = {};
+ delete vbox.stash[0];
+ delete vbox.stash[1];
}
}
@@ -79,9 +88,9 @@ function tutorialVbox(player, store, tutorial) {
if (player.constructs.every(c => c.skills.length !== 0)) {
stage += 1;
} else {
- vbox.free[0] = [];
- vbox.free[1] = [];
- vbox.free[2] = [];
+ vbox.store.Colours = {};
+ vbox.store.Skills = {};
+ vbox.store.Specs = {};
}
}
@@ -89,22 +98,18 @@ function tutorialVbox(player, store, tutorial) {
if (vbox.bits < 25) {
stage += 1;
} else {
- vbox.free[0] = [];
- vbox.free[1] = [];
- vbox.free[2] = [];
+ vbox.store.Colours = {};
+ vbox.store.Skills = {};
+ vbox.store.Specs = {};
}
}
store.dispatch(actions.setTutorial(stage));
}
-function tutorialStage(tutorial, ws, clearTutorial, instance) {
+function tutorialStage(tutorial, clearTutorial, instance) {
if (!(instance.time_control === 'Practice' && instance.rounds.length === 1)) return false;
- const exit = () => {
- clearTutorial();
- localStorage.setItem('tutorial-complete', true);
- ws.sendInstanceState(instance.id);
- };
+ const exit = () => clearTutorial();
const tutorialText = () => {
if (tutorial === 1) {
@@ -113,7 +118,7 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
Tutorial
Welcome to the vbox phase tutorial.
Colours are used to create powerful combinations with base items.
-
Buy the two colours from the vbox to continue.
+
Buy the two colours from the store to continue.
);
}
@@ -122,11 +127,8 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
return (
);
}
@@ -138,8 +140,8 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
Skill items can be equipped to your constructs to be used in the combat phase.
Click the newly combined skill item in the top right of the inventory.
- Once selected click the construct SKILL slot to equip the skill.
Click your new skill from the stash.
+ Once selected click the flashing SKILL slot to equip the skill.