Merge branch 'develop' into svt

This commit is contained in:
ntr 2019-09-12 12:47:19 +10:00
commit 937fdae92e
66 changed files with 533 additions and 191 deletions

View File

@ -8,6 +8,13 @@ minimal studios is ntr & mashy: 2 mates with a friendship forged in the fires of
we have both bailed out of the big city life and have dedicated ourselves to growing farm fresh, organic, ethical gaming produce in the rolling hills of brisbane and leaves of melbourne. we have both bailed out of the big city life and have dedicated ourselves to growing farm fresh, organic, ethical gaming produce in the rolling hills of brisbane and leaves of melbourne.
completely self funded, we're just here to make games that feel good & play it clean. completely self funded, we're just here to make games that feel good & play it clean.
## inspiration
- peggle
- soldat
- mini metro
- poe
- mtg
## Construct Alignments ## Construct Alignments

View File

@ -19,11 +19,8 @@
* msg pane * msg pane
* game invites * game invites
* change score to enum
* pct based translates for combat animation
* add speed to descriptions * add speed to descriptions
* clear skill (if currently targetted)
## SOON ## SOON
*SERVER* *SERVER*

View File

@ -77,6 +77,12 @@
} }
} }
} }
.login {
display: flex;
flex-flow: column;
margin-bottom: 2em;
}
} }
section { section {
@ -108,4 +114,60 @@ section {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
} }
}
.demo {
margin-top: 1em;
display: grid;
grid-template-areas:
"vinfo game"
"vcons game";
grid-template-columns: 1fr 1fr;
grid-template-rows: min-content 1fr;
.colour-info {
grid-area: vinfo;
display: flex;
align-items: center;
div {
display: flex;
}
svg {
flex: 1;
height: 1em;
}
}
.vbox-demo {
grid-area: vinfo;
}
.game-demo {
grid-area: game;
display: grid;
grid-template-columns: 1fr 2fr;
.game {
display: flex;
flex-flow: column;
.game-construct {
flex: 1;
}
}
}
.construct-list {
grid-area: vcons;
height: 100%;
svg {
height: 100%;
}
}
} }

View File

@ -112,7 +112,7 @@ button, input {
flex: 1; flex: 1;
/*the transitions */ /*the transitions */
transition-property: color, background; transition-property: border-color, color, background;
transition-duration: 0.25s; transition-duration: 0.25s;
transition-delay: 0; transition-delay: 0;
transition-timing-function: ease; transition-timing-function: ease;
@ -195,28 +195,6 @@ button[disabled] {
border-color: #222; border-color: #222;
} }
/*
LOGIN
*/
.welcome {
.login {
width: 50%;
display: flex;
flex-flow: column;
margin-bottom: 2em;
}
.options {
display: flex;
width: 50%;
}
h2 {
margin-bottom: 0.5em;
}
}
#mnml input, #mnml select { #mnml input, #mnml select {
border-color: #222; border-color: #222;
background-color: #222; background-color: #222;
@ -272,14 +250,6 @@ header {
color: @white; color: @white;
box-shadow: inset 0px 5px 0px 0px @white; box-shadow: inset 0px 5px 0px 0px @white;
border: 0; border: 0;
&:first-child {
border-left: 1px solid #444;
}
&:last-child {
border-right: 1px solid #444;
}
} }
border: 1px solid #444; border: 1px solid #444;

View File

@ -8,6 +8,8 @@ export const setAnimSource = value => ({ type: 'SET_ANIM_SOURCE', value });
export const setAnimTarget = value => ({ type: 'SET_ANIM_TARGET', value }); export const setAnimTarget = value => ({ type: 'SET_ANIM_TARGET', value });
export const setAnimText = value => ({ type: 'SET_ANIM_TEXT', value }); export const setAnimText = value => ({ type: 'SET_ANIM_TEXT', value });
export const setDemo = value => ({ type: 'SET_DEMO', value });
export const setActiveItem = value => ({ type: 'SET_ACTIVE_VAR', value }); export const setActiveItem = value => ({ type: 'SET_ACTIVE_VAR', value });
export const setActiveSkill = (constructId, skill) => ({ type: 'SET_ACTIVE_SKILL', value: constructId ? { constructId, skill } : null }); export const setActiveSkill = (constructId, skill) => ({ type: 'SET_ACTIVE_SKILL', value: constructId ? { constructId, skill } : null });
export const setCombiner = value => ({ type: 'SET_COMBINER', value: Array.from(value) }); export const setCombiner = value => ({ type: 'SET_COMBINER', value: Array.from(value) });

View File

