mnml/client/src/components/instance.constructs.jsx
2021-03-20 13:33:05 +10:00

291 lines
9.4 KiB
JavaScript

const { connect } = require('preact-redux');
const preact = require('preact');
const range = require('lodash/range');
const buttons = require('./buttons');
const shapes = require('./shapes');
const { OFFENSE, DEFENSE } = require('../utils');
const { ConstructAvatar } = require('./construct');
const actions = require('../actions');
const { removeTier } = require('../utils');
const { tutorialConstructDisplay, tutorialShouldDisableEquip } = require('../tutorial.utils.jsx');
const addState = connect(
function receiveState(state) {
const {
ws,
instance,
player,
account,
itemInfo,
itemUnequip,
vboxSelected,
tutorial,
} = state;
function sendVboxBuyEquip(constructId) {
return ws.sendVboxBuyEquip(instance.id, vboxSelected.storeSelect[0][0], vboxSelected.storeSelect[0][1], constructId);
}
function sendVboxApply(constructId, i) {
return ws.sendVboxApply(instance.id, constructId, i);
}
function sendVboxUnequipApply(targetConstructId) {
return ws.sendVboxUnequipApply(instance.id, itemUnequip[0], itemUnequip[1], targetConstructId);
}
return {
instance,
player,
account,
sendVboxBuyEquip,
sendVboxUnequipApply,
sendVboxApply,
itemInfo,
itemUnequip,
vboxSelected,
tutorial,
};
},
function receiveDispatch(dispatch) {
function quit() {
dispatch(actions.setInstance(null));
}
function setInfo(item) {
dispatch(actions.setInfo(item));
}
function setItemUnequip(v) {
const info = v.length ? v[1] : null;
dispatch(actions.setVboxInfo(info));
dispatch(actions.setVboxCombiner(null));
dispatch(actions.setVboxHighlight(false));
dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
return dispatch(actions.setItemUnequip(v));
}
return { quit, setInfo, setItemUnequip };
}
);
function Construct(props) {
const {
// Changing state variables
construct,
iter,
itemUnequip,
instance,
player,
vboxSelected,
tutorial,
// Static Info
itemInfo,
// Function Calls
sendVboxApply,
sendVboxBuyEquip,
sendVboxUnequipApply,
setItemUnequip,
setInfo,
} = props;
const { vbox } = player;
const itemEquip = vboxSelected.storeSelect.length === 0 && vboxSelected.stashSelect.length === 1
? vboxSelected.stashSelect[0]
: -1;
const duplicateSkill = construct.skills.length !== 0 && construct.skills.every(sk => {
if (!itemEquip && itemEquip !== 0) return false;
if (!sk) return false;
return sk.skill === vbox.stash[itemEquip];
});
const tutorialDisableEquip = tutorialShouldDisableEquip(tutorial, iter, instance, construct);
function onClick(e) {
e.stopPropagation();
e.preventDefault();
if (duplicateSkill || tutorialDisableEquip) return true;
if (itemEquip !== -1) return sendVboxApply(construct.id, itemEquip);
if (vboxSelected.storeSelect.length === 1) return sendVboxBuyEquip(construct.id);
if (itemUnequip.length && itemUnequip[0] !== construct.id) return sendVboxUnequipApply(construct.id);
setItemUnequip([]);
return true;
}
function hoverInfo(e, info) {
e.stopPropagation();
if (!info) return false;
if (vboxSelected.storeSelect.length || vboxSelected.stashSelect.length) return false;
return setInfo(info);
}
const skillList = itemInfo.items.filter(v => v.skill).map(v => v.item);
const specList = itemInfo.items.filter(v => v.spec).map(v => v.item);
const skills = range(0, 1).map(i => {
const skill = construct.skills[i];
const s = skill
? skill.skill
: (<span class="gray">SKILL</span>);
function skillClick(e) {
if (!skill) return false;
e.stopPropagation();
if (itemUnequip.length && itemUnequip[0] === construct.id && skill.skill === itemUnequip[1]
&& i === itemUnequip[2]) return setItemUnequip([]);
setItemUnequip([construct.id, skill.skill, i]);
return true;
}
const equipping = skillList.includes(vbox.stash[itemEquip]) && !skill
&& !tutorialDisableEquip && !duplicateSkill && i === construct.skills.length;
const border = () => {
if (!skill) return '';
const borderFn = buttons[removeTier(skill.skill)];
if (!borderFn) return '';
return borderFn();
};
const highlight = itemUnequip[0] === construct.id && itemUnequip[1] === s ? 'highlight' : '';
const classes = `${highlight} ${equipping ? 'equipping' : ''} ${!skill ? 'empty' : ''} ${border()}`;
return (
<label onDragStart={ev => {
ev.dataTransfer.setData('text', '');
skillClick(ev);
}} key={i} draggable={skill} onDragEnd={() => setItemUnequip([])}>
<button
key={i}
disabled={!skill && !equipping}
class={classes}
onClick={skillClick}
onMouseOver={e => hoverInfo(e, skill && skill.skill)} >
{s}
</button>
</label>
);
});
const colours = () => {
return (
<div class="colours" onMouseOver={e => hoverInfo(e, 'constructSpecs')} >
<div> {shapes.Red()} {construct.colours.red} </div>
<div> {shapes.Blue()} {construct.colours.blue} </div>
<div> {shapes.Green()} {construct.colours.green} </div>
</div>
);
};
const offensiveStats = Object.keys(OFFENSE).map(s => {
const stat = OFFENSE[s];
const info = (s === 'SpeedStat' && 'speedStat')
|| (s.includes('Power') && 'powerStat');
return <div key={stat.stat}
alt={stat.stat}
class={stat.stat}
onMouseOver={e => hoverInfo(e, info)} >
{shapes[s]()}
<div>{construct[stat.stat].value}</div>
</div>;
});
const defensiveStats = Object.keys(DEFENSE).map(s => {
const stat = DEFENSE[s];
const info = (s.includes('Life') && 'lifeStat');
return <div key={stat.stat}
alt={stat.stat}
class={stat.stat}
onMouseOver={e => hoverInfo(e, info)} >
{shapes[s]()}
<div>{construct[stat.stat].value}</div>
</div>;
});
const classes = 'instance-construct';
const avatarMouseOver = e => hoverInfo(e, `constructAvatar ${construct.name}`);
return (
<div key={construct.id} class={classes} onClick={onClick} onDragOver={ev => ev.preventDefault()} onDrop={onClick}>
<ConstructAvatar construct={construct} mouseOver={avatarMouseOver}/>
<h2 class="name" onMouseOver={e => hoverInfo(e, `constructName ${construct.name}`)}>{construct.name}</h2>
<div class="skills" onMouseOver={e => hoverInfo(e, 'constructSkills')} >
{skills}
</div>
{colours()}
<div class="offStats">
{offensiveStats}
</div>
<div class="defStats">
{defensiveStats}
</div>
</div>
);
}
class InstanceConstructs extends preact.Component {
shouldComponentUpdate(newProps) {
if (newProps.itemUnequip !== this.props.itemUnequip) return true;
if (newProps.tutorial !== this.props.tutorial) return true;
// JSON or Array objects
if (newProps.player !== this.props.player) return true;
if (newProps.instance !== this.props.instance) return true;
if (newProps.vboxSelected !== this.props.vboxSelected) return true;
return false;
}
render(props) {
const {
// Changing state variables
itemUnequip,
instance,
player,
tutorial,
vboxSelected,
// Static data
itemInfo,
// Function calls
setInfo,
sendVboxApply,
sendVboxBuyEquip,
sendVboxUnequipApply,
setItemUnequip,
} = props;
if (!player) return false;
if (instance.phase === 'Lobby') return false;
const constructs = range(0, 3).map(i => {
const tutorialConstruct = tutorialConstructDisplay(player, instance, tutorial, i);
if (tutorialConstruct) return (tutorialConstruct);
return Construct({
iter: i,
construct: player.constructs[i],
itemUnequip,
instance,
setItemUnequip,
player,
sendVboxApply,
sendVboxBuyEquip,
sendVboxUnequipApply,
setInfo,
itemInfo,
vboxSelected,
tutorial,
});
});
return (
<div class='construct-list'>
{constructs}
</div>
);
}
}
module.exports = addState(InstanceConstructs);