@ -68,6 +68,7 @@ class ConstructAnimation extends Component {
const animSkill = removeTier(skill); const animSkill = removeTier(skill);
if (!constructId.includes(construct.id)) return false; if (!constructId.includes(construct.id)) return false;
// find target animation // find target animation
const chooseAnim = (animSkill) => { const chooseAnim = (animSkill) => {
switch (animSkill) { switch (animSkill) {

View File

@ -90,7 +90,7 @@ class Absorb extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -78,7 +78,7 @@ class Amplify extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -59,7 +59,7 @@ class Attack extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -100,7 +100,7 @@ class Bash extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -103,7 +103,7 @@ class Bash extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -88,7 +88,7 @@ class Blast extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -53,7 +53,7 @@ class Block extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -107,7 +107,7 @@ class Break extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -81,7 +81,7 @@ class Buff extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -69,10 +69,9 @@ class Chaos extends Component {
stroke: colour, stroke: colour,
}); });
}); });
anime.set('.skill-anim', { anime.set('.skill-anim', {
translateY: -300 * this.props.direction.y, translateY: -(window.screen.height) * 0.35 * this.props.direction.y,
translateX: -200 * this.props.direction.x, translateX: -(window.screen.width) * 0.15 * this.props.direction.x,
opacity: 0, opacity: 0,
}); });
anime.set('#explosion feDisplacementMap', { anime.set('#explosion feDisplacementMap', {
@ -120,7 +119,7 @@ class Chaos extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -85,7 +85,7 @@ class Counter extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -78,7 +78,7 @@ class Curse extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -83,7 +83,7 @@ class Debuff extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -68,7 +68,7 @@ class Decay extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -109,7 +109,7 @@ class Electrify extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -101,7 +101,7 @@ class Electrocute extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -83,7 +83,7 @@ class Haste extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -81,7 +81,7 @@ class Heal extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -88,7 +88,7 @@ class Hex extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -153,7 +153,7 @@ class Hybrid extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -96,7 +96,7 @@ class Intercept extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -91,7 +91,7 @@ class Link extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -81,7 +81,7 @@ class Purge extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -115,7 +115,7 @@ class Purify extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -95,7 +95,7 @@ class Recharge extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -76,7 +76,7 @@ class Block extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -82,7 +82,7 @@ class Intercept extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -77,7 +77,7 @@ class Refl extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -102,7 +102,7 @@ class Restrict extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -100,7 +100,7 @@ class Ruin extends Component {
this.animations[i].reset(); this.animations[i].reset();
} }
try { try {
this.props.animCb(); this.props.animCb && this.props.animCb();
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }

View File

@ -97,7 +97,7 @@ class Silence extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -69,9 +69,8 @@ class Siphon extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Siphon); module.exports = addState(Siphon);

View File

@ -116,7 +116,7 @@ class SiphonTick extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -91,7 +91,7 @@ class Slay extends Component {
}); });
anime.set('#slay', { anime.set('#slay', {
translateY: -400, translateY: (window.screen.height) * 0.35 * this.props.direction.y,
translateX: 0, translateX: 0,
}); });
anime.set('#explosion feDisplacementMap', { anime.set('#explosion feDisplacementMap', {
@ -152,7 +152,7 @@ class Slay extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -47,7 +47,7 @@ class Sleep extends Component {
version="1.1" version="1.1"
id="sleep" id="sleep"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="-291 -191 582 582"> viewBox="-291 -291 582 582">
<defs> <defs>
<filter id="sleepFilter"> <filter id="sleepFilter">
<feGaussianBlur stdDeviation="3"/> <feGaussianBlur stdDeviation="3"/>
@ -140,7 +140,7 @@ class Sleep extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -85,7 +85,7 @@ class Strike extends Component {
}); });
anime.set('#strike', { anime.set('#strike', {
translateY: -400, translateY: (window.screen.height) * 0.35 * this.props.direction.y,
translateX: 0, translateX: 0,
}); });
@ -120,7 +120,7 @@ class Strike extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -78,7 +78,7 @@ class Stun extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -127,7 +127,7 @@ class Sustain extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -80,7 +80,7 @@ class Triage extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -79,7 +79,7 @@ class TriageTick extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }

View File

@ -0,0 +1,179 @@
const { connect } = require('preact-redux');
const preact = require('preact');
const actions = require('../actions');
const shapes = require('./shapes');
const { ConstructAvatar } = require('./construct');
const { ConstructAnimation } = require('./animations');
const addState = connect(
function receiveState(state) {
const {
account,
itemInfo,
demo,
} = state;
return {
account,
itemInfo,
demo,
};
},
function receiveDispatch(dispatch) {
function setAnimTarget(anim) {
dispatch(actions.setAnimTarget(anim));
}
return { setAnimTarget };
}
);
function Demo(args) {
const {
demo,
itemInfo,
account,
setAnimTarget,
} = args;
if (!demo || !itemInfo.items.length || account) return false;
const { combiner, items, equipping, equipped, players } = demo;
console.log(items);
const vboxDemo = () => {
function inventoryBtn(i, j) {
if (!i) return <button disabled class='empty' >&nbsp;</button>;
const highlighted = combiner.indexOf(j) > -1;
const classes = `${highlighted ? 'highlight' : ''}`;
if (shapes[i]) {
return <button class={classes} key={j}>{shapes[i]()}</button>;
}
return <button class={classes}>{i}</button>;
}
function combinerBtn() {
let text = '';
if (combiner.length < 3) {
for (let i = 0; i < 3; i++) {
if (combiner.length > i) {
text += '■ ';
} else {
text += '▫ ';
}
}
} else {
text = 'combine';
}
return (
<button
class='vbox-btn'
disabled={combiner.length !== 3}>
{text}
</button>
);
}
function inventoryElement() {
return (
<div class="vbox">
<div class='vbox-section'>
<h2 class='colour-info'>
VBOX PHASE {shapes.Red()} {shapes.Green()} {shapes.Blue()}
</h2>
<p>
Combine the colour base items with an array of skills and specialisations to build powerful variants.
</p>
</div>
<div>&nbsp;</div>
<div class='vbox-section'>
<div class='vbox-items'>
{items.map((i, j) => inventoryBtn(i, j))}
</div>
{combinerBtn()}
</div>
</div>
);
}
return (
<div class="news vbox-demo">
{inventoryElement()}
</div>
);
};
const vboxConstructs = () => {
const btnClass = equipping
? 'equipping empty gray'
: 'empty gray';
return (
<div class='news construct-list'>
{players[0].constructs.map((c, i) => (
<div class="instance-construct" key={i}>
<h2 class="name" >{c.name}</h2>
<ConstructAvatar construct={c} />
<div class="skills">
{i === 0 && equipped
? <button>Strike</button>
: <button disabled={!equipping} class={btnClass}>SKILL</button>
}
<button disabled={!equipping} class={btnClass}>SKILL</button>
<button disabled={!equipping} class={btnClass}>SKILL</button>
</div>
<div class="specs">
</div>
<div class="stats">
</div>
</div>
))}
</div>
);
};
const gameDemo = () => {
return (
<div class="game-demo">
<div>
<h2>COMBAT PHASE</h2>
<p>Battle your opponent using dynamic team builds from the VBOX phase.</p>
<p>Crafted skills can be used to damage the opponent or support your team.</p>
<p>Turn based combat, each team picks targets for their skills during this phase.</p>
<p>The damage dealt by skills, cast order and construct life depend on your decisions in the VBOX phase. </p>
</div>
<div class="game">
<div class="game-construct">
<ConstructAvatar construct={players[1].constructs[0]} />
<ConstructAnimation construct={players[1].constructs[0]} />
</div>
<div></div>
<div class="game-construct">
<ConstructAvatar construct={players[1].constructs[1]} />
<ConstructAnimation construct={players[1].constructs[1]} />
</div>
</div>
</div>
);
};
return (
<section class='demo news'>
{vboxDemo()}
{vboxConstructs()}
{gameDemo()}
</section>
);
}
module.exports = addState(Demo);

View File

@ -27,8 +27,13 @@ const addState = connect(
return ws.sendGameSkillClear(game.id); return ws.sendGameSkillClear(game.id);
} }
function sendAbandon() {
return ws.sendInstanceAbandon(game.instance);
}
return { return {
game, game,
sendAbandon,
sendGameSkillClear, sendGameSkillClear,
sendReady, sendReady,
account, account,
@ -51,6 +56,7 @@ const addState = connect(
function Controls(args) { function Controls(args) {
const { const {
account, account,
sendAbandon,
game, game,
animating, animating,
sendGameSkillClear, sendGameSkillClear,
@ -104,7 +110,7 @@ function Controls(args) {
<div class="controls"> <div class="controls">
<PlayerBox player={opponent}/> <PlayerBox player={opponent}/>
{game.phase === 'Finish' ? quitBtn : readyBtn} {game.phase === 'Finish' ? quitBtn : readyBtn}
<PlayerBox player={player} isPlayer={true} isGame={true} clear={sendGameSkillClear}/> <PlayerBox player={player} isPlayer={true} isGame={true} clear={sendGameSkillClear} abandon={sendAbandon}/>
</div> </div>
</aside> </aside>
); );

View File

@ -42,7 +42,6 @@ const addState = connect(
function receiveDispatch(dispatch) { function receiveDispatch(dispatch) {
function setActiveSkill(constructId, skill) { function setActiveSkill(constructId, skill) {
dispatch(actions.setActiveSkill(constructId, skill)); dispatch(actions.setActiveSkill(constructId, skill));
// particlesJS(`particles-${constructId}`, config);
} }
function setActiveConstruct(construct) { function setActiveConstruct(construct) {

View File

@ -21,15 +21,14 @@ function InfoComponent(args) {
return ( return (
<div> <div>
<h2>VBOX phase</h2> <h2>VBOX phase</h2>
<p>in this phase you strengthen and specialise your constructs by equipping items to them.</p> <p>strengthen and specialise your constructs by equipping items to them.</p>
<p>double clicking items in the <b>VBOX</b> will purchase and move them to your <b>INVENTORY</b>.</p> <p>double click to purchase items in the <b>VBOX</b> and move them to your <b>INVENTORY</b>.</p>
<p> <p>
hover over an item to see its effects and combinations.<br />
combine a <b>SKILL</b> or <b>SPEC</b> with 2 <b>COLOURS</b> to create an item.<br /> combine a <b>SKILL</b> or <b>SPEC</b> with 2 <b>COLOURS</b> to create an item.<br />
combine 3 of the same item to upgrade it.<br /> combine <b>3 of the same item</b> to upgrade it.<br />
click an item and then click a construct to equip that item to it.<br /> click an item and then click a construct to <b>equip</b> that item to it.<br />
</p> </p>
<p>click the <b>READY</b> button on the right to progress to the <b>GAME PHASE</b>.</p> <p>click the <b>READY</b> button for the <b>GAME PHASE</b>.</p>
</div> </div>
); );
} }

View File

@ -179,7 +179,7 @@ function Construct(props) {
const stats = Object.keys(STATS).map(s => { const stats = Object.keys(STATS).map(s => {
const stat = STATS[s]; const stat = STATS[s];
const info = (s === 'Speed' && 'Speed') const info = (s === 'SpeedStat' && 'Speed')
|| (s.includes('Power') && 'Power') || (s.includes('Power') && 'Power')
|| (s.includes('Life') && 'Life'); || (s.includes('Life') && 'Life');

View File

@ -15,12 +15,16 @@ const addState = connect(
} = state; } = state;
function sendReady() { function sendReady() {
document.activeElement.blur() document.activeElement.blur();
return ws.sendInstanceReady(instance.id); return ws.sendInstanceReady(instance.id);
return false; }
function sendAbandon() {
return ws.sendInstanceAbandon(instance.id);
} }
return { return {
sendAbandon,
instance, instance,
sendReady, sendReady,
account, account,
@ -41,6 +45,7 @@ const addState = connect(
function Controls(args) { function Controls(args) {
const { const {
account, account,
sendAbandon,
instance, instance,
sendReady, sendReady,
leave, leave,
@ -77,13 +82,18 @@ function Controls(args) {
</div> </div>
); );
const ready = instance.phase !== 'Finished'
? <button class="ready" onClick={() => sendReady()}>Ready</button>
: <button class="ready" onClick={leave}>Leave</button>
const abandon = instance.phase !== 'Finished' ? sendAbandon : false;
return ( return (
<aside> <aside>
{timer} {timer}
<div class="controls"> <div class="controls">
<PlayerBox player={opponent} /> <PlayerBox player={opponent} />
<button class="ready" onClick={() => sendReady()}>Ready</button> {ready}
<PlayerBox player={player} isPlayer={true} leave={leave}/> <PlayerBox player={player} isPlayer={true} abandon={abandon}/>
</div> </div>
</aside> </aside>
); );

View File

@ -1,18 +1,11 @@
const preact = require('preact'); const preact = require('preact');
module.exports = function molecule(combatText) { module.exports = function molecule() {
const text = combatText return (
? <text x="0" y="400" class="combat-text"> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 500 500">
{combatText} <rect style="fill: none;"></rect>
<animate attributeType="XML" attributeName="y" from="300" to="200" dur="2s" repeatCount="1"/> <g transform="translate(-133.97462520598594, -116.025374794014) "><g><line x1="325.9602631261639" x2="296.95475676906796" y1="332.53171849251754" y2="315.78489034176926" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="267.949250411972" x2="296.95475676906796" y1="299.038062191021" y2="315.78489034176926" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="383.9712758403558" x2="383.9712758403558" y1="433.01268739700697" y2="466.5063436985035" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="383.9712758403558" x2="383.9712758403558" y1="500" y2="466.5063436985035" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="383.9712758403558" x2="383.9712758403558" y1="299.038062191021" y2="265.5444058895245" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="383.9712758403558" x2="383.9712758403558" y1="232.050749588028" y2="265.5444058895245" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="500" x2="470.994493642904" y1="433.01268739700697" y2="416.26585924625874" transform="" style="stroke: #1FF01F; stroke-width: 3; stroke-linecap: butt;"></line><line x1="441.98898728580804" x2="470.994493642904" y1="399.5190310955105" y2="416.26585924625874" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="441.98898728580804" x2="470.994493642904" y1="332.53171849251754" y2="315.78489034176926" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="500" x2="470.994493642904" y1="299.038062191021" y2="315.78489034176926" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="325.9602631261639" x2="296.95475676906796" y1="399.5190310955105" y2="416.26585924625874" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="267.949250411972" x2="296.95475676906796" y1="433.01268739700697" y2="416.26585924625874" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="383.9712758403558" x2="354.96576948325986" y1="299.038062191021" y2="315.78489034176926" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="325.9602631261639" x2="354.96576948325986" y1="332.53171849251754" y2="315.78489034176926" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="441.98898728580804" x2="412.98013156308195" y1="332.53171849251754" y2="315.78489034176926" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="383.9712758403558" x2="412.98013156308195" y1="299.038062191021" y2="315.78489034176926" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="325.9602631261639" x2="325.9602631261639" y1="332.53171849251754" y2="366.02537479401406" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="325.9602631261639" x2="325.9602631261639" y1="399.5190310955105" y2="366.02537479401406" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="383.9712758403558" x2="412.98013156308195" y1="433.01268739700697" y2="416.26585924625874" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="441.98898728580804" x2="412.98013156308195" y1="399.5190310955105" y2="416.26585924625874" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="325.9602631261639" x2="354.96576948325986" y1="399.5190310955105" y2="416.26585924625874" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="383.9712758403558" x2="354.96576948325986" y1="433.01268739700697" y2="416.26585924625874" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="441.98898728580804" x2="441.98898728580804" y1="399.5190310955105" y2="366.02537479401406" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="441.98898728580804" x2="441.98898728580804" y1="332.53171849251754" y2="366.02537479401406" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g class="atom" transform="translate(267.949250411972, 299.038062191021) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(383.9712758403558, 500) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(383.9712758403558, 232.050749588028) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(500, 433.01268739700697) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(500, 299.038062191021) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(267.949250411972, 433.01268739700697) "><circle r="8" style="fill: #1FF01F;"></circle></g>
<animate attributeType="XML" attributeName="opacity" from="1" to="0" dur="2s" repeatCount="1"/> </g>
</text> </svg>
: ''; );
return (<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 500 500">
<rect style="fill: none;"></rect>
<g transform="translate(-133.97462520598594, -116.025374794014) "><g><line x1="325.9602631261639" x2="296.95475676906796" y1="332.53171849251754" y2="315.78489034176926" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="267.949250411972" x2="296.95475676906796" y1="299.038062191021" y2="315.78489034176926" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="383.9712758403558" x2="383.9712758403558" y1="433.01268739700697" y2="466.5063436985035" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="383.9712758403558" x2="383.9712758403558" y1="500" y2="466.5063436985035" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="383.9712758403558" x2="383.9712758403558" y1="299.038062191021" y2="265.5444058895245" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="383.9712758403558" x2="383.9712758403558" y1="232.050749588028" y2="265.5444058895245" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="500" x2="470.994493642904" y1="433.01268739700697" y2="416.26585924625874" transform="" style="stroke: #1FF01F; stroke-width: 3; stroke-linecap: butt;"></line><line x1="441.98898728580804" x2="470.994493642904" y1="399.5190310955105" y2="416.26585924625874" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="441.98898728580804" x2="470.994493642904" y1="332.53171849251754" y2="315.78489034176926" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="500" x2="470.994493642904" y1="299.038062191021" y2="315.78489034176926" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="325.9602631261639" x2="296.95475676906796" y1="399.5190310955105" y2="416.26585924625874" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: butt;"></line><line x1="267.949250411972" x2="296.95475676906796" y1="433.01268739700697" y2="416.26585924625874" transform="" style="stroke-width: 3; stroke: #1FF01F;"></line></g><g><line x1="383.9712758403558" x2="354.96576948325986" y1="299.038062191021" y2="315.78489034176926" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="325.9602631261639" x2="354.96576948325986" y1="332.53171849251754" y2="315.78489034176926" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="441.98898728580804" x2="412.98013156308195" y1="332.53171849251754" y2="315.78489034176926" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="383.9712758403558" x2="412.98013156308195" y1="299.038062191021" y2="315.78489034176926" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="325.9602631261639" x2="325.9602631261639" y1="332.53171849251754" y2="366.02537479401406" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="325.9602631261639" x2="325.9602631261639" y1="399.5190310955105" y2="366.02537479401406" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="383.9712758403558" x2="412.98013156308195" y1="433.01268739700697" y2="416.26585924625874" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="441.98898728580804" x2="412.98013156308195" y1="399.5190310955105" y2="416.26585924625874" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="325.9602631261639" x2="354.96576948325986" y1="399.5190310955105" y2="416.26585924625874" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="383.9712758403558" x2="354.96576948325986" y1="433.01268739700697" y2="416.26585924625874" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g><line x1="441.98898728580804" x2="441.98898728580804" y1="399.5190310955105" y2="366.02537479401406" transform="" style="stroke: #909090; stroke-width: 3; stroke-linecap: round;"></line><line x1="441.98898728580804" x2="441.98898728580804" y1="332.53171849251754" y2="366.02537479401406" transform="" style="stroke-width: 3; stroke: #909090;"></line></g><g class="atom" transform="translate(267.949250411972, 299.038062191021) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(383.9712758403558, 500) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(383.9712758403558, 232.050749588028) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(500, 433.01268739700697) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(500, 299.038062191021) "><circle r="8" style="fill: #1FF01F;"></circle></g><g class="atom" transform="translate(267.949250411972, 433.01268739700697) "><circle r="8" style="fill: #1FF01F;"></circle></g>
</g>
{text}
</svg>);
}; };

View File

@ -1,15 +1,24 @@
const preact = require('preact'); const preact = require('preact');
const { connect } = require('preact-redux');
const addState = connect(
function receiveState(state) {
const { animating } = state;
return { animating };
}
);
function Scoreboard(args) { function Scoreboard(args) {
const { const {
abandon,
animating,
isPlayer, isPlayer,
player, player,
isGame, isGame,
clear, clear,
leave,
} = args; } = args;
let scoreText = () => { const scoreText = () => {
if (player.score === 'Zero') return '▫▫▫▫'; if (player.score === 'Zero') return '▫▫▫▫';
if (player.score === 'One') return '■▫▫▫'; if (player.score === 'One') return '■▫▫▫';
if (player.score === 'Two') return '■■▫▫'; if (player.score === 'Two') return '■■▫▫';
@ -37,14 +46,10 @@ function Scoreboard(args) {
<div class="img"> <div class="img">
<div>{player.name}</div> <div>{player.name}</div>
</div> </div>
<div> {(isPlayer && isGame) ? <button disabled={animating} onClick={clear}>Clear</button> : null}
{(isPlayer && isGame) ? <button onClick={clear}>Clear</button> : null} {(abandon) ? <button disabled={animating} onClick={abandon}>Abandon</button> : null}
</div>
<div>
{leave ? <button onClick={leave}>Leave</button> : null}
</div>
</div> </div>
); );
} }
module.exports = Scoreboard; module.exports = addState(Scoreboard);

View File

@ -5,6 +5,7 @@ const Login = require('./welcome.login');
const Register = require('./welcome.register'); const Register = require('./welcome.register');
const Help = require('./welcome.help'); const Help = require('./welcome.help');
const About = require('./welcome.about'); const About = require('./welcome.about');
const Demo = require('./demo');
function Welcome() { function Welcome() {
const page = this.state.page || 'login'; const page = this.state.page || 'login';
@ -19,41 +20,57 @@ function Welcome() {
}; };
return ( return (
<main class="welcome"> <main class="menu">
<h1>mnml.gg</h1> <header>
<div class="login"> <div class="options">
<div>mnml is an abstract turn based strategy game</div> <button
<div>free to play</div> class={`login-btn ${page === 'login' ? 'highlight' : ''}`}
<div>no email required</div> disabled={page === 'login'}
<div>glhf</div> onClick={() => this.setState({ page: 'login' })}>
Login
</button>
<button
class={`login-btn ${page === 'register' ? 'highlight' : ''}`}
disabled={page === 'register'}
onClick={() => this.setState({ page: 'register' })}>
Register
</button>
<button
class={`login-btn ${page === 'about' ? 'highlight' : ''}`}
disabled={page === 'about'}
onClick={() => this.setState({ page: 'about' })}>
About
</button>
<button
class={`login-btn ${page === 'help' ? 'highlight' : ''}`}
disabled={page === 'help'}
onClick={() => this.setState({ page: 'help' })}>
Help
</button>
</div>
</header>
<div class="top">
<section>
<div class="news">
<h1>mnml.gg</h1>
<p>
mnml is a turn-based 1v1 strategy game in an abstract setting.<br />
outplay your opponents by building your team of 3 constructs from a shifting meta of skills, effects and specialisations.<br />
</p>
<p>
simple rules, complex interactions, simultaneous turns to increase the pace, and a unique speed mechanic;<br />
mnml is a tactical game unlike any other.
</p>
<p>
free to play<br />
no email required<br />
glhf
</p>
</div>
{pageEl()}
</section>
</div> </div>
<div class="options"> <Demo />
<button
class={`login-btn ${page === 'login' ? 'highlight' : ''}`}
disabled={page === 'login'}
onClick={() => this.setState({ page: 'login' })}>
Login
</button>
<button
class={`login-btn ${page === 'register' ? 'highlight' : ''}`}
disabled={page === 'register'}
onClick={() => this.setState({ page: 'register' })}>
Register
</button>
<button
class={`login-btn ${page === 'about' ? 'highlight' : ''}`}
disabled={page === 'about'}
onClick={() => this.setState({ page: 'about' })}>
About
</button>
<button
class={`login-btn ${page === 'help' ? 'highlight' : ''}`}
disabled={page === 'help'}
onClick={() => this.setState({ page: 'help' })}>
Help
</button>
</div>
{pageEl()}
</main> </main>
); );
} }

View File

@ -12,8 +12,8 @@ const addState = connect(
ws ws
} = state; } = state;
function submitRegister(name, password, code) { function submitRegister(name, password) {
postData('/account/register', { name, password, code }) postData('/account/register', { name, password })
.then(res => res.json()) .then(res => res.json())
.then(data => { .then(data => {
if (data.error) return errorToast(data.error); if (data.error) return errorToast(data.error);
@ -34,19 +34,18 @@ function Register(args) {
submitRegister, submitRegister,
} = args; } = args;
const { password, confirm, name, code } = this.state; const { password, confirm, name } = this.state;
const registerSubmit = (event) => { const registerSubmit = (event) => {
event.preventDefault(); event.preventDefault();
submitRegister(name, password, code); submitRegister(name, password);
// this.setState({ name: '', password: '', confirm: '', code: ''});
} }
const registerConfirm = () => const registerConfirm = () =>
password === confirm; password === confirm;
const registerDisabled = () => { const registerDisabled = () => {
return !(registerConfirm() && password && name && code); return !(registerConfirm() && password && name);
} }
return ( return (
@ -75,14 +74,6 @@ function Register(args) {
value={this.state.confirm} value={this.state.confirm}
onInput={linkState(this, 'confirm')} onInput={linkState(this, 'confirm')}
/> />
<label for="code">Access Code</label>
<input
class="login-input"
type="text"
placeholder="code"
value={this.state.code}
onInput={linkState(this, 'code')}
/>
<button <button
class="login-btn" class="login-btn"
disabled={registerDisabled()} disabled={registerDisabled()}

View File

@ -1,3 +1,5 @@
const preact = require('preact');
const SOURCE_DURATION_MS = 1000; const SOURCE_DURATION_MS = 1000;
const TARGET_DELAY_MS = 500; const TARGET_DELAY_MS = 500;
const TARGET_DURATION_MS = 1500; const TARGET_DURATION_MS = 1500;
@ -24,15 +26,11 @@ module.exports = {
INFO: { INFO: {
vbox: { vbox: {
item: 'VBOX', item: 'VBOX',
description: 'Contains ITEMS that are available for you to buy.\nDouble-click to purchase.', description: <p><b>ITEMS</b> that are available to buy.<br />the <b>VBOX</b> is refilled every round.<br />click <b>REFILL</b> at the bottom to purchase a refill. </p>,
}, },
inventory: { inventory: {
item: 'INVENTORY', item: 'INVENTORY',
description: 'Holds purchased ITEMS.\nClick to add ITEM to I-COMBINATOR.', description: <p>holds <b>ITEMS</b><br /><b>ITEMS</b> carry over each round.</p>,
},
combiner: {
item: 'I-COMBINATOR',
description: 'Combines purchased ITEMS into more powerful variants. Hover over an ITEM to see recipes.',
}, },
bits: { bits: {
item: 'BITS', item: 'BITS',
@ -51,12 +49,12 @@ module.exports = {
description: 'Reclaim ITEMS for half the purchase cost of their combined ITEMS.\nClick to enable and click ITEM to reclaim.', description: 'Reclaim ITEMS for half the purchase cost of their combined ITEMS.\nClick to enable and click ITEM to reclaim.',
}, },
refine: { refine: {
item: 'REFINE', item: 'COMBINE',
description: 'Refine ITEMS currently in I-COMBINATOR into more powerful variants', description: <p>combine the selected items.<br />hover over an item to see <b>RECIPES</b>.</p>,
}, },
refill: { refill: {
item: 'REFILL', item: 'REFILL',
description: 'Refill the VBOX with new ITEMS.', description: 'Refill the VBOX with new items.',
}, },
equipSkills: { equipSkills: {
item: 'QUICK ACCESS - SKILLS', item: 'QUICK ACCESS - SKILLS',

View File

@ -1,4 +1,5 @@
const eachSeries = require('async/eachSeries'); const eachSeries = require('async/eachSeries');
const sample = require('lodash/sample');
const actions = require('./actions'); const actions = require('./actions');
const { TIMES } = require('./constants'); const { TIMES } = require('./constants');
@ -179,6 +180,7 @@ function registerEvents(store) {
store.dispatch(actions.setActiveConstruct(first)); store.dispatch(actions.setActiveConstruct(first));
} }
} }
if (v.phase === 'Finished') setGame(null);
return store.dispatch(actions.setInstance(v)); return store.dispatch(actions.setInstance(v));
} }
@ -186,6 +188,53 @@ function registerEvents(store) {
return store.dispatch(actions.setItemInfo(v)); return store.dispatch(actions.setItemInfo(v));
} }
function setDemo(d) {
const initial = {
players: d,
combiner: [],
items: ['Red', 'Red', 'Attack'],
equipped: false,
equipping: false,
};
const startDemo = () => {
console.log(initial);
store.dispatch(actions.setDemo(initial));
store.dispatch(actions.setAnimTarget(null));
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, initial, { combiner: [0] }))), 2000);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, initial, { combiner: [0, 1] }))), 4000);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, initial, { combiner: [0, 1, 2] }))), 6000);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, initial, { combiner: [], items: ['Strike', '', ''] }))), 8000);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, initial, { combiner: [0], items: ['Strike', '', ''], equipping: true }))), 10000);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, initial, { combiner: [], items: ['', '', ''], equipped: true, equipping: false }))), 12000);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, initial, { items: ['', '', ''], equipped: true, equipping: false }))), 12000);
setTimeout(() => {
const { itemInfo } = store.getState();
return store.dispatch(actions.setAnimTarget({
skill: sample(itemInfo.items.filter(i => i.skill)).item,
constructId: d[1].constructs[0].id,
player: false,
direction: 0,
}));
}, 14000);
setTimeout(() => {
const { itemInfo } = store.getState();
return store.dispatch(actions.setAnimTarget({
skill: sample(itemInfo.items.filter(i => i.skill)).item,
constructId: d[1].constructs[1].id,
player: true,
direction: 0,
}));
}, 16000);
setTimeout(startDemo, 20000);
};
startDemo();
}
// events.on('SET_PLAYER', setInstance); // events.on('SET_PLAYER', setInstance);
// events.on('SEND_SKILL', function skillActive(gameId, constructId, targetConstructId, skill) { // events.on('SEND_SKILL', function skillActive(gameId, constructId, targetConstructId, skill) {
@ -229,6 +278,7 @@ function registerEvents(store) {
setAccountInstances, setAccountInstances,
setActiveItem, setActiveItem,
setActiveSkill, setActiveSkill,
setDemo,
setConstructList, setConstructList,
setNewConstruct, setNewConstruct,
setGame, setGame,

View File

@ -23,6 +23,8 @@ module.exports = {
animTarget: createReducer(null, 'SET_ANIM_TARGET'), animTarget: createReducer(null, 'SET_ANIM_TARGET'),
animText: createReducer(null, 'SET_ANIM_TEXT'), animText: createReducer(null, 'SET_ANIM_TEXT'),
demo: createReducer(null, 'SET_DEMO'),
combiner: createReducer([], 'SET_COMBINER'), combiner: createReducer([], 'SET_COMBINER'),
constructs: createReducer([], 'SET_CONSTRUCTS'), constructs: createReducer([], 'SET_CONSTRUCTS'),
constructEditId: createReducer(null, 'SET_CONSTRUCT_EDIT_ID'), constructEditId: createReducer(null, 'SET_CONSTRUCT_EDIT_ID'),

View File

@ -130,6 +130,10 @@ function createSocket(events) {
send(['InstanceReady', { instance_id: instanceId }]); send(['InstanceReady', { instance_id: instanceId }]);
} }
function sendInstanceAbandon(instanceId) {
send(['InstanceAbandon', { instance_id: instanceId }]);
}
function sendMtxApply(constructId, mtx, name) { function sendMtxApply(constructId, mtx, name) {
send(['MtxConstructApply', { construct_id: constructId, mtx, name }]); send(['MtxConstructApply', { construct_id: constructId, mtx, name }]);
if (mtx === 'Rename') { if (mtx === 'Rename') {
@ -202,10 +206,14 @@ function createSocket(events) {
events.setItemInfo(info); events.setItemInfo(info);
} }
function onDemo(v) {
events.setDemo(v);
}
let pongTimeout; let pongTimeout;
function onPong() { function onPong() {
events.setPing(Date.now() - ping); events.setPing(Date.now() - ping);
// pongTimeout = setTimeout(sendPing, 1000); pongTimeout = setTimeout(sendPing, 10000);
} }
// ------------- // -------------
@ -227,6 +235,7 @@ function createSocket(events) {
InstanceState: onInstanceState, InstanceState: onInstanceState,
ItemInfo: onItemInfo, ItemInfo: onItemInfo,
Pong: onPong, Pong: onPong,
Demo: onDemo,
QueueRequested: () => events.notify('pvp queue request received'), QueueRequested: () => events.notify('pvp queue request received'),
QueueJoined: () => events.notify('you have joined the pvp queue'), QueueJoined: () => events.notify('you have joined the pvp queue'),
@ -324,6 +333,7 @@ function createSocket(events) {
sendGameSkillClear, sendGameSkillClear,
sendGameTarget, sendGameTarget,
sendInstanceAbandon,
sendInstanceReady, sendInstanceReady,
sendInstancePractice, sendInstancePractice,
sendInstanceQueue, sendInstanceQueue,

View File

@ -288,15 +288,11 @@ pub fn set_subscribed(tx: &mut Transaction, id: Uuid, subscribed: bool) -> Resul
Ok(name) Ok(name)
} }
pub fn create(name: &String, password: &String, code: &String, tx: &mut Transaction) -> Result<String, MnmlHttpError> { pub fn create(name: &String, password: &String, tx: &mut Transaction) -> Result<String, MnmlHttpError> {
if password.len() < PASSWORD_MIN_LEN { if password.len() < PASSWORD_MIN_LEN {
return Err(MnmlHttpError::PasswordUnacceptable); return Err(MnmlHttpError::PasswordUnacceptable);
} }
if code.to_lowercase() != "grep842" {
return Err(MnmlHttpError::InvalidCode);
}
if name.len() == 0 { if name.len() == 0 {
return Err(MnmlHttpError::AccountNameNotProvided); return Err(MnmlHttpError::AccountNameNotProvided);
} }

View File

@ -52,8 +52,6 @@ pub enum MnmlHttpError {
PasswordUnacceptable, PasswordUnacceptable,
#[fail(display="incorrect token. refresh or logout of existing sessions")] #[fail(display="incorrect token. refresh or logout of existing sessions")]
TokenDoesNotMatch, TokenDoesNotMatch,
#[fail(display="invalid code. https://discord.gg/YJJgurM")]
InvalidCode,
} }
impl From<bcrypt::BcryptError> for MnmlHttpError { impl From<bcrypt::BcryptError> for MnmlHttpError {
@ -129,7 +127,6 @@ impl From<MnmlHttpError> for IronError {
MnmlHttpError::PasswordUnacceptable => (m_err.compat(), status::BadRequest), MnmlHttpError::PasswordUnacceptable => (m_err.compat(), status::BadRequest),
MnmlHttpError::PasswordNotMatch | MnmlHttpError::PasswordNotMatch |
MnmlHttpError::InvalidCode |
MnmlHttpError::TokenDoesNotMatch | MnmlHttpError::TokenDoesNotMatch |
MnmlHttpError::Unauthorized => (m_err.compat(), status::Unauthorized), MnmlHttpError::Unauthorized => (m_err.compat(), status::Unauthorized),
@ -211,7 +208,6 @@ fn token_res(token: String) -> Response {
struct RegisterBody { struct RegisterBody {
name: String, name: String,
password: String, password: String,
code: String,
} }
fn register(req: &mut Request) -> IronResult<Response> { fn register(req: &mut Request) -> IronResult<Response> {
@ -224,7 +220,7 @@ fn register(req: &mut Request) -> IronResult<Response> {
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?; let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?; let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?;
match account::create(&params.name, &params.password, &params.code, &mut tx) { match account::create(&params.name, &params.password, &mut tx) {
Ok(token) => { Ok(token) => {
tx.commit().or(Err(MnmlHttpError::ServerError))?; tx.commit().or(Err(MnmlHttpError::ServerError))?;
Ok(token_res(token)) Ok(token_res(token))

View File

@ -316,10 +316,6 @@ impl Instance {
} }
fn finish_condition(&mut self) -> bool { fn finish_condition(&mut self) -> bool {
if self.rounds.len() < 4 {
return false;
}
// tennis // tennis
for player in self.players.iter() { for player in self.players.iter() {
if player.score == Score::Win { if player.score == Score::Win {
@ -327,6 +323,10 @@ impl Instance {
return true; return true;
} }
} }
// Game defaults to lose otherwise
if self.rounds.len() < 4 {
return false;
}
// both players afk // both players afk
if self.players.iter().all(|p| p.score == Score::Zero) { if self.players.iter().all(|p| p.score == Score::Zero) {
@ -458,6 +458,13 @@ impl Instance {
.ok_or(err_msg("account not in instance")) .ok_or(err_msg("account not in instance"))
} }
fn account_opponent(&mut self, account: Uuid) -> Result<&mut Player, Error> {
self.players
.iter_mut()
.find(|p| p.id != account)
.ok_or(err_msg("opponent not in instance"))
}
pub fn vbox_action_allowed(&self, account: Uuid) -> Result<(), Error> { pub fn vbox_action_allowed(&self, account: Uuid) -> Result<(), Error> {
if self.players.iter().find(|p| p.id == account).is_none() { if self.players.iter().find(|p| p.id == account).is_none() {
return Err(err_msg("player not in this instance")); return Err(err_msg("player not in this instance"));
@ -739,6 +746,14 @@ pub fn pvp(tx: &mut Transaction, a: &Account, b: &Account) -> Result<Instance, E
instance_update(tx, instance) instance_update(tx, instance)
} }
pub fn instance_abandon(tx: &mut Transaction, account: &Account, instance_id: Uuid) -> Result<RpcMessage, Error> {
let mut instance = instance_get(tx, instance_id)?;
instance.account_player(account.id)?.set_lose();
instance.account_opponent(account.id)?.set_win();
instance.next_round();
Ok(RpcMessage::InstanceState(instance_update(tx, instance)?))
}
pub fn instance_ready(tx: &mut Transaction, account: &Account, instance_id: Uuid) -> Result<RpcMessage, Error> { pub fn instance_ready(tx: &mut Transaction, account: &Account, instance_id: Uuid) -> Result<RpcMessage, Error> {
let mut instance = instance_get(tx, instance_id)?; let mut instance = instance_get(tx, instance_id)?;
@ -800,6 +815,25 @@ pub fn bot_instance() -> Instance {
return instance; return instance;
} }
pub fn demo() -> Result<Vec<Player>, Error> {
let bot = bot_player();
// generate bot imgs for the client to see
for c in bot.constructs.iter() {
img::molecular_write(c.img)?;
};
let bot2 = bot_player();
// generate bot imgs for the client to see
for c in bot2.constructs.iter() {
img::molecular_write(c.img)?;
};
Ok(vec![bot, bot2])
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -27,3 +27,4 @@ pub fn bot_player() -> Player {
let constructs = instance_mobs(bot_id); let constructs = instance_mobs(bot_id);
Player::new(bot_id, &name(), constructs).set_bot(true) Player::new(bot_id, &name(), constructs).set_bot(true)
} }

View File

@ -99,6 +99,16 @@ impl Player {
self self
} }
pub fn set_win(&mut self) -> &mut Player {
self.score = Score::Win;
self
}
pub fn set_lose(&mut self) -> &mut Player {
self.score = Score::Lose;
self
}
pub fn construct_get(&mut self, id: Uuid) -> Result<&mut Construct, Error> { pub fn construct_get(&mut self, id: Uuid) -> Result<&mut Construct, Error> {
self.constructs.iter_mut().find(|c| c.id == id).ok_or(err_msg("construct not found")) self.constructs.iter_mut().find(|c| c.id == id).ok_or(err_msg("construct not found"))
} }

View File

@ -21,10 +21,12 @@ use account;
use construct::{Construct}; use construct::{Construct};
use events::{Event}; use events::{Event};
use game::{Game, game_state, game_skill, game_skill_clear, game_ready}; use game::{Game, game_state, game_skill, game_skill_clear, game_ready};
use instance::{Instance, instance_state, instance_practice, instance_ready}; use instance::{Instance, instance_state, instance_practice, instance_ready, instance_abandon, demo};
use item::{Item, ItemInfoCtr, item_info}; use item::{Item, ItemInfoCtr, item_info};
use mtx; use mtx;
use mail; use mail;
use player::{Player};
use payments; use payments;
use mail::Email; use mail::Email;
use pg::{Db}; use pg::{Db};
@ -41,6 +43,8 @@ pub enum RpcMessage {
AccountInstances(Vec<Instance>), AccountInstances(Vec<Instance>),
AccountShop(mtx::Shop), AccountShop(mtx::Shop),
Demo(Vec<Player>),
ConstructSpawn(Construct), ConstructSpawn(Construct),
GameState(Game), GameState(Game),
ItemInfo(ItemInfoCtr), ItemInfo(ItemInfoCtr),
@ -88,6 +92,7 @@ enum RpcRequest {
InstanceQueue {}, InstanceQueue {},
InstancePractice {}, InstancePractice {},
InstanceAbandon { instance_id: Uuid },
InstanceReady { instance_id: Uuid }, InstanceReady { instance_id: Uuid },
InstanceState { instance_id: Uuid }, InstanceState { instance_id: Uuid },
@ -184,6 +189,8 @@ impl Connection {
Ok(instance_ready(&mut tx, account, instance_id)?), Ok(instance_ready(&mut tx, account, instance_id)?),
RpcRequest::InstanceState { instance_id } => RpcRequest::InstanceState { instance_id } =>
Ok(instance_state(&mut tx, instance_id)?), Ok(instance_state(&mut tx, instance_id)?),
RpcRequest::InstanceAbandon { instance_id } =>
Ok(instance_abandon(&mut tx, account, instance_id)?),
RpcRequest::VboxAccept { instance_id, group, index } => RpcRequest::VboxAccept { instance_id, group, index } =>
Ok(RpcMessage::InstanceState(vbox_accept(&mut tx, account, instance_id, group, index)?)), Ok(RpcMessage::InstanceState(vbox_accept(&mut tx, account, instance_id, group, index)?)),
@ -268,6 +275,8 @@ impl Handler for Connection {
// tx should do nothing // tx should do nothing
tx.commit().unwrap(); tx.commit().unwrap();
} else {
self.ws.send(RpcMessage::Demo(demo().unwrap())).unwrap();
} }
Ok(()) Ok(())