Merge branch 'release/1.10.0'
This commit is contained in:
commit
e847ebb177
82
WORKLOG.md
82
WORKLOG.md
@ -1,10 +1,45 @@
|
|||||||
# WORK WORK
|
# WORK WORK
|
||||||
## NOW
|
## NOW
|
||||||
|
|
||||||
*PRODUCTION*
|
_ntr_
|
||||||
|
|
||||||
* can't reset password without knowing password =\
|
* can't reset password without knowing password =\
|
||||||
|
* skip faceoff on server side
|
||||||
|
* change cooldowns to delay & recharge
|
||||||
|
- delay is cooldown before skill can first be used
|
||||||
|
- recharge is cooldown after using skill
|
||||||
|
- every x speed reduces delay of skills
|
||||||
|
* audio
|
||||||
|
* animation effects
|
||||||
|
* vbox combine / buy / equip etc
|
||||||
|
* background music
|
||||||
|
* effects rework
|
||||||
|
|
||||||
|
Siphon =
|
||||||
|
[
|
||||||
|
DamageBlue(50%),
|
||||||
|
Apply(
|
||||||
|
Siphon(2T)
|
||||||
|
- Siphoning(2T)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
Hexagon Set
|
||||||
|
- Pick Colour
|
||||||
|
- Random Walk
|
||||||
|
- Draw hex
|
||||||
|
- Increase intensity for each visit
|
||||||
|
|
||||||
|
_mashy_
|
||||||
|
* floating combat text combat (start opposite hp and float towards it) to speed up animations
|
||||||
|
* represent construct colours during game phase (try %bar or dots)
|
||||||
|
* buy from preview if you have the required bases in vbox / inventory
|
||||||
|
- a "buy" becomes available under the current info / preview section
|
||||||
|
- clicking the buy automatically purchases / combine items
|
||||||
|
- could also be used to upgrade already equipped skills / specs
|
||||||
|
- e.g. an equipped white power spec could be upgraded by clicking under preview
|
||||||
|
- if this was added we could reduce inventory size to 3 and rearrange vbox (see mockup img)
|
||||||
|
|
||||||
|
_external_
|
||||||
* Graphics
|
* Graphics
|
||||||
* Img
|
* Img
|
||||||
* Skill Icons
|
* Skill Icons
|
||||||
@ -15,42 +50,13 @@
|
|||||||
- Speed (e.g. boots)
|
- Speed (e.g. boots)
|
||||||
- Life (e.g. heart)
|
- Life (e.g. heart)
|
||||||
|
|
||||||
* reduce inventory size and consolidate vbox and inventory on left side
|
_tba_
|
||||||
|
|
||||||
* audio
|
|
||||||
* background music
|
|
||||||
* animation effects
|
|
||||||
* vbox combine / buy / equip etc
|
|
||||||
|
|
||||||
* reclaim change colour from red (clashes with red items)
|
|
||||||
|
|
||||||
* represent construct colours during game phase (coloured border?)
|
|
||||||
* supporter gold name in instance (anyone whos put any money into game)
|
* supporter gold name in instance (anyone whos put any money into game)
|
||||||
|
|
||||||
* Give the bots some ai / make stronger so its a challenge for new people to beat
|
* Give the bots some ai / make stronger so its a challenge for new people to beat
|
||||||
- train a few games of with some round losses to get them into the game
|
- train a few games of with some round losses to get them into the game
|
||||||
|
|
||||||
* Speed up animations slightly (3s per normal event too long)
|
|
||||||
- Improve combat text to start at the opposite end of construct and float towards health stats
|
|
||||||
- Show combat text for skill cast possibly? Watch some pokemans etc for modern combat smoothing
|
|
||||||
|
|
||||||
* skip faceoff on server side
|
|
||||||
|
|
||||||
## SOON
|
## SOON
|
||||||
|
|
||||||
* buy from preview if you have the required bases in vbox / inventory
|
|
||||||
- a "buy" becomes available under the current info / preview section
|
|
||||||
- clicking the buy automatically purchases / combine items
|
|
||||||
- could also be used to upgrade already equipped skills / specs
|
|
||||||
- e.g. an equipped white power spec could be upgraded by clicking under preview
|
|
||||||
- if this was added we could reduce inventory size to 3 and rearrange vbox (see mockup img)
|
|
||||||
|
|
||||||
|
|
||||||
* change cooldowns to delay & recharge
|
|
||||||
- delay is cooldown before skill can first be used
|
|
||||||
- recharge is cooldown after using skill
|
|
||||||
- every x speed reduces delay of skills
|
|
||||||
|
|
||||||
* combo rework
|
* combo rework
|
||||||
- reduce number of items for creating t2/t3 items from 3 -> 2
|
- reduce number of items for creating t2/t3 items from 3 -> 2
|
||||||
- add lost complexity by adding skill spec items
|
- add lost complexity by adding skill spec items
|
||||||
@ -61,18 +67,14 @@
|
|||||||
- Strike + SpeedRR -> StrikeSpeed (strike has Y% more speed)
|
- Strike + SpeedRR -> StrikeSpeed (strike has Y% more speed)
|
||||||
- Strike + LifeRR -> StrikeLife (Strike recharges X% of damage as red life)
|
- Strike + LifeRR -> StrikeLife (Strike recharges X% of damage as red life)
|
||||||
|
|
||||||
- Can also work as module style passive keystones
|
- Can also work as module style passive keystones
|
||||||
* troll life -> dmg -> Invert life spec?
|
* troll life -> dmg -> Invert life spec?
|
||||||
* prince of peace
|
* prince of peace
|
||||||
* bonus healing / no damage -> Heal power spec?
|
* bonus healing / no damage -> Heal power spec?
|
||||||
* fuck magic -> Some sort of reflect spec?
|
* fuck magic -> Some sort of reflect spec?
|
||||||
* empower on ko -> Amplify + Power spec
|
* empower on ko -> Amplify + Power spec
|
||||||
|
|
||||||
* elo + leaderboards
|
* elo + leaderboards
|
||||||
* reconnect based on time delta
|
|
||||||
|
|
||||||
* ACP
|
|
||||||
* essential
|
|
||||||
|
|
||||||
## LATER
|
## LATER
|
||||||
|
|
||||||
@ -95,8 +97,8 @@
|
|||||||
- Working as cooldowns in reverse by building up to the skill rather than waiting
|
- Working as cooldowns in reverse by building up to the skill rather than waiting
|
||||||
|
|
||||||
* constants
|
* constants
|
||||||
* (maybe) return of the combat log (last few events with condensed descriptions)
|
* return of the combat log (last few events with condensed descriptions)
|
||||||
- click in to scroll
|
- button to switch context or overlay the combatlog
|
||||||
|
|
||||||
* mnml tv
|
* mnml tv
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-client",
|
"name": "mnml-client",
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -44,7 +44,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&[disabled] {
|
&[disabled] {
|
||||||
border: 1px solid @yellow;
|
border: 0.1em solid @yellow;
|
||||||
color: @yellow;
|
color: @yellow;
|
||||||
background: black;
|
background: black;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
@white: #f5f5f5; // whitesmoke
|
@white: #f5f5f5; // whitesmoke
|
||||||
@purple: #9355b5; // 6lack - that far cover
|
@purple: #9355b5; // 6lack - that far cover
|
||||||
@yellow: #ffa100;
|
@yellow: #ffa100;
|
||||||
|
@silver: #c0c0c0;
|
||||||
|
|
||||||
@black: black;
|
@black: black;
|
||||||
@gray: #222;
|
@gray: #222;
|
||||||
|
|||||||
@ -60,7 +60,7 @@ aside {
|
|||||||
width: 0.25em;
|
width: 0.25em;
|
||||||
max-width: 0.25em;
|
max-width: 0.25em;
|
||||||
|
|
||||||
margin: 0 1em 0 0;
|
margin: 0 0.5em 0 0;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,13 +150,13 @@ aside {
|
|||||||
&:hover {
|
&:hover {
|
||||||
color: @red;
|
color: @red;
|
||||||
border-color: @red;
|
border-color: @red;
|
||||||
border: 2px solid @red;
|
border: 0.1em solid @red;
|
||||||
};
|
};
|
||||||
|
|
||||||
&:active, &.confirming {
|
&:active, &.confirming {
|
||||||
background: @red;
|
background: @red;
|
||||||
color: black;
|
color: black;
|
||||||
border: 2px solid @red;
|
border: 0.1em solid @red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ aside {
|
|||||||
&:active, &.confirming {
|
&:active, &.confirming {
|
||||||
background: @gray-hover;
|
background: @gray-hover;
|
||||||
color: black;
|
color: black;
|
||||||
border: 2px solid @gray-hover;
|
border: 0.1em solid @gray-hover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
height: 50%;
|
height: 50%;
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
@ -36,6 +37,11 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.combat-text {
|
||||||
|
left: 15%;
|
||||||
|
top: 20%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.opponent {
|
.opponent {
|
||||||
@ -71,6 +77,11 @@
|
|||||||
"avatar"
|
"avatar"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.combat-text {
|
||||||
|
left: 15%;
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.effects {
|
.effects {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
@ -144,21 +155,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.combat-text {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
svg {
|
||||||
|
display: inline;
|
||||||
|
height: 1em;
|
||||||
|
margin-right: 0.1em
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.skills {
|
.skills {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
button {
|
button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 2em;
|
height: 3em;
|
||||||
|
line-height: 1;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
span {
|
background-color: black;
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
button.active {
|
button.active {
|
||||||
background: #2c2c2c;
|
background-color: #2c2c2c;
|
||||||
span {
|
|
||||||
background-color: #2c2c2c;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +219,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.ko-transition {
|
&.ko-transition {
|
||||||
animation: target-ko 1s ease-in-out 0s 1;
|
animation: target-ko 1.3s ease-in-out 0s 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ko {
|
&.ko {
|
||||||
@ -236,8 +256,8 @@
|
|||||||
|
|
||||||
#targeting, .resolving-skill {
|
#targeting, .resolving-skill {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 35%;
|
top: calc(35% + 0.5em); // calc for 0.5em top gap
|
||||||
height: 15%;
|
height: calc(15% - 1em); // calc for 0.5em + 0.5em top / bottom gap
|
||||||
width: calc(90% - 1.25em);
|
width: calc(90% - 1.25em);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
span {
|
span {
|
||||||
@ -266,9 +286,7 @@
|
|||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
span {
|
background-color: black;
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
svg {
|
svg {
|
||||||
display: inline;
|
display: inline;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
@ -279,7 +297,7 @@
|
|||||||
/* some stupid bug in chrome makes it fill the entire screen */
|
/* some stupid bug in chrome makes it fill the entire screen */
|
||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
#targeting {
|
#targeting {
|
||||||
max-height: 10em;
|
// max-height: 10em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,129 +1,21 @@
|
|||||||
.instance {
|
|
||||||
overflow: hidden;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr minmax(min-content, 1fr);
|
|
||||||
grid-template-rows: min-content 1fr;
|
|
||||||
|
|
||||||
grid-template-areas:
|
|
||||||
"vbox info"
|
|
||||||
"constructs constructs";
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1920px) {
|
|
||||||
.instance .info table td svg {
|
|
||||||
// height: 50%;
|
|
||||||
stroke-width: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance svg {
|
|
||||||
height: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance .top {
|
|
||||||
grid-area: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance.lobby {
|
.instance.lobby {
|
||||||
align-content: center;
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scoreboard {
|
.instance {
|
||||||
flex: 1;
|
overflow: hidden;
|
||||||
}
|
|
||||||
|
|
||||||
.instance .info {
|
|
||||||
margin: 0 0 0 1em;
|
|
||||||
grid-area: info;
|
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 13em min-content;
|
grid-template-rows: min-content 1fr;
|
||||||
|
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"item"
|
"vbox"
|
||||||
"combos";
|
"constructs";
|
||||||
|
|
||||||
.combos {
|
.constructs {
|
||||||
display: grid;
|
grid-area: constructs;
|
||||||
grid-template-columns: repeat(6, 1fr);
|
|
||||||
align-content: center;
|
|
||||||
|
|
||||||
.table-button {
|
|
||||||
display: grid;
|
|
||||||
text-align: center;
|
|
||||||
align-content: center;
|
|
||||||
border-bottom: 2px solid #222;
|
|
||||||
|
|
||||||
grid-template-areas:
|
|
||||||
"item"
|
|
||||||
"ingr";
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: whitesmoke;
|
|
||||||
background-color: @gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
border-top: 2px solid #222;
|
|
||||||
border-bottom: 2px solid #222;
|
|
||||||
flex: 1;
|
|
||||||
grid-area: item;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
border-right: 2px solid #222;
|
|
||||||
svg {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
div {
|
|
||||||
border-left: 2px solid #222;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.instance .info h2 {
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance .info svg {
|
|
||||||
display: inline;
|
|
||||||
height: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance .info figure {
|
|
||||||
display: inline;
|
|
||||||
height: 0.5em;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance .info figcaption {
|
|
||||||
font-size: 1em;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.instance .constructs {
|
|
||||||
grid-area: constructs;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance .equip {
|
|
||||||
grid-area: equip;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance .equip .skills {
|
|
||||||
border-right-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes action {
|
@keyframes action {
|
||||||
0% {
|
0% {
|
||||||
color: palegoldenrod;
|
color: palegoldenrod;
|
||||||
@ -135,10 +27,6 @@
|
|||||||
|
|
||||||
/* CONSTRUCT LIST */
|
/* CONSTRUCT LIST */
|
||||||
|
|
||||||
.construct-list {
|
|
||||||
grid-area: constructs;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instance-construct {
|
.instance-construct {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -152,20 +40,22 @@
|
|||||||
"stats ";
|
"stats ";
|
||||||
|
|
||||||
/*padding: 0.5em;*/
|
/*padding: 0.5em;*/
|
||||||
border: 2px solid #222;
|
border: 0.1em solid #222;
|
||||||
border-left-width: 0;
|
border-left-width: 0;
|
||||||
}
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
.instance-construct:first-child {
|
border-left-width: 1px;
|
||||||
margin-left: 0;
|
}
|
||||||
border-left-width: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.construct-list {
|
.construct-list {
|
||||||
|
grid-area: constructs;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
&.highlight {
|
&.highlight {
|
||||||
color: black;
|
color: black;
|
||||||
background: @white;
|
background: @silver;
|
||||||
// border: 1px solid @white; (this bangs around the vbox)
|
// border: 1px solid @white; (this bangs around the vbox)
|
||||||
|
|
||||||
// overwrite the classes on white svg elements
|
// overwrite the classes on white svg elements
|
||||||
@ -264,95 +154,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Equipment */
|
|
||||||
.equip {
|
|
||||||
display: flex;
|
|
||||||
margin: 1.5em 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equip h3 {
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-weight: bold;
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equip .specs {
|
|
||||||
flex: 1;
|
|
||||||
border: 2px solid #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
.equip .items {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 0 100%;
|
|
||||||
justify-content: space-around;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
flex: 1 0 100%;
|
flex: 1 0 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
// .equipping {
|
|
||||||
// position: relative;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .equipping::before {
|
|
||||||
// content: '';
|
|
||||||
// position: absolute;
|
|
||||||
// top: 2px;
|
|
||||||
// left: 50%;
|
|
||||||
// width: 100%;
|
|
||||||
// height: 2px;
|
|
||||||
// transform-origin: center;
|
|
||||||
// background-color: whitesmoke;
|
|
||||||
// animation: equipping-skill 2s infinite ease-out alternate;
|
|
||||||
// opacity: 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .equipping::after {
|
|
||||||
// content: '';
|
|
||||||
// position: absolute;
|
|
||||||
// bottom: 2px;
|
|
||||||
// left: 50%;
|
|
||||||
// width: 100%;
|
|
||||||
// height: 2px;
|
|
||||||
// transform-origin: center;
|
|
||||||
// background-color: whitesmoke;
|
|
||||||
// animation: equipping-skill 2s infinite ease-out alternate;
|
|
||||||
// opacity: 0;
|
|
||||||
// animation-delay: 0.75s
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @keyframes equipping-skill {
|
|
||||||
// from {
|
|
||||||
// transform: translate(-50%, 0) scaleX(0);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// to {
|
|
||||||
// transform: translate(-50%, 0) scaleX(0.75);
|
|
||||||
// opacity: 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .equip-spec {
|
|
||||||
// position: relative;
|
|
||||||
// stroke: #333;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .equip-spec::after {
|
|
||||||
// content: '';
|
|
||||||
// position: absolute;
|
|
||||||
// bottom: 2px;
|
|
||||||
// left: 50%;
|
|
||||||
// width: 100%;
|
|
||||||
// height: 2px;
|
|
||||||
// transform-origin: center;
|
|
||||||
// background-color: whitesmoke;
|
|
||||||
// animation: equipping-skill 2s infinite ease-out alternate;
|
|
||||||
// opacity: 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.equipping, .receiving {
|
.equipping, .receiving {
|
||||||
animation: eq 0.75s cubic-bezier(0, 0, 1, 1) 0s infinite alternate;
|
animation: eq 0.75s cubic-bezier(0, 0, 1, 1) 0s infinite alternate;
|
||||||
}
|
}
|
||||||
@ -484,9 +289,4 @@
|
|||||||
to {
|
to {
|
||||||
color: @yellow;
|
color: @yellow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mobile Nav*/
|
|
||||||
.instance-nav { display: none; }
|
|
||||||
|
|
||||||
.vbox-arrow-mobile { display: none }
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
// tablet / ipad
|
|
||||||
@media (max-width: 1100px) {
|
|
||||||
.instance {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-template-rows: min-content 1fr;
|
|
||||||
grid-template-areas:
|
|
||||||
"vbox"
|
|
||||||
"constructs";
|
|
||||||
|
|
||||||
.info {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 800px) {
|
|
||||||
.instance {
|
|
||||||
font-size: 6pt;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-template-rows: min-content 1fr;
|
|
||||||
grid-template-areas:
|
|
||||||
"vbox"
|
|
||||||
"constructs";
|
|
||||||
|
|
||||||
.info {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -74,7 +74,7 @@
|
|||||||
button {
|
button {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
border: 2px solid #222;
|
border: 0.1em solid #222;
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
.msg {
|
.msg {
|
||||||
grid-area: msg;
|
grid-area: msg;
|
||||||
|
text-transform: uppercase;
|
||||||
color: @white;
|
color: @white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
client/assets/styles/skeleton.css
vendored
1
client/assets/styles/skeleton.css
vendored
@ -176,7 +176,6 @@ input[type="button"] {
|
|||||||
/*padding: 0 2em;*/
|
/*padding: 0 2em;*/
|
||||||
color: #555;
|
color: #555;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 38px;
|
line-height: 38px;
|
||||||
letter-spacing: .1rem;
|
letter-spacing: .1rem;
|
||||||
|
|||||||
@ -8,8 +8,6 @@
|
|||||||
@import 'vbox.less';
|
@import 'vbox.less';
|
||||||
@import 'game.less';
|
@import 'game.less';
|
||||||
@import 'player.less';
|
@import 'player.less';
|
||||||
@import 'styles.mobile.less';
|
|
||||||
@import 'instance.mobile.less';
|
|
||||||
|
|
||||||
html body {
|
html body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -23,6 +21,7 @@ html body {
|
|||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
@ -33,7 +32,6 @@ html body {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
/*padding: 0 20%;*/
|
|
||||||
|
|
||||||
/* stops inspector going skitz*/
|
/* stops inspector going skitz*/
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
@ -125,14 +123,19 @@ button, input {
|
|||||||
font-family: 'Jura';
|
font-family: 'Jura';
|
||||||
color: whitesmoke;
|
color: whitesmoke;
|
||||||
height: auto;
|
height: auto;
|
||||||
border-width: 2px;
|
border-width: 0.1em;
|
||||||
border-color: @gray-exists;
|
border-color: @gray-exists;
|
||||||
letter-spacing: 0.25em;
|
letter-spacing: 0.25em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-size: 100%;
|
font-size: 1em;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
|
padding-right: 0.1em;
|
||||||
|
padding-left: 0.1em;
|
||||||
|
padding-bottom: 0.1em;
|
||||||
|
padding-top: 0.1em;
|
||||||
|
|
||||||
|
|
||||||
/*the transitions */
|
/*the transitions */
|
||||||
transition-property: border-color, color, background;
|
transition-property: border-color, color, background;
|
||||||
@ -250,6 +253,7 @@ figure.gray {
|
|||||||
|
|
||||||
svg {
|
svg {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
|
stroke-width: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,3 +358,5 @@ li {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import 'styles.mobile.less';
|
||||||
|
|||||||
@ -1,26 +1,47 @@
|
|||||||
@media (max-width: 1000px) {
|
@media (max-width: 800px) {
|
||||||
body {
|
body {
|
||||||
overflow-y: initial;
|
overflow-y: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mnml {
|
#mnml {
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
padding: 0.25em;
|
padding: 0;
|
||||||
|
|
||||||
.instance {
|
.instance {
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-template-rows: min-content 1fr;
|
|
||||||
|
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"vbox"
|
"vbox vbox"
|
||||||
"constructs";
|
"constructs constructs";
|
||||||
|
|
||||||
|
font-size: 7.5pt;
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
div:nth-child(4n) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
stroke-width: 1.25em;
|
stroke-width: 1.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.game {
|
.game {
|
||||||
|
font-size: 7.5pt;
|
||||||
|
.stats {
|
||||||
|
svg {
|
||||||
|
stroke-width: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div:nth-child(4n) {
|
||||||
|
margin: 0 0.25em;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
padding: 0 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.team, #targeting, .resolving-skill {
|
.team, #targeting, .resolving-skill {
|
||||||
width: calc(90% - 3em);
|
width: calc(90% - 3em);
|
||||||
}
|
}
|
||||||
@ -29,7 +50,6 @@
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
grid-template-rows: min-content 1fr;
|
grid-template-rows: min-content 1fr;
|
||||||
|
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
grid-area: initial;
|
grid-area: initial;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -45,23 +65,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.skills {
|
.skills {
|
||||||
button[disabled] {
|
display: flex;
|
||||||
display: none;
|
button {
|
||||||
|
font-size: 1em;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.effects {
|
.effects {
|
||||||
font-size: 1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skill-description {
|
.skill-description {
|
||||||
font-size: 0.8em;
|
|
||||||
svg {
|
svg {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.player {
|
.player {
|
||||||
|
width: calc(90%);
|
||||||
.game-construct {
|
.game-construct {
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"left"
|
"left"
|
||||||
@ -78,7 +103,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.resolving-skill {
|
||||||
|
width: calc(90%);
|
||||||
|
}
|
||||||
|
|
||||||
.opponent {
|
.opponent {
|
||||||
|
width: calc(90%);
|
||||||
.game-construct {
|
.game-construct {
|
||||||
grid-template-rows: 2fr min-content;
|
grid-template-rows: 2fr min-content;
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
@ -100,21 +130,31 @@
|
|||||||
.instance-construct {
|
.instance-construct {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-template-rows: min-content min-content 1fr min-content;
|
||||||
|
grid-template-areas:
|
||||||
|
"skills skills"
|
||||||
|
"specs specs"
|
||||||
|
"avatar name"
|
||||||
|
"stats stats ";
|
||||||
|
|
||||||
.skills, .specs {
|
.skills, .specs {
|
||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.stats {
|
||||||
grid-area: initial;
|
svg {
|
||||||
position: absolute;
|
height: 1em;
|
||||||
top: 0;
|
}
|
||||||
height: 100%;
|
}
|
||||||
width: 100%;
|
|
||||||
z-index: -1;
|
.name {
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aside {
|
aside {
|
||||||
|
font-size: 75%;
|
||||||
button {
|
button {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
@ -123,8 +163,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// portrait menu
|
// portrait menu or small size vertical in landscape
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 550px) and (max-height: 800px) {
|
||||||
#mnml {
|
#mnml {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
@ -166,7 +206,7 @@
|
|||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|
||||||
button:not(:last-child) {
|
button:not(:last-child) {
|
||||||
border: 2px solid #222;
|
border: 0.1em solid #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.logo {
|
button.logo {
|
||||||
@ -191,9 +231,107 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
font-size: 6pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-description {
|
||||||
|
font-size: 6pt;
|
||||||
|
}
|
||||||
|
|
||||||
section {
|
section {
|
||||||
.list {
|
.list {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.vbox {
|
||||||
|
grid-template-rows: min-content min-content 1fr;
|
||||||
|
grid-template-columns: min-content 1fr min-content 1fr;
|
||||||
|
grid-template-areas:
|
||||||
|
"store-hdr store-hdr stash-hdr stash-hdr"
|
||||||
|
"store store stash stash"
|
||||||
|
"store store info-combiner info-combiner";
|
||||||
|
|
||||||
|
> div {
|
||||||
|
padding: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.combos {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-combiner {
|
||||||
|
.info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.combiner {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash {
|
||||||
|
border: 0;
|
||||||
|
border-top: 0.1em solid @gray;
|
||||||
|
border-right: 0.1em solid @gray;
|
||||||
|
border-bottom: 0.1em solid @gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-hdr {
|
||||||
|
border: 0;
|
||||||
|
border-left: 0.1em solid @gray;
|
||||||
|
border-right: 0.1em solid @gray;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: min-content min-content;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.store {
|
||||||
|
border: 0;
|
||||||
|
border-top: 0.1em solid @gray;
|
||||||
|
border-bottom: 0.1em solid @gray;
|
||||||
|
border-right: 0.1em solid @gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-hdr {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content min-content 1fr;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
grid-template-areas:
|
||||||
|
"hdr bits btn";
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
grid-area: hdr;
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bits {
|
||||||
|
grid-area: bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
grid-area: btn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-hdr, .stash-hdr {
|
||||||
|
button {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,84 @@
|
|||||||
.vbox {
|
.vbox {
|
||||||
margin-bottom: 2em;
|
align-content: space-between;
|
||||||
line-height: 0;
|
grid-area: vbox;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 3fr minmax(min-content, 2fr);
|
||||||
|
grid-template-columns: 1fr 4fr 6fr minmax(min-content, 2fr);;
|
||||||
|
grid-template-areas:
|
||||||
|
"store-hdr store info-combiner combos"
|
||||||
|
"stash-hdr stash info-combiner combos";
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
// immediate children
|
||||||
|
> div {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.store {
|
||||||
|
grid-area: store;
|
||||||
|
border-right: 0.15em solid @gray;
|
||||||
|
border-top: 0.15em solid @gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.store-hdr {
|
||||||
|
grid-area: store-hdr;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
border-left: 0.15em solid @gray;
|
||||||
|
border-top: 0.15em solid @gray;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
line-height: 1.6;
|
||||||
|
letter-spacing: 0.15em;
|
||||||
|
background-color: #421010;
|
||||||
|
&:hover {
|
||||||
|
background-color: @red;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash {
|
||||||
|
grid-area: stash;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-gap: 0.5em 1em;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
border: 0.15em solid @gray;
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stash-hdr {
|
||||||
|
grid-area: stash-hdr;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
border: 0.15em solid @gray;
|
||||||
|
border-right: 0;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
line-height: 1.6;
|
||||||
|
letter-spacing: 0.15em;
|
||||||
|
border-width: 0.1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.vbox-hdr {
|
.vbox-hdr {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
@ -19,14 +97,12 @@
|
|||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
grid-gap: 0.5em 1em;
|
grid-gap: 0.5em 1em;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vbox-btn {
|
.vbox-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: @gray-box;
|
background-color: @gray-box;
|
||||||
height: 3em;
|
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
|
|
||||||
@ -34,26 +110,18 @@
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.reclaim {
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: @red;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
&[disabled] {
|
&[disabled] {
|
||||||
background: black;
|
background: black;
|
||||||
border-width: 1px;
|
border-width: 0.1em;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
.reclaiming {
|
.Refunding {
|
||||||
button:not([disabled]) {
|
button:not([disabled]) {
|
||||||
&, &:hover, &:active {
|
&, &:hover, &:active {
|
||||||
background: @red;
|
background: @red;
|
||||||
color: black;
|
color: black;
|
||||||
border: 2px solid black;
|
border: 0.1em solid black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
svg {
|
svg {
|
||||||
@ -65,21 +133,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
height: 4em;
|
height: 3.5em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
// text-transform: none;
|
// text-transform: none;
|
||||||
|
|
||||||
&.empty {
|
&.empty {
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fade {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
&.highlight {
|
&.highlight {
|
||||||
color: black;
|
color: black;
|
||||||
background: @white;
|
background: @silver;
|
||||||
// border: 1px solid @white; (this bangs around the vbox)
|
|
||||||
|
|
||||||
// overwrite the classes on white svg elements
|
// overwrite the classes on white svg elements
|
||||||
svg {
|
svg {
|
||||||
stroke-width: 0.75em;
|
stroke-width: 0.75em;
|
||||||
@ -94,7 +163,7 @@
|
|||||||
// figures don't scale well
|
// figures don't scale well
|
||||||
figure {
|
figure {
|
||||||
svg {
|
svg {
|
||||||
height: 2em;
|
height: 1.5em;
|
||||||
stroke-width: 0.5em;
|
stroke-width: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,63 +171,155 @@
|
|||||||
line-height: initial;
|
line-height: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-combiner {
|
||||||
|
grid-area: info-combiner;
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"info"
|
||||||
|
"combiner";
|
||||||
|
grid-template-rows: min-content 1fr;
|
||||||
|
|
||||||
|
.info {
|
||||||
|
grid-area: info;
|
||||||
|
line-height: 1.6;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: inline;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
display: inline;
|
||||||
|
height: 0.5em;
|
||||||
|
svg {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
figcaption {
|
||||||
|
font-size: 1em;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.combiner {
|
||||||
|
grid-area: combiner;
|
||||||
|
margin: 1em 0.5em;
|
||||||
|
width: 50%;
|
||||||
|
line-height: 1.3;
|
||||||
|
font-size: 1.25em;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
border: 0.1em solid @gray-exists;
|
||||||
|
&:hover {
|
||||||
|
border: 0.1em solid @gray-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.combos {
|
||||||
|
display: grid;
|
||||||
|
grid-area: combos;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
grid-template-rows: min-content min-content;
|
||||||
|
width: 15.5em;
|
||||||
|
grid-template-areas:
|
||||||
|
"comboHeader"
|
||||||
|
"comboList";
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: inline;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
display: inline;
|
||||||
|
height: 0.5em;
|
||||||
|
svg {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
figcaption {
|
||||||
|
font-size: 1em;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.combo-header {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.combo-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: min-content min-content min-content;
|
||||||
|
grid-template-columns: min-content min-content;
|
||||||
|
grid-gap: 0.5em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
width: 15.5em;
|
||||||
|
|
||||||
|
.table-button {
|
||||||
|
display: grid;
|
||||||
|
text-align: center;
|
||||||
|
align-content: center;
|
||||||
|
grid-template-areas:
|
||||||
|
"item"
|
||||||
|
"ingr";
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: whitesmoke;
|
||||||
|
background-color: @gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
border-top: 0.1em solid #222;
|
||||||
|
border-bottom: 0.1em solid #222;
|
||||||
|
flex: 1;
|
||||||
|
grid-area: item;
|
||||||
|
font-weight: bold;
|
||||||
|
div {
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div {
|
||||||
|
border-left: 0.1em solid #222;
|
||||||
|
border-right: 0.1em solid #222;
|
||||||
|
height: 1.75em;
|
||||||
|
width: 7.5em;
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0.1em solid #222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* VBOX */
|
@media (min-width: 2000px) {
|
||||||
.vbox {
|
.vbox {
|
||||||
align-content: space-between;
|
button {
|
||||||
grid-area: vbox;
|
height: 4.5em;
|
||||||
display: grid;
|
}
|
||||||
grid-template-rows: min-content min-content min-content;
|
}
|
||||||
grid-template-columns: 1fr min-content 1fr;
|
}
|
||||||
grid-template-areas:
|
|
||||||
"vbox varrow inventory"
|
|
||||||
"vbox varrow combiner";
|
|
||||||
}
|
|
||||||
|
|
||||||
.vbox-inventory {
|
|
||||||
grid-area: inventory;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vbox-combiner {
|
|
||||||
grid-area: combiner;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vbox-arrow, .vbox-arrow-mobile {
|
|
||||||
display: flex;
|
|
||||||
justify-content:center;
|
|
||||||
align-content:center;
|
|
||||||
flex-direction:column;
|
|
||||||
|
|
||||||
margin: 1em 0.25em 0 0.25em;
|
|
||||||
grid-area: varrow;
|
|
||||||
font-size: 2em;
|
|
||||||
color: @gray-hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vbox-combiner button {
|
|
||||||
flex: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.vbox-hdr {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vbox-hdr h3 {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vbox-hdr .bits {
|
|
||||||
font-size: 2em;
|
|
||||||
line-height: 1em;
|
|
||||||
animation: bits 1s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow {
|
|
||||||
grid-area: arrow;
|
|
||||||
color: @gray-hint;
|
|
||||||
}
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-client",
|
"name": "mnml-client",
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -23,7 +23,9 @@ export const setConstructRename = value => ({ type: 'SET_CONSTRUCT_RENAME', valu
|
|||||||
export const setGame = value => ({ type: 'SET_GAME', value });
|
export const setGame = value => ({ type: 'SET_GAME', value });
|
||||||
export const setGameSkillInfo = value => ({ type: 'SET_GAME_SKILL_INFO', value });
|
export const setGameSkillInfo = value => ({ type: 'SET_GAME_SKILL_INFO', value });
|
||||||
export const setGameEffectInfo = value => ({ type: 'SET_GAME_EFFECT_INFO', value });
|
export const setGameEffectInfo = value => ({ type: 'SET_GAME_EFFECT_INFO', value });
|
||||||
|
|
||||||
export const setInfo = value => ({ type: 'SET_INFO', value });
|
export const setInfo = value => ({ type: 'SET_INFO', value });
|
||||||
|
export const setComboPreview = value => ({ type: 'SET_COMBO_PREVIEW', value });
|
||||||
|
|
||||||
export const setEmail = value => ({ type: 'SET_EMAIL', value });
|
export const setEmail = value => ({ type: 'SET_EMAIL', value });
|
||||||
export const setInvite = value => ({ type: 'SET_INVITE', value });
|
export const setInvite = value => ({ type: 'SET_INVITE', value });
|
||||||
@ -35,7 +37,6 @@ export const setMtxActive = value => ({ type: 'SET_MTX_ACTIVE', value });
|
|||||||
export const setNav = value => ({ type: 'SET_NAV', value });
|
export const setNav = value => ({ type: 'SET_NAV', value });
|
||||||
export const setPing = value => ({ type: 'SET_PING', value });
|
export const setPing = value => ({ type: 'SET_PING', value });
|
||||||
export const setPlayer = value => ({ type: 'SET_PLAYER', value });
|
export const setPlayer = value => ({ type: 'SET_PLAYER', value });
|
||||||
export const setReclaiming = value => ({ type: 'SET_RECLAIMING', value });
|
|
||||||
export const setShowLog = value => ({ type: 'SET_SHOW_LOG', value });
|
export const setShowLog = value => ({ type: 'SET_SHOW_LOG', value });
|
||||||
export const setShop = value => ({ type: 'SET_SHOP', value });
|
export const setShop = value => ({ type: 'SET_SHOP', value });
|
||||||
export const setSubscription = value => ({ type: 'SET_SUBSCRIPTION', value });
|
export const setSubscription = value => ({ type: 'SET_SUBSCRIPTION', value });
|
||||||
@ -48,7 +49,9 @@ export const setTeamSelect = value => ({ type: 'SET_TEAM_SELECT', value: Array.f
|
|||||||
export const setTutorial = value => ({ type: 'SET_TUTORIAL', value });
|
export const setTutorial = value => ({ type: 'SET_TUTORIAL', value });
|
||||||
export const setTutorialGame = value => ({ type: 'SET_TUTORIAL_GAME', value });
|
export const setTutorialGame = value => ({ type: 'SET_TUTORIAL_GAME', value });
|
||||||
|
|
||||||
|
export const setVboxSelected = value => ({ type: 'SET_VBOX_SELECTED', value: value });
|
||||||
|
export const setVboxCombiner = value => ({ type: 'SET_VBOX_COMBINER', value });
|
||||||
export const setVboxHighlight = value => ({ type: 'SET_VBOX_HIGHLIGHT', value });
|
export const setVboxHighlight = value => ({ type: 'SET_VBOX_HIGHLIGHT', value });
|
||||||
export const setVboxSelected = value => ({ type: 'SET_VBOX_SELECTED', value });
|
export const setVboxInfo = value => ({ type: 'SET_VBOX_INFO', value });
|
||||||
|
|
||||||
export const setWs = value => ({ type: 'SET_WS', value });
|
export const setWs = value => ({ type: 'SET_WS', value });
|
||||||
|
|||||||
@ -128,11 +128,11 @@ function getText(resolution) {
|
|||||||
let { amount } = event;
|
let { amount } = event;
|
||||||
let css = '';
|
let css = '';
|
||||||
if (colour === 'Green') {
|
if (colour === 'Green') {
|
||||||
css = 'green-damage';
|
css = 'green';
|
||||||
amount *= -1;
|
amount *= -1;
|
||||||
}
|
}
|
||||||
if (colour === 'Red') css = 'red-damage';
|
if (colour === 'Red') css = 'red';
|
||||||
if (colour === 'Blue') css = 'blue-damage';
|
if (colour === 'Blue') css = 'blue';
|
||||||
|
|
||||||
const mitigationText = mitigation
|
const mitigationText = mitigation
|
||||||
? `(${mitigation})`
|
? `(${mitigation})`
|
||||||
@ -142,7 +142,7 @@ function getText(resolution) {
|
|||||||
|
|
||||||
if (type === 'Healing') {
|
if (type === 'Healing') {
|
||||||
const { amount, overhealing } = event;
|
const { amount, overhealing } = event;
|
||||||
return { text: `${amount} (${overhealing} OH)`, css: 'green-damage' };
|
return { text: `${amount} (${overhealing} OH)`, css: 'green' };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'Inversion') {
|
if (type === 'Inversion') {
|
||||||
@ -160,9 +160,9 @@ function getText(resolution) {
|
|||||||
|
|
||||||
if (type === 'Recharge') {
|
if (type === 'Recharge') {
|
||||||
const { red, blue } = event;
|
const { red, blue } = event;
|
||||||
if (red > 0 && blue > 0) return { text: [`+${red}R +${blue}B`, ''], css: 'rb-damage' };
|
if (red > 0 && blue > 0) return { text: `+${red}R +${blue}B`, css: 'rb' };
|
||||||
if (red > 0) return { text: [`+${red}R`, ''], css: 'red-damage' };
|
if (red > 0) return { text: `+${red}R`, css: 'red' };
|
||||||
if (blue > 0) return { text: [`+${blue}B`, ''], css: 'blue-damage' };
|
if (blue > 0) return { text: `+${blue}B`, css: 'blue' };
|
||||||
return nullText;
|
return nullText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,12 +178,14 @@ function getText(resolution) {
|
|||||||
|
|
||||||
const { green, red, blue } = resolution.target;
|
const { green, red, blue } = resolution.target;
|
||||||
const { text, css, effects } = generatePostSkill();
|
const { text, css, effects } = generatePostSkill();
|
||||||
|
const skill = resolution.event[1] ? resolution.event[1].skill : null;
|
||||||
return {
|
return {
|
||||||
css,
|
css,
|
||||||
text,
|
text,
|
||||||
effects,
|
effects,
|
||||||
life: { green, red, blue },
|
life: { green, red, blue },
|
||||||
constructId: resolution.target.id,
|
constructId: resolution.target.id,
|
||||||
|
skill,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ const { createStore, combineReducers } = require('redux');
|
|||||||
const { StripeProvider } = require('react-stripe-elements');
|
const { StripeProvider } = require('react-stripe-elements');
|
||||||
|
|
||||||
const reducers = require('./reducers');
|
const reducers = require('./reducers');
|
||||||
const actions = require('./actions');
|
|
||||||
const setupKeys = require('./keyboard');
|
const setupKeys = require('./keyboard');
|
||||||
const createSocket = require('./socket');
|
const createSocket = require('./socket');
|
||||||
const registerEvents = require('./events');
|
const registerEvents = require('./events');
|
||||||
|
|||||||
@ -33,11 +33,9 @@ const addState = connect(
|
|||||||
function accountPage() {
|
function accountPage() {
|
||||||
dispatch(actions.setGame(null));
|
dispatch(actions.setGame(null));
|
||||||
dispatch(actions.setInstance(null));
|
dispatch(actions.setInstance(null));
|
||||||
dispatch(actions.setReclaiming(false));
|
|
||||||
dispatch(actions.setActiveSkill(null));
|
dispatch(actions.setActiveSkill(null));
|
||||||
dispatch(actions.setInfo(null));
|
dispatch(actions.setInfo(null));
|
||||||
dispatch(actions.setItemUnequip([]));
|
dispatch(actions.setItemUnequip([]));
|
||||||
dispatch(actions.setVboxHighlight([]));
|
|
||||||
return dispatch(actions.setNav('account'));
|
return dispatch(actions.setNav('account'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,6 @@ const anime = require('animejs').default;
|
|||||||
const { connect } = require('preact-redux');
|
const { connect } = require('preact-redux');
|
||||||
|
|
||||||
const { TIMES } = require('../../constants');
|
const { TIMES } = require('../../constants');
|
||||||
const { randomPoints } = require('../../utils');
|
|
||||||
|
|
||||||
const duration = TIMES.TARGET_DURATION_MS;
|
|
||||||
|
|
||||||
const addState = connect(
|
const addState = connect(
|
||||||
function receiveState(state) {
|
function receiveState(state) {
|
||||||
@ -95,17 +92,17 @@ class Slay extends Component {
|
|||||||
this.animations.push(anime({
|
this.animations.push(anime({
|
||||||
targets: '#slay',
|
targets: '#slay',
|
||||||
opacity: [
|
opacity: [
|
||||||
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.3 },
|
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 },
|
||||||
{
|
{
|
||||||
value: 0,
|
value: 0,
|
||||||
delay: TIMES.TARGET_DURATION_MS * 0.7 + TIMES.POST_SKILL_DURATION_MS * 0.8,
|
delay: TIMES.TARGET_DURATION_MS + TIMES.POST_SKILL_DURATION_MS * 0.2,
|
||||||
duration: TIMES.POST_SKILL_DURATION_MS * 0.2,
|
duration: TIMES.POST_SKILL_DURATION_MS * 0.3,
|
||||||
}],
|
}],
|
||||||
translateY: 0,
|
translateY: 0,
|
||||||
translateX: 0,
|
translateX: 0,
|
||||||
loop: false,
|
loop: false,
|
||||||
delay: TIMES.TARGET_DELAY_MS,
|
delay: TIMES.TARGET_DELAY_MS,
|
||||||
duration: (duration * 1 / 2),
|
duration: (TIMES.TARGET_DURATION_MS * 0.5),
|
||||||
easing: 'easeInQuad',
|
easing: 'easeInQuad',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -113,15 +110,15 @@ class Slay extends Component {
|
|||||||
targets: ['#slayFilter feTurbulence', '#slayFilter feDisplacementMap'],
|
targets: ['#slayFilter feTurbulence', '#slayFilter feDisplacementMap'],
|
||||||
baseFrequency: 10,
|
baseFrequency: 10,
|
||||||
scale: 100,
|
scale: 100,
|
||||||
delay: (TIMES.TARGET_DELAY_MS + duration * 1 / 2),
|
delay: (TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.5),
|
||||||
duration: (duration * 1 / 2),
|
duration: (TIMES.TARGET_DURATION_MS * 0.5),
|
||||||
easing: 'easeInQuad',
|
easing: 'easeInQuad',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.animations.push(anime({
|
this.animations.push(anime({
|
||||||
targets: '#sword',
|
targets: '#sword',
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
delay: (TIMES.TARGET_DELAY_MS + duration + TIMES.POST_SKILL_DURATION_MS * 0.7),
|
delay: (TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const projectiles = document.querySelectorAll('#slay circle');
|
const projectiles = document.querySelectorAll('#slay circle');
|
||||||
@ -130,7 +127,7 @@ class Slay extends Component {
|
|||||||
targets: proj,
|
targets: proj,
|
||||||
cx: Math.random() * 250 + 25,
|
cx: Math.random() * 250 + 25,
|
||||||
cy: Math.random() * 200 - 100,
|
cy: Math.random() * 200 - 100,
|
||||||
delay: (TIMES.TARGET_DELAY_MS + duration + TIMES.POST_SKILL_DURATION_MS * 0.7),
|
delay: (TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS + TIMES.POST_SKILL_DURATION_MS * 0.2),
|
||||||
duration: (TIMES.POST_SKILL_DURATION_MS * 0.3),
|
duration: (TIMES.POST_SKILL_DURATION_MS * 0.3),
|
||||||
easing: 'easeInQuad',
|
easing: 'easeInQuad',
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -19,7 +19,7 @@ const addState = connect(
|
|||||||
);
|
);
|
||||||
|
|
||||||
class ConstructAvatar extends Component {
|
class ConstructAvatar extends Component {
|
||||||
constructor(props) {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
// The animation ids are a check to ensure that animations are not repeated
|
// The animation ids are a check to ensure that animations are not repeated
|
||||||
// When a new construct animation is communicated with state it will have a corresponding Id
|
// When a new construct animation is communicated with state it will have a corresponding Id
|
||||||
@ -96,26 +96,16 @@ class ConstructAvatar extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ConstructText(props) {
|
||||||
const addStateText = connect(
|
const { construct } = props;
|
||||||
function receiveState(state) {
|
|
||||||
const { animText } = state;
|
|
||||||
return { animText };
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
function constructText(props) {
|
|
||||||
const { construct, animText } = props;
|
|
||||||
if (!construct) return false;
|
if (!construct) return false;
|
||||||
|
|
||||||
const text = animText && animText.constructId === construct.id
|
const text = construct.name;
|
||||||
? animText.text
|
|
||||||
: construct.name;
|
|
||||||
|
|
||||||
return <h3 class={'name'}><span>{text}</span></h3>;
|
return <h3 class={'name'}><span>{text}</span></h3>;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ConstructAvatar: addState(ConstructAvatar),
|
ConstructAvatar: addState(ConstructAvatar),
|
||||||
ConstructText: addStateText(constructText),
|
ConstructText,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -46,7 +46,7 @@ function Demo(args) {
|
|||||||
const { combiner, items, equipping, equipped, players, combo } = demo;
|
const { combiner, items, equipping, equipped, players, combo } = demo;
|
||||||
|
|
||||||
const vboxDemo = () => {
|
const vboxDemo = () => {
|
||||||
function inventoryBtn(i, j) {
|
function stashBtn(i, j) {
|
||||||
if (!i) return <button disabled class='empty' > </button>;
|
if (!i) return <button disabled class='empty' > </button>;
|
||||||
const highlighted = combiner.indexOf(j) > -1;
|
const highlighted = combiner.indexOf(j) > -1;
|
||||||
const classes = `${highlighted ? 'highlight' : ''}`;
|
const classes = `${highlighted ? 'highlight' : ''}`;
|
||||||
@ -82,7 +82,7 @@ function Demo(args) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function inventoryElement() {
|
function stashElement() {
|
||||||
return (
|
return (
|
||||||
<div class="vbox">
|
<div class="vbox">
|
||||||
<div class='vbox-section'>
|
<div class='vbox-section'>
|
||||||
@ -96,7 +96,7 @@ function Demo(args) {
|
|||||||
<div> </div>
|
<div> </div>
|
||||||
<div class='vbox-section'>
|
<div class='vbox-section'>
|
||||||
<div class='vbox-items'>
|
<div class='vbox-items'>
|
||||||
{items.map((i, j) => inventoryBtn(i, j))}
|
{items.map((i, j) => stashBtn(i, j))}
|
||||||
</div>
|
</div>
|
||||||
{combinerBtn()}
|
{combinerBtn()}
|
||||||
</div>
|
</div>
|
||||||
@ -106,7 +106,7 @@ function Demo(args) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="news vbox-demo">
|
<div class="news vbox-demo">
|
||||||
{inventoryElement()}
|
{stashElement()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,17 +1,82 @@
|
|||||||
const { connect } = require('preact-redux');
|
|
||||||
const { Component } = require('preact');
|
|
||||||
const preact = require('preact');
|
const preact = require('preact');
|
||||||
|
const { connect } = require('preact-redux');
|
||||||
|
const anime = require('animejs').default;
|
||||||
const range = require('lodash/range');
|
const range = require('lodash/range');
|
||||||
const reactStringReplace = require('react-string-replace');
|
const reactStringReplace = require('react-string-replace');
|
||||||
|
|
||||||
const { STATS } = require('../utils');
|
const { STATS, removeTier } = require('../utils');
|
||||||
const { ConstructAvatar, ConstructText } = require('./construct');
|
const { ConstructAvatar, ConstructText } = require('./construct');
|
||||||
const shapes = require('./shapes');
|
const shapes = require('./shapes');
|
||||||
const { INFO } = require('./../constants');
|
const { INFO, TIMES } = require('./../constants');
|
||||||
const actions = require('../actions');
|
const actions = require('../actions');
|
||||||
|
|
||||||
const SkillBtn = require('./skill.btn');
|
const SkillBtn = require('./skill.btn');
|
||||||
|
|
||||||
|
const addStateText = connect(({ animText, itemInfo }) => ({ animText, itemInfo }));
|
||||||
|
|
||||||
|
class combatText extends preact.Component {
|
||||||
|
shouldComponentUpdate(newProps) {
|
||||||
|
if (newProps.animText !== this.props.animText) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const { animText, construct } = this.props;
|
||||||
|
if (animText && animText !== prevProps.animText && animText.constructId === construct.id) {
|
||||||
|
anime({
|
||||||
|
targets: '.combat-text',
|
||||||
|
top: '40%',
|
||||||
|
duration: TIMES.POST_SKILL_DURATION_MS - 500,
|
||||||
|
easing: 'easeOutQuad',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(props) {
|
||||||
|
const { construct, animText, itemInfo } = props;
|
||||||
|
if (animText && animText.constructId === construct.id) {
|
||||||
|
const itemSourceDescription = () => {
|
||||||
|
const itemSource = itemInfo.combos.filter(c => c.item === removeTier(animText.skill));
|
||||||
|
const itemSourceInfo = itemSource.length
|
||||||
|
? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}`
|
||||||
|
: false;
|
||||||
|
const itemRegEx = /(Red|Blue|Green)/;
|
||||||
|
return reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]());
|
||||||
|
};
|
||||||
|
|
||||||
|
const animationTextHtml = () => {
|
||||||
|
// monkaW hack to make red / blue recharge work nicely
|
||||||
|
if (animText.css === 'rb') {
|
||||||
|
const text = animText.text.split(' ');
|
||||||
|
return (
|
||||||
|
<h1>
|
||||||
|
<span class="red">{text[0]}</span>
|
||||||
|
<span class="blue">{text[1]}</span>
|
||||||
|
</h1>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<h1 class={animText.css}>
|
||||||
|
<span>{animText.text}</span>
|
||||||
|
</h1>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="combat-text">
|
||||||
|
<h2><span>{animText.skill}</span></h2>
|
||||||
|
<span>{itemSourceDescription()}</span>
|
||||||
|
{animationTextHtml()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConstructAnimationText = addStateText(combatText);
|
||||||
|
|
||||||
const addState = connect(
|
const addState = connect(
|
||||||
function receiveState(state) {
|
function receiveState(state) {
|
||||||
const {
|
const {
|
||||||
@ -79,7 +144,7 @@ const eventClasses = (animating, animFocus, construct, postSkill) => {
|
|||||||
return postSkill.css;
|
return postSkill.css;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GameConstruct extends Component {
|
class GameConstruct extends preact.Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.resolvedLength = 0;
|
this.resolvedLength = 0;
|
||||||
@ -120,6 +185,7 @@ class GameConstruct extends Component {
|
|||||||
const koEvent = animText ? animText.text === 'KO!' && animText.constructId === construct.id : false;
|
const koEvent = animText ? animText.text === 'KO!' && animText.constructId === construct.id : false;
|
||||||
const ko = construct.green_life.value === 0 && !koEvent ? 'ko' : '';
|
const ko = construct.green_life.value === 0 && !koEvent ? 'ko' : '';
|
||||||
const classes = eventClasses(animating, animFocus, construct, animText);
|
const classes = eventClasses(animating, animFocus, construct, animText);
|
||||||
|
const cssClass = ['ko-transition', 'unfocus'].includes(classes) ? classes : null;
|
||||||
|
|
||||||
const stats = ['RedLife', 'GreenLife', 'BlueLife'].map((s, j) => (
|
const stats = ['RedLife', 'GreenLife', 'BlueLife'].map((s, j) => (
|
||||||
<div key={j} alt={STATS[s].stat}>
|
<div key={j} alt={STATS[s].stat}>
|
||||||
@ -170,7 +236,7 @@ class GameConstruct extends Component {
|
|||||||
setTutorialGameClear(activeSkill, tutorialGame);
|
setTutorialGameClear(activeSkill, tutorialGame);
|
||||||
}}
|
}}
|
||||||
style={ activeSkill ? { cursor: 'pointer' } : {}}
|
style={ activeSkill ? { cursor: 'pointer' } : {}}
|
||||||
class={`game-construct ${ko} ${classes}`} >
|
class={`game-construct ${ko} ${cssClass}`}>
|
||||||
<div class="left">
|
<div class="left">
|
||||||
{crypSkills}
|
{crypSkills}
|
||||||
{effectBox()}
|
{effectBox()}
|
||||||
@ -179,6 +245,7 @@ class GameConstruct extends Component {
|
|||||||
<div class="stats"> {stats} </div>
|
<div class="stats"> {stats} </div>
|
||||||
<ConstructAvatar construct={construct} />
|
<ConstructAvatar construct={construct} />
|
||||||
<ConstructText construct={construct} />
|
<ConstructText construct={construct} />
|
||||||
|
<ConstructAnimationText construct={construct} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -32,11 +32,9 @@ const addState = connect(
|
|||||||
function setNav(place) {
|
function setNav(place) {
|
||||||
dispatch(actions.setGame(null));
|
dispatch(actions.setGame(null));
|
||||||
dispatch(actions.setInstance(null));
|
dispatch(actions.setInstance(null));
|
||||||
dispatch(actions.setReclaiming(false));
|
|
||||||
dispatch(actions.setActiveSkill(null));
|
dispatch(actions.setActiveSkill(null));
|
||||||
dispatch(actions.setInfo(null));
|
dispatch(actions.setInfo(null));
|
||||||
dispatch(actions.setItemUnequip([]));
|
dispatch(actions.setItemUnequip([]));
|
||||||
dispatch(actions.setVboxHighlight([]));
|
|
||||||
dispatch(actions.setMtxActive(null));
|
dispatch(actions.setMtxActive(null));
|
||||||
return dispatch(actions.setNav(place));
|
return dispatch(actions.setNav(place));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,184 +0,0 @@
|
|||||||
const preact = require('preact');
|
|
||||||
const reactStringReplace = require('react-string-replace');
|
|
||||||
|
|
||||||
const specThresholds = require('./info.thresholds');
|
|
||||||
const { INFO } = require('./../constants');
|
|
||||||
const { convertItem, removeTier } = require('../utils');
|
|
||||||
const { tutorialStage } = require('../tutorial.utils');
|
|
||||||
const shapes = require('./shapes');
|
|
||||||
|
|
||||||
|
|
||||||
class InfoComponent extends preact.Component {
|
|
||||||
shouldComponentUpdate(newProps, newState) {
|
|
||||||
if (newProps.tutorial !== this.props.tutorial) return true;
|
|
||||||
// We don't care about info during tutorial
|
|
||||||
if (newProps.tutorial && this.props.instance.time_control === 'Practice'
|
|
||||||
&& this.props.instance.rounds.length === 1) return false;
|
|
||||||
if (newProps.info !== this.props.info) return true;
|
|
||||||
if (newState.comboItem !== this.state.comboItem) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
// Catch case where mouse events don't properly clear state and info changed
|
|
||||||
if (prevProps.info !== this.props.info && this.state.comboItem) this.setState({ comboItem: null });
|
|
||||||
}
|
|
||||||
|
|
||||||
render(args) {
|
|
||||||
const {
|
|
||||||
// Variables that will change
|
|
||||||
info,
|
|
||||||
tutorial,
|
|
||||||
|
|
||||||
// Static
|
|
||||||
player, // Only used for colour calcs which will be update if info changes
|
|
||||||
ws,
|
|
||||||
itemInfo,
|
|
||||||
instance, // Only used for instance id
|
|
||||||
// functions
|
|
||||||
setInfo,
|
|
||||||
setTutorialNull,
|
|
||||||
} = args;
|
|
||||||
const { comboItem } = this.state;
|
|
||||||
function Info() {
|
|
||||||
if (tutorial) {
|
|
||||||
const tutorialStageInfo = tutorialStage(tutorial, ws, setTutorialNull, instance);
|
|
||||||
if (tutorialStageInfo) return tutorialStageInfo;
|
|
||||||
}
|
|
||||||
if (!info) return false;
|
|
||||||
if (info.includes('constructName')) {
|
|
||||||
return (
|
|
||||||
<div class='info-item'>
|
|
||||||
<h2> {info.replace('constructName ', '')} </h2>
|
|
||||||
<p> This is the name of your construct. <br />
|
|
||||||
Names are randomly generated and are purely cosmetic. <br />
|
|
||||||
You can change change your construct name in the <b>RESHAPE</b> tab outside of games.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.includes('constructAvatar')) {
|
|
||||||
return (
|
|
||||||
<div class='info-item'>
|
|
||||||
<h2> {info.replace('constructAvatar ', '')} </h2>
|
|
||||||
<p> This is your construct avatar. <br />
|
|
||||||
Avatars are randomly generated and are purely cosmetic. <br />
|
|
||||||
You can change your construct avatar in the <b>RESHAPE</b> tab outside of games.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const fullInfo = comboItem
|
|
||||||
? itemInfo.items.find(i => i.item === comboItem) || INFO[comboItem]
|
|
||||||
: itemInfo.items.find(i => i.item === info) || INFO[info];
|
|
||||||
if (!fullInfo) return false;
|
|
||||||
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 <div>{reactStringReplace(infoDescription, '\n', () => <br />)}</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 ? <h3> SKILL </h3> : <h3> SPEC </h3>;
|
|
||||||
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 ? <div>{fullInfo.cooldown} Turn delay</div> : null;
|
|
||||||
|
|
||||||
const speed = isSkill
|
|
||||||
? <div> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </div>
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const thresholds = isSpec ? specThresholds(player, fullInfo, info) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class={isSkill ? 'info-item' : 'info-item'}>
|
|
||||||
<h2>{infoName} {fullInfo.cost}b</h2>
|
|
||||||
{header}
|
|
||||||
{itemSourceInfo}
|
|
||||||
{cooldown}
|
|
||||||
{itemDescription()}
|
|
||||||
{speed}
|
|
||||||
{thresholds}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const cost = fullInfo.cost ? `- ${fullInfo.cost}b` : false;
|
|
||||||
return (
|
|
||||||
<div class="info-item">
|
|
||||||
<h2>{fullInfo.item} {cost}</h2>
|
|
||||||
{itemDescription()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Combos = () => {
|
|
||||||
if (tutorial && instance.time_control === 'Practice' && instance.rounds.length === 1) return false;
|
|
||||||
const generalNotes = (
|
|
||||||
<div>
|
|
||||||
<h2> General </h2>
|
|
||||||
<p>
|
|
||||||
You can preview combos by clicking the combined item when it appears in this section. <br />
|
|
||||||
Click the <b>READY</b> button to start the <b>GAME PHASE</b>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
if (!player) return generalNotes;
|
|
||||||
if (!info) return generalNotes;
|
|
||||||
|
|
||||||
const vboxCombos = itemInfo.combos.filter(c => c.components.includes(info));
|
|
||||||
if (vboxCombos.length > 6 || vboxCombos.length === 0) return generalNotes;
|
|
||||||
|
|
||||||
const comboTable = vboxCombos.map((c, i) => {
|
|
||||||
const mouseOver = e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
this.setState({ comboItem: c.item });
|
|
||||||
};
|
|
||||||
const componentTable = (c.components.some(ci => ['Red', 'Blue', 'Green'].includes(ci)))
|
|
||||||
? [<div key="0">{convertItem(c.components[0])} {convertItem(c.components[1])}</div>,
|
|
||||||
<div key="1">{convertItem(c.components[2])}</div>]
|
|
||||||
: c.components.map((u, j) => <div key={j} >{convertItem(u)}</div>);
|
|
||||||
return (
|
|
||||||
<div key={i} onMouseOver={mouseOver} class="table-button" onClick={() => setInfo(c.item)}>
|
|
||||||
<div class="item">
|
|
||||||
{convertItem(c.item)}
|
|
||||||
</div>
|
|
||||||
{componentTable}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<div class="combos">
|
|
||||||
{comboTable}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class='info' onMouseOver={() => this.setState({ comboItem: null })}>
|
|
||||||
<Info />
|
|
||||||
<Combos />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = InfoComponent;
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
const { connect } = require('preact-redux');
|
|
||||||
|
|
||||||
const actions = require('../actions');
|
|
||||||
const Info = require('./info.component');
|
|
||||||
|
|
||||||
const addState = connect(
|
|
||||||
function receiveState(state) {
|
|
||||||
const {
|
|
||||||
ws,
|
|
||||||
info,
|
|
||||||
itemInfo,
|
|
||||||
instance,
|
|
||||||
player,
|
|
||||||
account,
|
|
||||||
tutorial,
|
|
||||||
} = state;
|
|
||||||
|
|
||||||
return {
|
|
||||||
ws,
|
|
||||||
info,
|
|
||||||
itemInfo,
|
|
||||||
instance,
|
|
||||||
player,
|
|
||||||
account,
|
|
||||||
tutorial,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
function receiveDispatch(dispatch) {
|
|
||||||
function setTutorialNull() {
|
|
||||||
dispatch(actions.setTutorial(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
function setInfo(info) {
|
|
||||||
dispatch(actions.setInfo(info));
|
|
||||||
}
|
|
||||||
return { setTutorialNull, setInfo };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
module.exports = addState(Info);
|
|
||||||
@ -2,7 +2,6 @@ const preact = require('preact');
|
|||||||
const { connect } = require('preact-redux');
|
const { connect } = require('preact-redux');
|
||||||
|
|
||||||
const Vbox = require('./vbox.component');
|
const Vbox = require('./vbox.component');
|
||||||
const InfoContainer = require('./info.container');
|
|
||||||
const InstanceConstructsContainer = require('./instance.constructs');
|
const InstanceConstructsContainer = require('./instance.constructs');
|
||||||
const Faceoff = require('./faceoff');
|
const Faceoff = require('./faceoff');
|
||||||
|
|
||||||
@ -11,10 +10,12 @@ const actions = require('../actions');
|
|||||||
const addState = connect(
|
const addState = connect(
|
||||||
function receiveState(state) {
|
function receiveState(state) {
|
||||||
const {
|
const {
|
||||||
|
comboPreview,
|
||||||
instance,
|
instance,
|
||||||
nav,
|
nav,
|
||||||
} = state;
|
} = state;
|
||||||
return {
|
return {
|
||||||
|
comboPreview,
|
||||||
instance,
|
instance,
|
||||||
nav,
|
nav,
|
||||||
};
|
};
|
||||||
@ -27,14 +28,20 @@ const addState = connect(
|
|||||||
|
|
||||||
|
|
||||||
function clearItems() {
|
function clearItems() {
|
||||||
dispatch(actions.setReclaiming(false));
|
|
||||||
dispatch(actions.setItemUnequip([]));
|
dispatch(actions.setItemUnequip([]));
|
||||||
dispatch(actions.setVboxHighlight([]));
|
dispatch(actions.setVboxCombiner(null));
|
||||||
dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
|
dispatch(actions.setVboxHighlight(null));
|
||||||
|
dispatch(actions.setVboxInfo(null));
|
||||||
|
dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearComboPreview() {
|
||||||
|
return dispatch(actions.setComboPreview(null));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
clearComboPreview,
|
||||||
setInfo,
|
setInfo,
|
||||||
clearItems,
|
clearItems,
|
||||||
};
|
};
|
||||||
@ -43,7 +50,9 @@ const addState = connect(
|
|||||||
|
|
||||||
function Instance(args) {
|
function Instance(args) {
|
||||||
const {
|
const {
|
||||||
|
comboPreview,
|
||||||
instance,
|
instance,
|
||||||
|
clearComboPreview,
|
||||||
clearItems,
|
clearItems,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
@ -58,14 +67,14 @@ function Instance(args) {
|
|||||||
clearItems();
|
clearItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTouchMove(e) {
|
function mouseOver(e) {
|
||||||
e.preventDefault();
|
e.stopPropagation();
|
||||||
|
if (comboPreview) clearComboPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main id="instance" class='instance' onClick={instanceClick}>
|
<main id="instance" class='instance' onClick={instanceClick} onMouseOver={mouseOver}>
|
||||||
<Vbox />
|
<Vbox />
|
||||||
<InfoContainer />
|
|
||||||
<InstanceConstructsContainer />
|
<InstanceConstructsContainer />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -24,8 +24,8 @@ const addState = connect(
|
|||||||
tutorial,
|
tutorial,
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
function sendVboxAcceptEquip(constructId) {
|
function sendVboxBuyEquip(constructId) {
|
||||||
return ws.sendVboxAcceptEquip(instance.id, vboxSelected.shopSelect[0][0], vboxSelected.shopSelect[0][1], constructId);
|
return ws.sendVboxBuyEquip(instance.id, vboxSelected.storeSelect[0][0], vboxSelected.storeSelect[0][1], constructId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxApply(constructId, i) {
|
function sendVboxApply(constructId, i) {
|
||||||
@ -40,7 +40,7 @@ const addState = connect(
|
|||||||
instance,
|
instance,
|
||||||
player,
|
player,
|
||||||
account,
|
account,
|
||||||
sendVboxAcceptEquip,
|
sendVboxBuyEquip,
|
||||||
sendVboxUnequipApply,
|
sendVboxUnequipApply,
|
||||||
sendVboxApply,
|
sendVboxApply,
|
||||||
itemInfo,
|
itemInfo,
|
||||||
@ -60,7 +60,7 @@ const addState = connect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setItemUnequip(v) {
|
function setItemUnequip(v) {
|
||||||
dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
|
dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
|
||||||
return dispatch(actions.setItemUnequip(v));
|
return dispatch(actions.setItemUnequip(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ function Construct(props) {
|
|||||||
itemInfo,
|
itemInfo,
|
||||||
// Function Calls
|
// Function Calls
|
||||||
sendVboxApply,
|
sendVboxApply,
|
||||||
sendVboxAcceptEquip,
|
sendVboxBuyEquip,
|
||||||
sendVboxUnequipApply,
|
sendVboxUnequipApply,
|
||||||
setItemUnequip,
|
setItemUnequip,
|
||||||
setInfo,
|
setInfo,
|
||||||
@ -91,14 +91,14 @@ function Construct(props) {
|
|||||||
|
|
||||||
const { vbox } = player;
|
const { vbox } = player;
|
||||||
|
|
||||||
const itemEquip = vboxSelected.shopSelect.length === 0 && vboxSelected.stashSelect.length === 1
|
const itemEquip = vboxSelected.storeSelect.length === 0 && vboxSelected.stashSelect.length === 1
|
||||||
? vboxSelected.stashSelect[0]
|
? vboxSelected.stashSelect[0]
|
||||||
: -1;
|
: -1;
|
||||||
|
|
||||||
const duplicateSkill = construct.skills.length !== 0 && construct.skills.every(sk => {
|
const duplicateSkill = construct.skills.length !== 0 && construct.skills.every(sk => {
|
||||||
if (!itemEquip && itemEquip !== 0) return false;
|
if (!itemEquip && itemEquip !== 0) return false;
|
||||||
if (!sk) return false;
|
if (!sk) return false;
|
||||||
return sk.skill === vbox.bound[itemEquip];
|
return sk.skill === vbox.stash[itemEquip];
|
||||||
});
|
});
|
||||||
const tutorialDisableEquip = tutorialShouldDisableEquip(tutorial, iter, instance, construct);
|
const tutorialDisableEquip = tutorialShouldDisableEquip(tutorial, iter, instance, construct);
|
||||||
function onClick(e) {
|
function onClick(e) {
|
||||||
@ -106,7 +106,7 @@ function Construct(props) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (duplicateSkill || tutorialDisableEquip) return true;
|
if (duplicateSkill || tutorialDisableEquip) return true;
|
||||||
if (itemEquip !== -1) return sendVboxApply(construct.id, itemEquip);
|
if (itemEquip !== -1) return sendVboxApply(construct.id, itemEquip);
|
||||||
if (vboxSelected.shopSelect.length === 1) return sendVboxAcceptEquip(construct.id);
|
if (vboxSelected.storeSelect.length === 1) return sendVboxBuyEquip(construct.id);
|
||||||
if (itemUnequip.length && itemUnequip[0] !== construct.id) return sendVboxUnequipApply(construct.id);
|
if (itemUnequip.length && itemUnequip[0] !== construct.id) return sendVboxUnequipApply(construct.id);
|
||||||
setItemUnequip([]);
|
setItemUnequip([]);
|
||||||
return true;
|
return true;
|
||||||
@ -114,7 +114,7 @@ function Construct(props) {
|
|||||||
function hoverInfo(e, info) {
|
function hoverInfo(e, info) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (!info) return false;
|
if (!info) return false;
|
||||||
if (vboxSelected.shopSelect.length || vboxSelected.stashSelect.length) return false;
|
if (vboxSelected.storeSelect.length || vboxSelected.stashSelect.length) return false;
|
||||||
return setInfo(info);
|
return setInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,12 +129,14 @@ function Construct(props) {
|
|||||||
|
|
||||||
function skillClick(e) {
|
function skillClick(e) {
|
||||||
if (!skill) return false;
|
if (!skill) return false;
|
||||||
setItemUnequip([construct.id, skill.skill, i]);
|
|
||||||
e.stopPropagation();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const equipping = skillList.includes(vbox.bound[itemEquip]) && !skill
|
const equipping = skillList.includes(vbox.stash[itemEquip]) && !skill
|
||||||
&& !tutorialDisableEquip && !duplicateSkill && i === construct.skills.length;
|
&& !tutorialDisableEquip && !duplicateSkill && i === construct.skills.length;
|
||||||
const border = () => {
|
const border = () => {
|
||||||
if (!skill) return '';
|
if (!skill) return '';
|
||||||
@ -167,7 +169,7 @@ function Construct(props) {
|
|||||||
const s = construct.specs[i];
|
const s = construct.specs[i];
|
||||||
|
|
||||||
if (!s) {
|
if (!s) {
|
||||||
const equipping = specList.includes(vbox.bound[itemEquip]) && i === construct.specs.length;
|
const equipping = specList.includes(vbox.stash[itemEquip]) && i === construct.specs.length;
|
||||||
const classes = `${equipping ? 'equipping' : 'gray'} empty`;
|
const classes = `${equipping ? 'equipping' : 'gray'} empty`;
|
||||||
return (
|
return (
|
||||||
<button key={i} class={classes} disabled={!equipping} >
|
<button key={i} class={classes} disabled={!equipping} >
|
||||||
@ -177,8 +179,11 @@ function Construct(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function specClick(e) {
|
function specClick(e) {
|
||||||
|
if (!s) return false;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setItemUnequip([construct.id, s, i]);
|
if (itemUnequip.length && itemUnequip[0] === construct.id && itemUnequip[1] === s
|
||||||
|
&& i === itemUnequip[2]) return setItemUnequip([]);
|
||||||
|
return setItemUnequip([construct.id, s, i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const highlight = itemUnequip[0] === construct.id && itemUnequip[1] === s && i === itemUnequip[2];
|
const highlight = itemUnequip[0] === construct.id && itemUnequip[1] === s && i === itemUnequip[2];
|
||||||
@ -256,9 +261,8 @@ class InstanceConstructs extends preact.Component {
|
|||||||
// Function calls
|
// Function calls
|
||||||
setInfo,
|
setInfo,
|
||||||
sendVboxApply,
|
sendVboxApply,
|
||||||
sendVboxAcceptEquip,
|
sendVboxBuyEquip,
|
||||||
sendVboxUnequipApply,
|
sendVboxUnequipApply,
|
||||||
setVboxHighlight,
|
|
||||||
setItemUnequip,
|
setItemUnequip,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -277,11 +281,10 @@ class InstanceConstructs extends preact.Component {
|
|||||||
setItemUnequip,
|
setItemUnequip,
|
||||||
player,
|
player,
|
||||||
sendVboxApply,
|
sendVboxApply,
|
||||||
sendVboxAcceptEquip,
|
sendVboxBuyEquip,
|
||||||
sendVboxUnequipApply,
|
sendVboxUnequipApply,
|
||||||
setInfo,
|
setInfo,
|
||||||
itemInfo,
|
itemInfo,
|
||||||
setVboxHighlight,
|
|
||||||
vboxSelected,
|
vboxSelected,
|
||||||
tutorial,
|
tutorial,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -26,6 +26,9 @@ const addState = connect(
|
|||||||
dispatch(actions.setNav('play'));
|
dispatch(actions.setNav('play'));
|
||||||
dispatch(actions.setGame(null));
|
dispatch(actions.setGame(null));
|
||||||
dispatch(actions.setInstance(null));
|
dispatch(actions.setInstance(null));
|
||||||
|
dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
|
||||||
|
dispatch(actions.setInfo(null));
|
||||||
|
dispatch(actions.setItemUnequip([]));
|
||||||
if (tutorial) dispatch(actions.setTutorial(1));
|
if (tutorial) dispatch(actions.setTutorial(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ function Mnml(args) {
|
|||||||
instance,
|
instance,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
const rotateClass = (game || instance) && window.innerWidth < window.innerHeight
|
const rotateClass = (game || instance) && window.innerHeight < 900 && window.innerWidth < window.innerHeight
|
||||||
? 'show'
|
? 'show'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
|
|||||||
@ -33,11 +33,9 @@ const addState = connect(
|
|||||||
function setNav(place) {
|
function setNav(place) {
|
||||||
dispatch(actions.setGame(null));
|
dispatch(actions.setGame(null));
|
||||||
dispatch(actions.setInstance(null));
|
dispatch(actions.setInstance(null));
|
||||||
dispatch(actions.setReclaiming(false));
|
|
||||||
dispatch(actions.setActiveSkill(null));
|
dispatch(actions.setActiveSkill(null));
|
||||||
dispatch(actions.setInfo(null));
|
dispatch(actions.setInfo(null));
|
||||||
dispatch(actions.setItemUnequip([]));
|
dispatch(actions.setItemUnequip([]));
|
||||||
dispatch(actions.setVboxHighlight([]));
|
|
||||||
|
|
||||||
return dispatch(actions.setNav(place));
|
return dispatch(actions.setNav(place));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,7 +69,7 @@ function Skill(props) {
|
|||||||
// return false;
|
// return false;
|
||||||
// }
|
// }
|
||||||
const cdText = construct.skills[i].cd > 0
|
const cdText = construct.skills[i].cd > 0
|
||||||
? `- ${s.cd}T`
|
? `${s.cd}T`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const highlight = activeSkill
|
const highlight = activeSkill
|
||||||
@ -91,7 +91,7 @@ function Skill(props) {
|
|||||||
onMouseOut={e => hoverInfo(e, null)}
|
onMouseOut={e => hoverInfo(e, null)}
|
||||||
type="submit"
|
type="submit"
|
||||||
onClick={onClick}>
|
onClick={onClick}>
|
||||||
<span>{s.skill} {cdText}</span>
|
<span>{s.skill} <br /> {cdText}</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,21 +81,7 @@ class TargetSvg extends Component {
|
|||||||
|
|
||||||
// resolutions happening
|
// resolutions happening
|
||||||
// just put skill name up
|
// just put skill name up
|
||||||
if (animating) {
|
if (animating) return false;
|
||||||
if (!animSkill) return false;
|
|
||||||
const itemSource = itemInfo.combos.filter(c => c.item === removeTier(animSkill));
|
|
||||||
const itemSourceInfo = itemSource.length
|
|
||||||
? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}`
|
|
||||||
: false;
|
|
||||||
const itemRegEx = /(Red|Blue|Green)/;
|
|
||||||
const itemSourceDescription = reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]());
|
|
||||||
return (
|
|
||||||
<div class="resolving-skill">
|
|
||||||
<h1>{animSkill}</h1>
|
|
||||||
<div>{itemSourceDescription}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const playerTeam = game.players.find(t => t.id === account.id);
|
const playerTeam = game.players.find(t => t.id === account.id);
|
||||||
const otherTeam = game.players.find(t => t.id !== account.id);
|
const otherTeam = game.players.find(t => t.id !== account.id);
|
||||||
@ -110,8 +96,11 @@ class TargetSvg extends Component {
|
|||||||
? playerTeam.constructs.findIndex(c => c.id === cast.target_construct_id)
|
? playerTeam.constructs.findIndex(c => c.id === cast.target_construct_id)
|
||||||
: otherTeam.constructs.findIndex(c => c.id === cast.target_construct_id);
|
: otherTeam.constructs.findIndex(c => c.id === cast.target_construct_id);
|
||||||
|
|
||||||
|
const skillNumber = window.innerWidth <= 800 // mobile styling trigger
|
||||||
|
? playerTeam.constructs[source].skills.findIndex(s => s.skill === cast.skill)
|
||||||
|
: 0;
|
||||||
const sourceY = height;
|
const sourceY = height;
|
||||||
const sourceX = (source * width / 3) + width / 24;
|
const sourceX = (source * width / 3) + width / 18 + skillNumber * (width / 9);
|
||||||
const targetX = (target * width / 3) + width / 6
|
const targetX = (target * width / 3) + width / 6
|
||||||
+ (defensive ? width / 64 : 0)
|
+ (defensive ? width / 64 : 0)
|
||||||
+ (source * width / 18);
|
+ (source * width / 18);
|
||||||
|
|||||||
67
client/src/components/vbox.combiner.jsx
Normal file
67
client/src/components/vbox.combiner.jsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
const preact = require('preact');
|
||||||
|
const { connect } = require('preact-redux');
|
||||||
|
|
||||||
|
const addState = connect(({ vboxCombiner }) => ({ vboxCombiner }));
|
||||||
|
|
||||||
|
class Combiner extends preact.Component {
|
||||||
|
shouldComponentUpdate(newProps) {
|
||||||
|
if (newProps.vbox !== this.props.vbox) return true;
|
||||||
|
if (newProps.vboxSelected !== this.props.vboxSelected) return true;
|
||||||
|
if (newProps.vboxBuySelected !== this.props.vboxBuySelected) return true;
|
||||||
|
if (newProps.sendVboxCombine !== this.props.sendVboxCombine) return true;
|
||||||
|
if (newProps.vboxCombiner !== this.props.vboxCombiner) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(args) {
|
||||||
|
const {
|
||||||
|
// passed props
|
||||||
|
vbox,
|
||||||
|
vboxSelected,
|
||||||
|
vboxBuySelected,
|
||||||
|
sendVboxCombine,
|
||||||
|
// state props
|
||||||
|
vboxCombiner,
|
||||||
|
} = args;
|
||||||
|
|
||||||
|
const { stashSelect, storeSelect } = vboxSelected;
|
||||||
|
|
||||||
|
function cost([group, i]) {
|
||||||
|
if (group === 'Colours') return 1;
|
||||||
|
if (group === 'Skills') return 2;
|
||||||
|
if (group === 'Specs') return 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (vboxCombiner) {
|
||||||
|
const combinerComboText = vboxCombiner.replace('Plus', '+');
|
||||||
|
let bits = 0;
|
||||||
|
storeSelect.forEach(item => bits += cost(item));
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
class='combiner vbox-btn'
|
||||||
|
disabled={bits > vbox.bits}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
onMouseDown={sendVboxCombine}>
|
||||||
|
{bits ? `Buy ${combinerComboText} ${bits}b` : `Combine ${combinerComboText}`}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stashSelect.length === 0 && storeSelect.length === 1) {
|
||||||
|
const item = storeSelect[0];
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
class='combiner vbox-btn'
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
onMouseDown={vboxBuySelected}>
|
||||||
|
{`Buy ${vbox.store[item[0]][item[1]]} ${cost(item)}b`}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addState(Combiner);
|
||||||
86
client/src/components/vbox.combos.jsx
Normal file
86
client/src/components/vbox.combos.jsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
const preact = require('preact');
|
||||||
|
const { connect } = require('preact-redux');
|
||||||
|
|
||||||
|
const { convertItem } = require('../utils');
|
||||||
|
const actions = require('../actions');
|
||||||
|
|
||||||
|
const addState = connect(
|
||||||
|
function receiveState(state) {
|
||||||
|
const {
|
||||||
|
info, instance, itemInfo, tutorial, vboxInfo,
|
||||||
|
} = state;
|
||||||
|
return {
|
||||||
|
info, instance, itemInfo, tutorial, vboxInfo,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
function receiveDispatch(dispatch) {
|
||||||
|
function setComboPreview(item) {
|
||||||
|
return dispatch(actions.setComboPreview(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setComboPreview,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
class Combos extends preact.Component {
|
||||||
|
shouldComponentUpdate(newProps) {
|
||||||
|
if (newProps.info !== this.props.info) return true;
|
||||||
|
if (newProps.instance !== this.props.instance) return true;
|
||||||
|
if (newProps.itemInfo !== this.props.itemInfo) return true;
|
||||||
|
if (newProps.tutorial !== this.props.tutorial) return true;
|
||||||
|
if (newProps.vboxInfo !== this.props.vboxInfo) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(props) {
|
||||||
|
const {
|
||||||
|
info,
|
||||||
|
instance,
|
||||||
|
itemInfo,
|
||||||
|
tutorial,
|
||||||
|
vboxInfo,
|
||||||
|
setComboPreview,
|
||||||
|
} = props;
|
||||||
|
if (tutorial && instance.time_control === 'Practice' && instance.rounds.length === 1) return false;
|
||||||
|
|
||||||
|
const vboxCombos = itemInfo.combos.filter(c => c.components.includes(vboxInfo || info));
|
||||||
|
if (vboxCombos.length > 6 || vboxCombos.length === 0) return <div class="combos"></div>;
|
||||||
|
|
||||||
|
const comboTable = vboxCombos.map((c, i) => {
|
||||||
|
const mouseOver = e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setComboPreview(c.item);
|
||||||
|
};
|
||||||
|
const componentTable = (c.components.some(ci => ['Red', 'Blue', 'Green'].includes(ci)))
|
||||||
|
? [<div key="0">{convertItem(c.components[0])} {convertItem(c.components[1])}</div>,
|
||||||
|
<div key="1">{convertItem(c.components[2])}</div>]
|
||||||
|
: c.components.map((u, j) => <div key={j} >{convertItem(u)}</div>);
|
||||||
|
return (
|
||||||
|
<div key={i} onMouseOver={mouseOver} class="table-button">
|
||||||
|
<div class="item">
|
||||||
|
{convertItem(c.item)}
|
||||||
|
</div>
|
||||||
|
{componentTable}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div class="combos">
|
||||||
|
<div class="combo-header">
|
||||||
|
<h2>COMBOS</h2>
|
||||||
|
Combine colours and items.
|
||||||
|
</div>
|
||||||
|
<div class="combo-list"
|
||||||
|
onMouseOver={e => e.stopPropagation()}
|
||||||
|
onClick={e => e.stopPropagation()}>
|
||||||
|
{comboTable}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addState(Combos);
|
||||||
@ -1,43 +1,48 @@
|
|||||||
const preact = require('preact');
|
const preact = require('preact');
|
||||||
const { connect } = require('preact-redux');
|
const { connect } = require('preact-redux');
|
||||||
const range = require('lodash/range');
|
|
||||||
const countBy = require('lodash/countBy');
|
|
||||||
const without = require('lodash/without');
|
|
||||||
const forEach = require('lodash/forEach');
|
|
||||||
|
|
||||||
const { removeTier } = require('../utils');
|
|
||||||
const shapes = require('./shapes');
|
|
||||||
const actions = require('../actions');
|
const actions = require('../actions');
|
||||||
const buttons = require('./buttons');
|
|
||||||
|
const InfoContainer = require('./vbox.info');
|
||||||
|
const StashElement = require('./vbox.stash');
|
||||||
|
const StoreElement = require('./vbox.store');
|
||||||
|
const Combiner = require('./vbox.combiner');
|
||||||
|
const Combos = require('./vbox.combos');
|
||||||
|
|
||||||
|
const { setVboxState } = require('./vbox.utils');
|
||||||
|
|
||||||
const addState = connect(
|
const addState = connect(
|
||||||
function receiveState(state) {
|
function receiveState(state) {
|
||||||
const {
|
const {
|
||||||
ws,
|
ws,
|
||||||
|
itemUnequip,
|
||||||
instance,
|
instance,
|
||||||
player,
|
player,
|
||||||
reclaiming,
|
tutorial,
|
||||||
vboxSelected,
|
vboxSelected,
|
||||||
itemInfo,
|
itemInfo,
|
||||||
itemUnequip,
|
|
||||||
tutorial,
|
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
function sendVboxDiscard() {
|
function sendInstance() {
|
||||||
return ws.sendVboxDiscard(instance.id);
|
return ws.sendInstanceState(instance.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxAccept(group, index) {
|
function sendVboxRefill() {
|
||||||
|
return ws.sendVboxRefill(instance.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendVboxBuy(group, index) {
|
||||||
|
if (!(vboxSelected.storeSelect.length === 1 && vboxSelected.stashSelect.length === 0)) return false;
|
||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
return ws.sendVboxAccept(instance.id, group, index);
|
return ws.sendVboxBuy(instance.id, group, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxCombine() {
|
function sendVboxCombine() {
|
||||||
return ws.sendVboxCombine(instance.id, vboxSelected.stashSelect, vboxSelected.shopSelect);
|
return ws.sendVboxCombine(instance.id, vboxSelected.stashSelect, vboxSelected.storeSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxReclaim(i) {
|
function sendVboxRefund(i) {
|
||||||
return ws.sendVboxReclaim(instance.id, i);
|
return ws.sendVboxRefund(instance.id, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendItemUnequip([constructId, item]) {
|
function sendItemUnequip([constructId, item]) {
|
||||||
@ -45,85 +50,53 @@ const addState = connect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
itemUnequip,
|
||||||
instance,
|
instance,
|
||||||
player,
|
player,
|
||||||
reclaiming,
|
|
||||||
sendVboxAccept,
|
|
||||||
sendVboxCombine,
|
|
||||||
sendVboxDiscard,
|
|
||||||
sendVboxReclaim,
|
|
||||||
vboxSelected,
|
|
||||||
itemInfo,
|
|
||||||
itemUnequip,
|
|
||||||
sendItemUnequip,
|
|
||||||
tutorial,
|
tutorial,
|
||||||
|
vboxSelected,
|
||||||
|
|
||||||
|
itemInfo,
|
||||||
|
|
||||||
|
sendInstance,
|
||||||
|
sendItemUnequip,
|
||||||
|
sendVboxBuy,
|
||||||
|
sendVboxCombine,
|
||||||
|
sendVboxRefill,
|
||||||
|
sendVboxRefund,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
function receiveDispatch(dispatch) {
|
function receiveDispatch(dispatch) {
|
||||||
function setReclaiming(v) {
|
|
||||||
dispatch(actions.setItemUnequip([]));
|
|
||||||
dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
|
|
||||||
return dispatch(actions.setReclaiming(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
function setInfo(item) {
|
function setInfo(item) {
|
||||||
return dispatch(actions.setInfo(item));
|
return dispatch(actions.setInfo(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setVboxSelected(v) {
|
function setTutorial(stage) {
|
||||||
|
return dispatch(actions.setTutorial(stage));
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchVboxSelect(v, state) {
|
||||||
|
setVboxState(dispatch, v, state);
|
||||||
dispatch(actions.setItemUnequip([]));
|
dispatch(actions.setItemUnequip([]));
|
||||||
dispatch(actions.setVboxSelected(v));
|
|
||||||
return dispatch(actions.setVboxSelected(v));
|
return dispatch(actions.setVboxSelected(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setReclaiming,
|
dispatchVboxSelect,
|
||||||
setInfo,
|
setInfo,
|
||||||
setVboxSelected,
|
setTutorial,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
function validVboxSelect(vbox, itemInfo, shopSelect, stashSelect) {
|
|
||||||
if (shopSelect.length === 0 && stashSelect.length === 0) return false;
|
|
||||||
|
|
||||||
const validSelects = [];
|
|
||||||
|
|
||||||
const stashItems = stashSelect.map(j => vbox.bound[j]);
|
|
||||||
const shopItems = shopSelect.map(j => vbox.free[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) {
|
|
||||||
forEach(buyCount, (value, key) => {
|
|
||||||
if (value > 0 && !validSelects.includes(key)) validSelects.push(key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return validSelects;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Vbox extends preact.Component {
|
class Vbox extends preact.Component {
|
||||||
shouldComponentUpdate(newProps) {
|
shouldComponentUpdate(newProps) {
|
||||||
// Single variable props
|
|
||||||
if (newProps.itemUnequip !== this.props.itemUnequip) return true;
|
if (newProps.itemUnequip !== this.props.itemUnequip) return true;
|
||||||
if (newProps.reclaiming !== this.props.reclaiming) return true;
|
if (newProps.instance !== this.props.instance) return true;
|
||||||
|
if (newProps.player !== this.props.player) return true;
|
||||||
if (newProps.tutorial !== this.props.tutorial) return true;
|
if (newProps.tutorial !== this.props.tutorial) return true;
|
||||||
if (newProps.vboxSelected !== this.props.vboxSelected) return true;
|
if (newProps.vboxSelected !== this.props.vboxSelected) return true;
|
||||||
if (newProps.player !== this.props.player) return true;
|
|
||||||
if (newProps.instance !== this.props.instance) return true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,310 +104,132 @@ class Vbox extends preact.Component {
|
|||||||
const {
|
const {
|
||||||
// Changing state variables
|
// Changing state variables
|
||||||
itemUnequip,
|
itemUnequip,
|
||||||
|
instance,
|
||||||
player,
|
player,
|
||||||
reclaiming,
|
|
||||||
tutorial,
|
tutorial,
|
||||||
vboxSelected,
|
vboxSelected,
|
||||||
instance,
|
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
itemInfo,
|
itemInfo,
|
||||||
// Function Calls
|
// Function Calls
|
||||||
|
dispatchVboxSelect,
|
||||||
sendItemUnequip,
|
sendItemUnequip,
|
||||||
sendVboxAccept,
|
sendInstance,
|
||||||
|
sendVboxBuy,
|
||||||
sendVboxCombine,
|
sendVboxCombine,
|
||||||
sendVboxDiscard,
|
sendVboxRefill,
|
||||||
sendVboxReclaim,
|
sendVboxRefund,
|
||||||
setVboxSelected,
|
|
||||||
setInfo,
|
setInfo,
|
||||||
setReclaiming,
|
setTutorial,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
if (!player) return false;
|
if (!player) return false;
|
||||||
const { vbox } = player;
|
const { vbox } = player;
|
||||||
const { shopSelect, stashSelect } = vboxSelected;
|
const { storeSelect, stashSelect } = vboxSelected;
|
||||||
const vboxSelecting = shopSelect.length === 1 && stashSelect.length === 0;
|
|
||||||
|
|
||||||
function combinerChange(newStashSelect) {
|
const setVboxSelected = v => dispatchVboxSelect(v, { itemInfo, itemUnequip, vbox });
|
||||||
return setVboxSelected({ shopSelect, stashSelect: newStashSelect });
|
const clearVboxSelected = () => setVboxSelected({ storeSelect: [], stashSelect: [] });
|
||||||
}
|
const vboxBuySelected = () => sendVboxBuy(storeSelect[0][0], storeSelect[0][1]);
|
||||||
|
const clearTutorial = () => {
|
||||||
const vboxHighlight = validVboxSelect(vbox, itemInfo, shopSelect, stashSelect);
|
setTutorial(null);
|
||||||
//
|
sendInstance();
|
||||||
// VBOX
|
};
|
||||||
//
|
|
||||||
function vboxHover(e, v) {
|
function vboxHover(e, v) {
|
||||||
if (v) {
|
if (v) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (shopSelect.find(c => c[0])) return true; // There is a base skill or spec selected in the vbox
|
if (stashSelect.length !== 0 || storeSelect.length !== 0) return true;
|
||||||
if (stashSelect.length !== 0) {
|
|
||||||
const base = stashSelect.find(c => !['Red', 'Blue', 'Green'].includes(vbox.bound[c]));
|
|
||||||
if (base || base === 0) return true;
|
|
||||||
}
|
|
||||||
setInfo(v);
|
setInfo(v);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearVboxSelected() {
|
function storeHdr() {
|
||||||
setVboxSelected({ shopSelect: [], stashSelect: [] });
|
|
||||||
}
|
|
||||||
|
|
||||||
function vboxBuySelected() {
|
|
||||||
if (!vboxSelecting) return false;
|
|
||||||
document.activeElement.blur();
|
|
||||||
sendVboxAccept(shopSelect[0][0], shopSelect[0][1]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function availableBtn(v, group, index) {
|
|
||||||
if (!v) return <button disabled class='empty' key={(group * 10) + index} > </button>;
|
|
||||||
const selected = shopSelect.length && shopSelect.some(vs => vs[0] === group && vs[1] === index);
|
|
||||||
|
|
||||||
const comboHighlight = vboxHighlight && vboxHighlight.includes(v) ? 'combo-border' : '';
|
|
||||||
|
|
||||||
function onClick(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (!comboHighlight) setInfo(vbox.free[group][index]);
|
|
||||||
if (shopSelect.length && shopSelect.some(vs => vs[0] === group && vs[1] === index)) {
|
|
||||||
return setVboxSelected({ shopSelect: shopSelect.filter(vs => !(vs[0] === group && vs[1] === index)), stashSelect });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shopSelect.length && !stashSelect.length) return setVboxSelected({ shopSelect: [[group, index]], stashSelect });
|
|
||||||
if (comboHighlight !== 'combo-border') {
|
|
||||||
return setVboxSelected({ shopSelect: [[group, index]], stashSelect: [] });
|
|
||||||
}
|
|
||||||
return setVboxSelected({ shopSelect: [...shopSelect, [group, index]], stashSelect });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const classes = `${v.toLowerCase()} ${selected ? 'highlight' : ''} ${comboHighlight}`;
|
|
||||||
|
|
||||||
const vboxObject = shapes[v] ? shapes[v]() : v;
|
|
||||||
const disabled = vbox.bits <= group;
|
|
||||||
return (
|
return (
|
||||||
<label draggable='true'
|
<div class="store-hdr">
|
||||||
onDragStart={ev => ev.dataTransfer.setData('text', '')}
|
<h2
|
||||||
key={group * 10 + index}
|
onTouchStart={e => e.target.scrollIntoView(true)}
|
||||||
onDragEnd={clearVboxSelected}>
|
onMouseOver={e => vboxHover(e, 'store')}> STORE
|
||||||
<button
|
</h2>
|
||||||
class={classes}
|
<h1 class={`bits ${vbox.bits < 3 ? 'red' : false}`} onMouseOver={e => vboxHover(e, 'bits')}>
|
||||||
disabled={disabled}
|
{vbox.bits}b
|
||||||
onMouseOver={e => vboxHover(e, v)}
|
</h1>
|
||||||
onMouseDown={onClick}
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
> {vboxObject}
|
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function vboxElement() {
|
|
||||||
return (
|
|
||||||
<div class='vbox-vbox'
|
|
||||||
onClick={e => e.stopPropagation()}>
|
|
||||||
<div class="vbox-hdr">
|
|
||||||
<h3
|
|
||||||
onTouchStart={e => e.target.scrollIntoView(true)}
|
|
||||||
onMouseOver={e => hoverInfo(e, 'vbox')}> VBOX
|
|
||||||
</h3>
|
|
||||||
<div class={`bits ${vbox.bits < 3 ? 'red' : false}`} onMouseOver={e => hoverInfo(e, 'bits')} >{vbox.bits}b</div>
|
|
||||||
</div>
|
|
||||||
<div class="vbox-colours">
|
|
||||||
{range(0, 6).map(i => availableBtn(vbox.free[0][i], 0, i))}
|
|
||||||
</div>
|
|
||||||
<div class="vbox-items">
|
|
||||||
{range(0, 3).map(i => availableBtn(vbox.free[1][i], 1, i))}
|
|
||||||
{range(0, 3).map(i => availableBtn(vbox.free[2][i], 2, i))}
|
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
class='vbox-btn'
|
class='vbox-btn'
|
||||||
onMouseOver={e => hoverInfo(e, 'refill')}
|
onMouseOver={e => vboxHover(e, 'refill')}
|
||||||
disabled={vbox.bits < 2
|
disabled={vbox.bits < 2
|
||||||
|| (tutorial && tutorial < 7 && instance.time_control === 'Practice' && instance.rounds.length === 1)
|
|| (tutorial && tutorial < 7
|
||||||
|
&& instance.time_control === 'Practice' && instance.rounds.length === 1)
|
||||||
}
|
}
|
||||||
onClick={e => e.stopPropagation()}
|
onClick={e => e.stopPropagation()}
|
||||||
onMouseDown={() => sendVboxDiscard()}>
|
onMouseDown={() => sendVboxRefill()}>
|
||||||
refill - 2b
|
refill <br />
|
||||||
|
2b
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
function stashHdr() {
|
||||||
// INVENTORY
|
const refund = storeSelect.length === 0 && stashSelect.length === 1
|
||||||
//
|
? itemInfo.items.find(i => i.item === vbox.stash[stashSelect[0]]).cost
|
||||||
function reclaimClick(e) {
|
: 0;
|
||||||
e.stopPropagation();
|
const tutorialDisabled = tutorial && tutorial < 8
|
||||||
return setReclaiming(!reclaiming);
|
&& instance.time_control === 'Practice' && instance.rounds.length === 1;
|
||||||
}
|
const refundBtn = (
|
||||||
|
|
||||||
function inventoryBtn(v, i) {
|
|
||||||
const inventoryHighlight = vboxSelecting || itemUnequip.length;
|
|
||||||
|
|
||||||
if (!v && v !== 0) {
|
|
||||||
const emptyInvClick = () => {
|
|
||||||
if (vboxSelecting) return vboxBuySelected();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
return <button key={i} onClick={emptyInvClick} disabled={!inventoryHighlight} class={inventoryHighlight ? 'receiving' : 'empty'} > </button>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const comboHighlight = vboxHighlight && vboxHighlight.includes(v) ? 'combo-border' : '';
|
|
||||||
|
|
||||||
function onClick(type) {
|
|
||||||
if (reclaiming) return sendVboxReclaim(i);
|
|
||||||
|
|
||||||
const combinerContainsIndex = stashSelect.indexOf(i) > -1;
|
|
||||||
// removing
|
|
||||||
if (combinerContainsIndex) {
|
|
||||||
if (type === 'click') {
|
|
||||||
return combinerChange(without(stashSelect, i));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!comboHighlight) {
|
|
||||||
setInfo(vbox.bound[i]);
|
|
||||||
return setVboxSelected({ shopSelect: [], stashSelect: [i] });
|
|
||||||
}
|
|
||||||
|
|
||||||
stashSelect.push(i);
|
|
||||||
// if (stashSelect.length === 3) setInfo(comboItem.item);
|
|
||||||
return combinerChange(stashSelect);
|
|
||||||
}
|
|
||||||
|
|
||||||
const highlighted = stashSelect.indexOf(i) > -1;
|
|
||||||
const border = buttons[removeTier(v)] ? buttons[removeTier(v)]() : '';
|
|
||||||
const classes = `${highlighted ? 'highlight' : border} ${comboHighlight}`;
|
|
||||||
|
|
||||||
const invObject = shapes[v] ? shapes[v]() : v;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
key={i}
|
|
||||||
draggable='true'
|
|
||||||
onDragStart={ev => {
|
|
||||||
onClick('drag');
|
|
||||||
ev.dataTransfer.setData('text', '');
|
|
||||||
}}>
|
|
||||||
<button
|
|
||||||
class={classes}
|
|
||||||
onMouseOver={e => vboxHover(e, v)}
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
onMouseDown={() => onClick('click')}>
|
|
||||||
{invObject}
|
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function combinerBtn() {
|
|
||||||
let text = '';
|
|
||||||
let mouseEvent = false;
|
|
||||||
const combineLength = stashSelect.length + shopSelect.length;
|
|
||||||
if (vboxHighlight && vboxHighlight.length === 0) {
|
|
||||||
// The selected items can't be combined with additional items therefore valid combo
|
|
||||||
const stashItems = stashSelect.map(j => vbox.bound[j]);
|
|
||||||
const shopItems = shopSelect.map(j => vbox.free[j[0]][j[1]]);
|
|
||||||
const selectedItems = stashItems.concat(shopItems);
|
|
||||||
const combinerCount = countBy(selectedItems, co => co);
|
|
||||||
|
|
||||||
const comboItemObj = itemInfo.combos.find(combo => selectedItems.every(c => {
|
|
||||||
if (!combo.components.includes(c)) return false;
|
|
||||||
const comboCount = countBy(combo.components, co => co);
|
|
||||||
if (combinerCount[c] > comboCount[c]) return false;
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
let comboItem = comboItemObj ? comboItemObj.item : 'refine';
|
|
||||||
setInfo(comboItem);
|
|
||||||
comboItem = comboItem.replace('Plus', '+');
|
|
||||||
let bits = 0;
|
|
||||||
shopSelect.forEach(item => bits += item[0] + 1);
|
|
||||||
text = bits
|
|
||||||
? `Buy ${comboItem} - ${bits}b`
|
|
||||||
: `Combine - ${comboItem}`;
|
|
||||||
if (vbox.bits >= bits) mouseEvent = sendVboxCombine;
|
|
||||||
} else if (stashSelect.length === 0 && shopSelect.length === 1) {
|
|
||||||
const item = shopSelect[0];
|
|
||||||
text = `Buy ${vbox.free[item[0]][item[1]]} ${item[0] + 1}b`;
|
|
||||||
mouseEvent = vboxBuySelected;
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
if (combineLength > i) {
|
|
||||||
text += '■ ';
|
|
||||||
} else {
|
|
||||||
text += '▫ ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<button
|
<button
|
||||||
|
disabled={tutorialDisabled || !refund}
|
||||||
class='vbox-btn'
|
class='vbox-btn'
|
||||||
disabled={!mouseEvent}
|
|
||||||
onClick={e => e.stopPropagation()}
|
onClick={e => e.stopPropagation()}
|
||||||
onMouseDown={() => mouseEvent()}>
|
onMouseDown={e => {
|
||||||
{text}
|
e.stopPropagation();
|
||||||
|
sendVboxRefund(vboxSelected.stashSelect[0]);
|
||||||
|
}}>
|
||||||
|
refund <br />
|
||||||
|
{refund}b
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
function inventoryElement() {
|
|
||||||
function inventoryClick(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (itemUnequip.length) return sendItemUnequip(itemUnequip);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div class='stash-hdr'>
|
||||||
onMouseDown={inventoryClick}
|
<h2 onTouchStart={e => e.target.scrollIntoView(true)}
|
||||||
onClick={e => e.stopPropagation()}
|
onMouseOver={e => vboxHover(e, 'stash')}> STASH
|
||||||
onDragOver={ev => ev.preventDefault()}
|
</h2>
|
||||||
onDrop={inventoryClick}
|
{refundBtn}
|
||||||
>
|
|
||||||
<div class={`vbox-hdr ${reclaiming ? 'reclaiming' : ''}`}>
|
|
||||||
<h3
|
|
||||||
onTouchStart={e => e.target.scrollIntoView(true)}
|
|
||||||
onMouseOver={e => hoverInfo(e, 'inventory')}> INVENTORY
|
|
||||||
</h3>
|
|
||||||
<button
|
|
||||||
disabled={tutorial && tutorial < 8 && instance.time_control === 'Practice' && instance.rounds.length === 1}
|
|
||||||
class='vbox-btn reclaim'
|
|
||||||
onMouseOver={e => hoverInfo(e, 'reclaim')}
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
onMouseDown={reclaimClick}>
|
|
||||||
reclaim
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class={`vbox-items ${reclaiming ? 'reclaiming' : ''}`}>
|
|
||||||
{range(0, 9).map(i => inventoryBtn(vbox.bound[i], i))}
|
|
||||||
</div>
|
|
||||||
{combinerBtn()}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// EVERYTHING
|
// EVERYTHING
|
||||||
//
|
|
||||||
function hoverInfo(e, newInfo) {
|
|
||||||
if (shopSelect.find(c => c[0])) return true;
|
|
||||||
if (stashSelect.length !== 0) {
|
|
||||||
const base = stashSelect.find(c => !['Red', 'Blue', 'Green'].includes(vbox.bound[c]));
|
|
||||||
if (base || base === 0) return true;
|
|
||||||
}
|
|
||||||
return setInfo(newInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
const classes = 'vbox';
|
|
||||||
return (
|
return (
|
||||||
<div class={classes}>
|
<div class='vbox'>
|
||||||
{vboxElement()}
|
{storeHdr()}
|
||||||
<div class="vbox-arrow">⮞</div>
|
{stashHdr()}
|
||||||
{inventoryElement()}
|
<StoreElement
|
||||||
|
clearVboxSelected={clearVboxSelected}
|
||||||
|
setVboxSelected={setVboxSelected}
|
||||||
|
vbox={vbox}
|
||||||
|
vboxSelected={vboxSelected}
|
||||||
|
vboxHover = {vboxHover}
|
||||||
|
/>
|
||||||
|
<StashElement
|
||||||
|
sendItemUnequip={sendItemUnequip}
|
||||||
|
setInfo={setInfo}
|
||||||
|
setVboxSelected={setVboxSelected}
|
||||||
|
vbox={vbox}
|
||||||
|
vboxBuySelected={vboxBuySelected}
|
||||||
|
vboxHover={vboxHover}
|
||||||
|
/>
|
||||||
|
<div class='info-combiner'>
|
||||||
|
<InfoContainer
|
||||||
|
clearTutorial={clearTutorial}
|
||||||
|
/>
|
||||||
|
<Combiner
|
||||||
|
vbox={vbox}
|
||||||
|
vboxSelected={vboxSelected}
|
||||||
|
vboxBuySelected={vboxBuySelected}
|
||||||
|
sendVboxCombine={sendVboxCombine}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Combos />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
78
client/src/components/vbox.info.jsx
Normal file
78
client/src/components/vbox.info.jsx
Normal file
@ -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 (
|
||||||
|
<div class='info info-item'>
|
||||||
|
<h2> {info.replace('constructName ', '')} </h2>
|
||||||
|
<p> This is the name of your construct. <br />
|
||||||
|
Names are randomly generated and are purely cosmetic. <br />
|
||||||
|
You can change change your construct name in the <b>RESHAPE</b> tab outside of games.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.includes('constructAvatar')) {
|
||||||
|
return (
|
||||||
|
<div class='info info-item'>
|
||||||
|
<h2> {info.replace('constructAvatar ', '')} </h2>
|
||||||
|
<p> This is your construct avatar. <br />
|
||||||
|
Avatars are randomly generated and are purely cosmetic. <br />
|
||||||
|
You can change your construct avatar in the <b>RESHAPE</b> tab outside of games.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return genItemInfo(info, itemInfo, player, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addState(Info);
|
||||||
@ -3,6 +3,7 @@ const range = require('lodash/range');
|
|||||||
const shapes = require('./shapes');
|
const shapes = require('./shapes');
|
||||||
|
|
||||||
function specThresholds(player, fullInfo, info) {
|
function specThresholds(player, fullInfo, info) {
|
||||||
|
if (!info) return false;
|
||||||
let red = 0;
|
let red = 0;
|
||||||
let blue = 0;
|
let blue = 0;
|
||||||
let green = 0;
|
let green = 0;
|
||||||
@ -87,9 +88,12 @@ function specThresholds(player, fullInfo, info) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div class="thresholds">
|
<div>
|
||||||
{thresholds}
|
<div class="thresholds">
|
||||||
|
{thresholds}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
132
client/src/components/vbox.stash.jsx
Normal file
132
client/src/components/vbox.stash.jsx
Normal file
@ -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 <button
|
||||||
|
key={i}
|
||||||
|
onClick={emptyInvClick}
|
||||||
|
disabled={!stashHighlight}
|
||||||
|
class={stashHighlight ? 'receiving' : 'empty'} > </button>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<label
|
||||||
|
key={i}
|
||||||
|
draggable='true'
|
||||||
|
onDragStart={ev => {
|
||||||
|
onClick('drag', ev);
|
||||||
|
ev.dataTransfer.setData('text', '');
|
||||||
|
}}>
|
||||||
|
<button
|
||||||
|
class={classes}
|
||||||
|
onMouseOver={e => vboxHover(e, v)}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
onMouseDown={e => onClick('click', e)}>
|
||||||
|
{invObject}
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class='stash'
|
||||||
|
onMouseDown={stashClick}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
onDragOver={ev => ev.preventDefault()}
|
||||||
|
onDrop={stashClick}
|
||||||
|
>
|
||||||
|
{range(0, 6).map(i => stashBtn(vbox.stash[i], i.toString()))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addState(stashElement);
|
||||||
95
client/src/components/vbox.store.jsx
Normal file
95
client/src/components/vbox.store.jsx
Normal file
@ -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 <button disabled class='empty' key={group + index} > </button>;
|
||||||
|
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 (
|
||||||
|
<label
|
||||||
|
key={group + index}
|
||||||
|
onDragEnd={clearVboxSelected}>
|
||||||
|
<button
|
||||||
|
class={classes}
|
||||||
|
disabled={disabled}
|
||||||
|
onMouseOver={e => vboxHover(e, v)}
|
||||||
|
onMouseDown={onClick}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
> {vboxObject}
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class='store'
|
||||||
|
onClick={e => e.stopPropagation()}>
|
||||||
|
<div class="vbox-colours">
|
||||||
|
{range(0, 6).map(i => availableBtn(vbox.store['Colours'][i], 'Colours', i.toString()))}
|
||||||
|
</div>
|
||||||
|
<div class="vbox-items">
|
||||||
|
{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()))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = addState(storeElement);
|
||||||
133
client/src/components/vbox.utils.jsx
Normal file
133
client/src/components/vbox.utils.jsx
Normal file
@ -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 <div>{reactStringReplace(infoDescription, '\n', () => <br />)}</div>;
|
||||||
|
};
|
||||||
|
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 ? <h3> SKILL </h3> : <h3> SPEC </h3>;
|
||||||
|
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 ? <div>{fullInfo.cooldown} Turn delay</div> : null;
|
||||||
|
|
||||||
|
const speed = isSkill
|
||||||
|
? <div> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </div>
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const thresholds = isSpec ? specThresholds(player, fullInfo, item) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="info info-item">
|
||||||
|
<h2>{infoName}</h2>
|
||||||
|
{header}
|
||||||
|
{itemSourceInfo}
|
||||||
|
{cooldown}
|
||||||
|
{itemDescription()}
|
||||||
|
{speed}
|
||||||
|
{thresholds}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div class="info info-item">
|
||||||
|
<h2>{fullInfo.item}</h2>
|
||||||
|
{itemDescription()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cost(group) {
|
||||||
|
if (group === 'Colours') return 1;
|
||||||
|
if (group === 'Skills') return 2;
|
||||||
|
if (group === 'Specs') return 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { setVboxState, genItemInfo, cost };
|
||||||
@ -3,7 +3,7 @@ const preact = require('preact');
|
|||||||
const SOURCE_DURATION_MS = 1000; // Time for SOURCE ONLY
|
const SOURCE_DURATION_MS = 1000; // Time for SOURCE ONLY
|
||||||
const TARGET_DELAY_MS = 500; // Used for Source + Target
|
const TARGET_DELAY_MS = 500; // Used for Source + Target
|
||||||
const TARGET_DURATION_MS = 1500; // Time for TARGET ONLY
|
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
|
const SOURCE_AND_TARGET_TOTAL_DURATION = TARGET_DELAY_MS + TARGET_DURATION_MS; // SOURCE + TARGET time
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -24,22 +24,18 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
INFO: {
|
INFO: {
|
||||||
vbox: {
|
store: {
|
||||||
item: 'VBOX',
|
item: 'STORE',
|
||||||
description: <p><b>ITEMS</b> that are available to buy.<br />
|
description: <p>Contains items 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>,
|
The store is refilled every round.<br />Click <b>REFILL</b> to purchase a refill for 2 bits. </p>,
|
||||||
},
|
},
|
||||||
inventory: {
|
stash: {
|
||||||
item: 'INVENTORY',
|
item: 'STASH',
|
||||||
description: <p>Holds <b>ITEMS</b><br /><b>ITEMS</b> carry over each round.</p>,
|
description: <p>Holds <b>ITEMS</b><br /><b>ITEMS</b> carry over each round.</p>,
|
||||||
},
|
},
|
||||||
bits: {
|
bits: {
|
||||||
item: 'BITS',
|
item: 'BITS',
|
||||||
description: <p>The VBOX currency.<br />
|
description: <p>Currency to buy items.<br />At the beginning of each round you receive 30 bits.</p>,
|
||||||
Colours - 1b<br />
|
|
||||||
Skills - 2b<br />
|
|
||||||
Specs - 3b<br />
|
|
||||||
At the beginning of each round you receive 30 bits.</p>,
|
|
||||||
},
|
},
|
||||||
ready: {
|
ready: {
|
||||||
item: 'READY',
|
item: 'READY',
|
||||||
@ -49,10 +45,9 @@ module.exports = {
|
|||||||
item: 'READY',
|
item: 'READY',
|
||||||
description: 'Ready for the game to begin. When all players are ready the first VBOX PHASE begins.',
|
description: 'Ready for the game to begin. When all players are ready the first VBOX PHASE begins.',
|
||||||
},
|
},
|
||||||
reclaim: {
|
Refund: {
|
||||||
item: 'RECLAIM',
|
item: 'Refund',
|
||||||
description: <p>Reclaim items refunding the listed cost of the item.<br />
|
description: 'Refund the listed cost of a single selected item from the stash.',
|
||||||
Click to enable and then click the item to reclaim.</p>,
|
|
||||||
},
|
},
|
||||||
refill: {
|
refill: {
|
||||||
item: 'REFILL',
|
item: 'REFILL',
|
||||||
@ -60,7 +55,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
constructSkills: {
|
constructSkills: {
|
||||||
item: 'SKILLS',
|
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: {
|
constructSpecs: {
|
||||||
item: 'SPECS',
|
item: 'SPECS',
|
||||||
|
|||||||
@ -26,6 +26,12 @@ function registerEvents(store) {
|
|||||||
return errorToast(msg);
|
return errorToast(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearTutorial() {
|
||||||
|
store.dispatch(actions.setTutorial(null));
|
||||||
|
localStorage.setItem('tutorial-complete', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function setPing(ping) {
|
function setPing(ping) {
|
||||||
store.dispatch(actions.setPing(ping));
|
store.dispatch(actions.setPing(ping));
|
||||||
}
|
}
|
||||||
@ -79,7 +85,7 @@ function registerEvents(store) {
|
|||||||
const newRes = game.resolved.slice(currentGame.resolved.length);
|
const newRes = game.resolved.slice(currentGame.resolved.length);
|
||||||
return eachSeries(newRes, (r, cb) => {
|
return eachSeries(newRes, (r, cb) => {
|
||||||
if (!r.event) return 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 anims = animations.getObjects(r, game, account);
|
||||||
const text = animations.getText(r);
|
const text = animations.getText(r);
|
||||||
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
|
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
|
||||||
@ -87,10 +93,12 @@ function registerEvents(store) {
|
|||||||
|
|
||||||
if (r.stages.includes('START_SKILL') && anims.animSource) {
|
if (r.stages.includes('START_SKILL') && anims.animSource) {
|
||||||
store.dispatch(actions.setAnimSource(anims.animSource));
|
store.dispatch(actions.setAnimSource(anims.animSource));
|
||||||
|
store.dispatch(actions.setAnimText(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r.stages.includes('END_SKILL') && anims.animTarget) {
|
if (r.stages.includes('END_SKILL') && anims.animTarget) {
|
||||||
store.dispatch(actions.setAnimTarget(anims.animTarget));
|
store.dispatch(actions.setAnimTarget(anims.animTarget));
|
||||||
|
store.dispatch(actions.setAnimText(null));
|
||||||
if (animations.isCbAnim(anims.animSkill)) store.dispatch(actions.setAnimCb(cb));
|
if (animations.isCbAnim(anims.animSkill)) store.dispatch(actions.setAnimCb(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,8 +109,9 @@ function registerEvents(store) {
|
|||||||
} else {
|
} else {
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => store.dispatch(actions.setAnimText(text)),
|
() => 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.setAnimSkill(null));
|
||||||
store.dispatch(actions.setAnimSource(null));
|
store.dispatch(actions.setAnimSource(null));
|
||||||
store.dispatch(actions.setAnimTarget(null));
|
store.dispatch(actions.setAnimTarget(null));
|
||||||
store.dispatch(actions.setAnimText(null));
|
// store.dispatch(actions.setAnimText(null));
|
||||||
store.dispatch(actions.setAnimFocus([]));
|
store.dispatch(actions.setAnimFocus([]));
|
||||||
if (r.stages.includes('END_SKILL') && animations.isCbAnim(anims.animSkill)) return true;
|
if (r.stages.includes('END_SKILL') && animations.isCbAnim(anims.animSkill)) return true;
|
||||||
return cb();
|
return cb();
|
||||||
@ -179,12 +188,13 @@ function registerEvents(store) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clearInstance() {
|
function clearInstance() {
|
||||||
store.dispatch(actions.setReclaiming(false));
|
|
||||||
store.dispatch(actions.setActiveSkill(null));
|
store.dispatch(actions.setActiveSkill(null));
|
||||||
store.dispatch(actions.setInfo(null));
|
store.dispatch(actions.setInfo(null));
|
||||||
store.dispatch(actions.setItemUnequip([]));
|
store.dispatch(actions.setItemUnequip([]));
|
||||||
store.dispatch(actions.setVboxHighlight([]));
|
store.dispatch(actions.setVboxCombiner(null));
|
||||||
store.dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
|
store.dispatch(actions.setVboxHighlight(null));
|
||||||
|
store.dispatch(actions.setVboxInfo(null));
|
||||||
|
store.dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAccountInstances(v) {
|
function setAccountInstances(v) {
|
||||||
@ -294,6 +304,9 @@ function registerEvents(store) {
|
|||||||
startDemo();
|
startDemo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store.subscribe(setInfo);
|
||||||
|
// store.on('SET_INFO', setInfo);
|
||||||
|
|
||||||
// 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) {
|
||||||
@ -342,6 +355,7 @@ function registerEvents(store) {
|
|||||||
clearInfo,
|
clearInfo,
|
||||||
clearInstance,
|
clearInstance,
|
||||||
clearMtxActive,
|
clearMtxActive,
|
||||||
|
clearTutorial,
|
||||||
setAccount,
|
setAccount,
|
||||||
setAccountInstances,
|
setAccountInstances,
|
||||||
setActiveItem,
|
setActiveItem,
|
||||||
|
|||||||
@ -6,12 +6,13 @@ function setupKeys(store) {
|
|||||||
key.unbind('esc');
|
key.unbind('esc');
|
||||||
|
|
||||||
key('esc', () => document.activeElement.blur());
|
key('esc', () => document.activeElement.blur());
|
||||||
key('esc', () => store.dispatch(actions.setReclaiming(false)));
|
|
||||||
key('esc', () => store.dispatch(actions.setActiveSkill(null)));
|
key('esc', () => store.dispatch(actions.setActiveSkill(null)));
|
||||||
key('esc', () => store.dispatch(actions.setInfo(null)));
|
key('esc', () => store.dispatch(actions.setInfo(null)));
|
||||||
key('esc', () => store.dispatch(actions.setItemUnequip([])));
|
key('esc', () => store.dispatch(actions.setItemUnequip([])));
|
||||||
key('esc', () => store.dispatch(actions.setVboxHighlight([])));
|
key('esc', () => store.dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] })));
|
||||||
key('esc', () => store.dispatch(actions.setVboxSelected({ shopSelect: [], 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)));
|
key('esc', () => store.dispatch(actions.setMtxActive(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,6 @@ module.exports = {
|
|||||||
gameEffectInfo: createReducer(null, 'SET_GAME_EFFECT_INFO'),
|
gameEffectInfo: createReducer(null, 'SET_GAME_EFFECT_INFO'),
|
||||||
email: createReducer(null, 'SET_EMAIL'),
|
email: createReducer(null, 'SET_EMAIL'),
|
||||||
invite: createReducer(null, 'SET_INVITE'),
|
invite: createReducer(null, 'SET_INVITE'),
|
||||||
info: createReducer(null, 'SET_INFO'),
|
|
||||||
instance: createReducer(null, 'SET_INSTANCE'),
|
instance: createReducer(null, 'SET_INSTANCE'),
|
||||||
instanceChat: createReducer(null, 'SET_INSTANCE_CHAT'),
|
instanceChat: createReducer(null, 'SET_INSTANCE_CHAT'),
|
||||||
instances: createReducer([], 'SET_INSTANCES'),
|
instances: createReducer([], 'SET_INSTANCES'),
|
||||||
@ -44,10 +43,12 @@ module.exports = {
|
|||||||
nav: createReducer(null, 'SET_NAV'),
|
nav: createReducer(null, 'SET_NAV'),
|
||||||
ping: createReducer(null, 'SET_PING'),
|
ping: createReducer(null, 'SET_PING'),
|
||||||
player: createReducer(null, 'SET_PLAYER'),
|
player: createReducer(null, 'SET_PLAYER'),
|
||||||
reclaiming: createReducer(false, 'SET_RECLAIMING'),
|
|
||||||
shop: createReducer(false, 'SET_SHOP'),
|
shop: createReducer(false, 'SET_SHOP'),
|
||||||
pvp: createReducer(null, 'SET_PVP'),
|
pvp: createReducer(null, 'SET_PVP'),
|
||||||
|
|
||||||
|
info: createReducer(null, 'SET_INFO'),
|
||||||
|
comboPreview: createReducer(null, 'SET_COMBO_PREVIEW'),
|
||||||
|
|
||||||
subscription: createReducer(null, 'SET_SUBSCRIPTION'),
|
subscription: createReducer(null, 'SET_SUBSCRIPTION'),
|
||||||
|
|
||||||
team: createReducer([], 'SET_TEAM'),
|
team: createReducer([], 'SET_TEAM'),
|
||||||
@ -57,7 +58,10 @@ module.exports = {
|
|||||||
tutorial: createReducer(1, 'SET_TUTORIAL'),
|
tutorial: createReducer(1, 'SET_TUTORIAL'),
|
||||||
tutorialGame: createReducer(1, 'SET_TUTORIAL_GAME'),
|
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'),
|
ws: createReducer(null, 'SET_WS'),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,7 @@ const toast = require('izitoast');
|
|||||||
const cbor = require('borc');
|
const cbor = require('borc');
|
||||||
|
|
||||||
const throttle = require('lodash/throttle');
|
const throttle = require('lodash/throttle');
|
||||||
|
const groupBy = require('lodash/groupBy');
|
||||||
|
|
||||||
const SOCKET_URL =
|
const SOCKET_URL =
|
||||||
`${window.location.protocol === 'https:' ? 'wss://' : 'ws://'}${window.location.host}/api/ws`;
|
`${window.location.protocol === 'https:' ? 'wss://' : 'ws://'}${window.location.host}/api/ws`;
|
||||||
@ -77,13 +78,13 @@ function createSocket(events) {
|
|||||||
send(['InstanceChat', { instance_id: instanceId, index }]);
|
send(['InstanceChat', { instance_id: instanceId, index }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxAccept(instanceId, group, index) {
|
function sendVboxBuy(instanceId, group, index) {
|
||||||
send(['VboxAccept', { instance_id: instanceId, group, index }]);
|
send(['VboxBuy', { instance_id: instanceId, group, index }]);
|
||||||
events.clearInstance();
|
events.clearInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxAcceptEquip(instanceId, group, index, constructId) {
|
function sendVboxBuyEquip(instanceId, group, index, constructId) {
|
||||||
send(['VboxAcceptEquip', { instance_id: instanceId, group, index, construct_id: constructId }]);
|
send(['VboxBuy', { instance_id: instanceId, group, index, construct_id: constructId }]);
|
||||||
events.clearInstance();
|
events.clearInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,18 +103,20 @@ function createSocket(events) {
|
|||||||
events.clearInstance();
|
events.clearInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxDiscard(instanceId) {
|
function sendVboxRefill(instanceId) {
|
||||||
send(['VboxDiscard', { instance_id: instanceId }]);
|
send(['VboxRefill', { instance_id: instanceId }]);
|
||||||
events.clearInstance();
|
events.clearInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxCombine(instanceId, invIndicies, vboxIndicies) {
|
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();
|
events.clearInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendVboxReclaim(instanceId, index) {
|
function sendVboxRefund(instanceId, index) {
|
||||||
send(['VboxReclaim', { instance_id: instanceId, index }]);
|
send(['VboxRefund', { instance_id: instanceId, index }]);
|
||||||
events.clearInstance();
|
events.clearInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,12 +413,12 @@ function createSocket(events) {
|
|||||||
sendInstanceChat,
|
sendInstanceChat,
|
||||||
sendInstanceLeave,
|
sendInstanceLeave,
|
||||||
|
|
||||||
sendVboxAccept,
|
sendVboxBuy,
|
||||||
sendVboxAcceptEquip,
|
sendVboxBuyEquip,
|
||||||
sendVboxApply,
|
sendVboxApply,
|
||||||
sendVboxReclaim,
|
sendVboxRefund,
|
||||||
sendVboxCombine,
|
sendVboxCombine,
|
||||||
sendVboxDiscard,
|
sendVboxRefill,
|
||||||
sendVboxUnequip,
|
sendVboxUnequip,
|
||||||
sendVboxUnequipApply,
|
sendVboxUnequipApply,
|
||||||
|
|
||||||
|
|||||||
@ -24,21 +24,26 @@ function tutorialVbox(player, store, tutorial) {
|
|||||||
if (vbox.bits < 29) {
|
if (vbox.bits < 29) {
|
||||||
stage += 1;
|
stage += 1;
|
||||||
} else {
|
} else {
|
||||||
vbox.free[0] = vbox.free[0].slice(0, 2);
|
for (let i = 2; i < 6; i += 1) {
|
||||||
vbox.free[1] = [];
|
delete vbox.store.Colours[i];
|
||||||
vbox.free[2] = [];
|
}
|
||||||
vbox.bound.fill(null, 0, 3);
|
vbox.store.Skills = {};
|
||||||
|
vbox.store.Specs = {};
|
||||||
|
delete vbox.stash[0];
|
||||||
|
delete vbox.stash[1];
|
||||||
|
delete vbox.stash[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stage === 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;
|
stage += 1;
|
||||||
} else {
|
} else {
|
||||||
vbox.free[0] = vbox.free[0].slice(0, 2);
|
vbox.store.Colours = {};
|
||||||
vbox.free[1] = [];
|
vbox.store.Skills = {};
|
||||||
vbox.free[2] = [];
|
vbox.store.Specs = {};
|
||||||
vbox.bound.fill(null, 1, 3);
|
delete vbox.stash[0];
|
||||||
|
delete vbox.stash[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,21 +51,24 @@ function tutorialVbox(player, store, tutorial) {
|
|||||||
if (player.constructs[0].skills.length !== 0) {
|
if (player.constructs[0].skills.length !== 0) {
|
||||||
stage += 1;
|
stage += 1;
|
||||||
} else {
|
} else {
|
||||||
vbox.free[0] = vbox.free[0].slice(0, 2);
|
vbox.store.Colours = {};
|
||||||
vbox.free[1] = [];
|
vbox.store.Skills = {};
|
||||||
vbox.free[2] = [];
|
vbox.store.Specs = {};
|
||||||
vbox.bound.fill(null, 0, 2);
|
delete vbox.stash[0];
|
||||||
|
delete vbox.stash[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stage === 4) {
|
if (stage === 4) {
|
||||||
if (!vbox.free[2][0] || vbox.bits < 24) {
|
if (!vbox.store.Specs[0] || vbox.bits < 24) {
|
||||||
stage += 1;
|
stage += 1;
|
||||||
} else {
|
} else {
|
||||||
vbox.free[0] = [];
|
vbox.store.Colours = {};
|
||||||
vbox.free[1] = [];
|
vbox.store.Skills = {};
|
||||||
vbox.free[2] = vbox.free[2].slice(0, 1);
|
delete vbox.store.Specs[1];
|
||||||
vbox.bound.fill(null, 0, 2);
|
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) {
|
if (player.constructs[0].specs.length !== 0) {
|
||||||
stage += 1;
|
stage += 1;
|
||||||
} else {
|
} else {
|
||||||
vbox.free[0] = [];
|
vbox.store.Colours = {};
|
||||||
vbox.free[1] = [];
|
vbox.store.Skills = {};
|
||||||
vbox.free[2] = vbox.free[2].slice(0, 1);
|
vbox.store.Specs = {};
|
||||||
vbox.bound.fill(null, 0, 2);
|
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)) {
|
if (player.constructs.every(c => c.skills.length !== 0)) {
|
||||||
stage += 1;
|
stage += 1;
|
||||||
} else {
|
} else {
|
||||||
vbox.free[0] = [];
|
vbox.store.Colours = {};
|
||||||
vbox.free[1] = [];
|
vbox.store.Skills = {};
|
||||||
vbox.free[2] = [];
|
vbox.store.Specs = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,22 +98,18 @@ function tutorialVbox(player, store, tutorial) {
|
|||||||
if (vbox.bits < 25) {
|
if (vbox.bits < 25) {
|
||||||
stage += 1;
|
stage += 1;
|
||||||
} else {
|
} else {
|
||||||
vbox.free[0] = [];
|
vbox.store.Colours = {};
|
||||||
vbox.free[1] = [];
|
vbox.store.Skills = {};
|
||||||
vbox.free[2] = [];
|
vbox.store.Specs = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
store.dispatch(actions.setTutorial(stage));
|
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;
|
if (!(instance.time_control === 'Practice' && instance.rounds.length === 1)) return false;
|
||||||
|
|
||||||
const exit = () => {
|
const exit = () => clearTutorial();
|
||||||
clearTutorial();
|
|
||||||
localStorage.setItem('tutorial-complete', true);
|
|
||||||
ws.sendInstanceState(instance.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const tutorialText = () => {
|
const tutorialText = () => {
|
||||||
if (tutorial === 1) {
|
if (tutorial === 1) {
|
||||||
@ -113,7 +118,7 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
|||||||
<h2>Tutorial</h2>
|
<h2>Tutorial</h2>
|
||||||
<p> Welcome to the vbox phase tutorial.</p>
|
<p> Welcome to the vbox phase tutorial.</p>
|
||||||
<p> Colours are used to create powerful combinations with base items. </p>
|
<p> Colours are used to create powerful combinations with base items. </p>
|
||||||
<p> Buy the two colours from the vbox to continue. </p>
|
<p> Buy the two colours from the store to continue. </p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -122,11 +127,8 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
|||||||
return (
|
return (
|
||||||
<div class='info-item'>
|
<div class='info-item'>
|
||||||
<h2>Tutorial</h2>
|
<h2>Tutorial</h2>
|
||||||
<p> In a normal game you start with three base <b>Attack</b> skill items. </p>
|
<p> You start the game with the base <b>Attack</b> skill item. </p>
|
||||||
<p> The <b>Attack</b> item can be combined with <b>colours</b> to create a new skill. </p>
|
<p> Highlight all three items then click combine.</p>
|
||||||
<p> Select the Attack item along with two colours. <br />
|
|
||||||
Once selected press <b>COMBINE</b> to create a new combo.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -138,8 +140,8 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
|||||||
<h2>Tutorial</h2>
|
<h2>Tutorial</h2>
|
||||||
<p> The first construct on your team is <b>{constructOne}</b>. </p>
|
<p> The first construct on your team is <b>{constructOne}</b>. </p>
|
||||||
<p> Skill items can be equipped to your constructs to be used in the combat phase. </p>
|
<p> Skill items can be equipped to your constructs to be used in the combat phase. </p>
|
||||||
<p> Click the newly combined skill item in the top right of the inventory. <br />
|
<p> Click your new skill from the stash. <br />
|
||||||
Once selected click the construct <b>SKILL</b> slot to equip the skill. </p>
|
Once selected click the flashing <b>SKILL</b> slot to equip the skill. </p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -150,7 +152,7 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
|||||||
<h2>Tutorial</h2>
|
<h2>Tutorial</h2>
|
||||||
<p> You can also buy specialisation items for your constructs. <br />
|
<p> You can also buy specialisation items for your constructs. <br />
|
||||||
Specialisation items increase stats including power, speed and life. </p>
|
Specialisation items increase stats including power, speed and life. </p>
|
||||||
<p> Buy the specialisation item from the vbox to continue. </p>
|
<p> Buy the specialisation item from the store to continue. </p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -161,8 +163,8 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
|||||||
<h2>Tutorial</h2>
|
<h2>Tutorial</h2>
|
||||||
<p> Equipping specialisation items will increase the stats of your constructs.</p>
|
<p> Equipping specialisation items will increase the stats of your constructs.</p>
|
||||||
<p> These can also be combined with colours for further specialisation. </p>
|
<p> These can also be combined with colours for further specialisation. </p>
|
||||||
<p> Click the specialisation item in the top right of the inventory.<br />
|
<p> Click the specialisation item in the stash.<br />
|
||||||
Once selected click the construct <b>SPEC</b> slot to equip the specialisation. </p>
|
Once selected click the flashing <b>SPEC</b> slot to equip the specialisation. </p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -185,13 +187,11 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
|||||||
return (
|
return (
|
||||||
<div class='info-item'>
|
<div class='info-item'>
|
||||||
<h2>Tutorial</h2>
|
<h2>Tutorial</h2>
|
||||||
<p> Each round you start with a vbox full of different skills, specs and colours. </p>
|
<p> Each round you start with 30 bits and a store full of different skills, specs and colours. </p>
|
||||||
<p> Bits are your currency for buying skills, specs and colours from the vbox. <br />
|
<p> Bits are your currency for buying items. <br />
|
||||||
Colours cost 1b, Skills cost 2b and specs cost 3b. <br />
|
You can refill the store by pressing the refill button for 2b. <br />
|
||||||
You can refill the vbox by pressing the refill button for 2b. <br />
|
|
||||||
After each combat round you get more bits to further upgrade your team.
|
|
||||||
</p>
|
</p>
|
||||||
<p> Press the <b>REFILL</b> button to get a new vbox and continue. </p>
|
<p> Press the <b>REFILL</b> button to buy new items. </p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -206,9 +206,7 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
|||||||
<h2>Tutorial</h2>
|
<h2>Tutorial</h2>
|
||||||
<p>That completes the VBOX Tutorial.</p>
|
<p>That completes the VBOX Tutorial.</p>
|
||||||
<p>Press <b>READY</b> to progress to the <b>GAME PHASE</b> <br />
|
<p>Press <b>READY</b> to progress to the <b>GAME PHASE</b> <br />
|
||||||
or continue creating new items to strengthen your constructs further</p>
|
You can continue creating new items to upgrade your constructs further. </p>
|
||||||
<p>You can unequip skills and specs back into the inventory by double clicking. <br />
|
|
||||||
Reclaim can be used to refund the cost of items in your inventory. </p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -197,7 +197,7 @@ function postData(url = '/', data = {}) {
|
|||||||
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
||||||
credentials: 'include', // include, same-origin, *omit
|
credentials: 'include', // include, same-origin, *omit
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Buy: 'application/json',
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
redirect: 'error', // manual, *follow, error
|
redirect: 'error', // manual, *follow, error
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-ops",
|
"name": "mnml-ops",
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mnml"
|
name = "mnml"
|
||||||
version = "1.9.1"
|
version = "1.10.0"
|
||||||
authors = ["ntr <ntr@smokestack.io>"]
|
authors = ["ntr <ntr@smokestack.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -323,8 +323,6 @@ impl Construct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_modifiers(&mut self, player_colours: &Colours) -> &mut Construct {
|
pub fn apply_modifiers(&mut self, player_colours: &Colours) -> &mut Construct {
|
||||||
self.specs.sort_unstable();
|
|
||||||
|
|
||||||
self.red_power.recalculate(&self.specs, player_colours);
|
self.red_power.recalculate(&self.specs, player_colours);
|
||||||
self.red_life.recalculate(&self.specs, player_colours);
|
self.red_life.recalculate(&self.specs, player_colours);
|
||||||
self.blue_power.recalculate(&self.specs, player_colours);
|
self.blue_power.recalculate(&self.specs, player_colours);
|
||||||
@ -600,11 +598,11 @@ impl Construct {
|
|||||||
self.reduce_green_life(red_remainder);
|
self.reduce_green_life(red_remainder);
|
||||||
let red_damage_amount = red_current_green_life - self.green_life();
|
let red_damage_amount = red_current_green_life - self.green_life();
|
||||||
|
|
||||||
events.push(Event::Damage {
|
events.push(Event::Damage {
|
||||||
skill,
|
skill,
|
||||||
amount: red_damage_amount,
|
amount: red_damage_amount,
|
||||||
mitigation: red_mitigation,
|
mitigation: red_mitigation,
|
||||||
colour: Colour::Red
|
colour: Colour::Red
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,11 +627,11 @@ impl Construct {
|
|||||||
self.reduce_green_life(blue_remainder);
|
self.reduce_green_life(blue_remainder);
|
||||||
let blue_damage_amount = blue_current_green_life - self.green_life();
|
let blue_damage_amount = blue_current_green_life - self.green_life();
|
||||||
|
|
||||||
events.push(Event::Damage {
|
events.push(Event::Damage {
|
||||||
skill,
|
skill,
|
||||||
amount: blue_damage_amount,
|
amount: blue_damage_amount,
|
||||||
mitigation: blue_mitigation,
|
mitigation: blue_mitigation,
|
||||||
colour: Colour::Blue
|
colour: Colour::Blue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use chrono::prelude::*;
|
|||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
|
|
||||||
use account::Account;
|
use account::Account;
|
||||||
use account;
|
use vbox;
|
||||||
|
|
||||||
use player::{Player, Score, player_create};
|
use player::{Player, Score, player_create};
|
||||||
|
|
||||||
@ -466,38 +466,38 @@ impl Instance {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_discard(mut self, account: Uuid) -> Result<Instance, Error> {
|
pub fn vbox_refill(mut self, account: Uuid) -> Result<Instance, Error> {
|
||||||
self.vbox_action_allowed(account)?;
|
self.vbox_action_allowed(account)?;
|
||||||
self.account_player(account)?
|
self.account_player(account)?
|
||||||
.vbox_discard()?;
|
.vbox_refill()?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_accept(mut self, account: Uuid, group: usize, index: usize, construct_id: Option<Uuid>) -> Result<Instance, Error> {
|
pub fn vbox_buy(mut self, account: Uuid, group: vbox::ItemType, index: String, construct_id: Option<Uuid>) -> Result<Instance, Error> {
|
||||||
self.vbox_action_allowed(account)?;
|
self.vbox_action_allowed(account)?;
|
||||||
self.account_player(account)?
|
self.account_player(account)?
|
||||||
.vbox_accept(group, index, construct_id)?;
|
.vbox_buy(group, index, construct_id)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_combine(mut self, account: Uuid, inv_indices: Vec<usize>, vbox_indices: Vec<Vec<usize>>) -> Result<Instance, Error> {
|
pub fn vbox_combine(mut self, account: Uuid, inv_indices: Vec<String>, vbox_indices: vbox::VboxIndices) -> Result<Instance, Error> {
|
||||||
self.vbox_action_allowed(account)?;
|
self.vbox_action_allowed(account)?;
|
||||||
self.account_player(account)?
|
self.account_player(account)?
|
||||||
.vbox_combine(inv_indices, vbox_indices)?;
|
.vbox_combine(inv_indices, vbox_indices)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_reclaim(mut self, account: Uuid, index: usize) -> Result<Instance, Error> {
|
pub fn vbox_refund(mut self, account: Uuid, index: String) -> Result<Instance, Error> {
|
||||||
self.vbox_action_allowed(account)?;
|
self.vbox_action_allowed(account)?;
|
||||||
self.account_player(account)?
|
self.account_player(account)?
|
||||||
.vbox_reclaim(index)?;
|
.vbox_refund(index)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_apply(mut self, account: Uuid, index: usize, construct_id: Uuid) -> Result<Instance, Error> {
|
pub fn vbox_apply(mut self, account: Uuid, index: String, construct_id: Uuid) -> Result<Instance, Error> {
|
||||||
self.vbox_action_allowed(account)?;
|
self.vbox_action_allowed(account)?;
|
||||||
self.account_player(account)?
|
self.account_player(account)?
|
||||||
.vbox_apply(index, construct_id)?;
|
.vbox_equip(index, construct_id)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -834,19 +834,17 @@ mod tests {
|
|||||||
fn instance_pve_test() {
|
fn instance_pve_test() {
|
||||||
let mut instance = Instance::new();
|
let mut instance = Instance::new();
|
||||||
|
|
||||||
let bot_player = bot_player();
|
let bot = bot_player();
|
||||||
let bot = bot_player.id;
|
let bot_one = bot.id;
|
||||||
instance.add_player(bot_player).unwrap();
|
instance.add_player(bot).unwrap();
|
||||||
|
|
||||||
let player_account = Uuid::new_v4();
|
let bot = bot_player();
|
||||||
let constructs = instance_mobs(player_account);
|
let bot_two = bot.id;
|
||||||
let player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true);
|
instance.add_player(bot).unwrap();
|
||||||
|
|
||||||
instance.add_player(player).expect("could not add player");
|
|
||||||
|
|
||||||
assert_eq!(instance.phase, InstancePhase::Lobby);
|
assert_eq!(instance.phase, InstancePhase::Lobby);
|
||||||
instance.player_ready(player_account).unwrap();
|
instance.player_ready(bot_one).unwrap();
|
||||||
instance.player_ready(bot).unwrap();
|
instance.player_ready(bot_two).unwrap();
|
||||||
|
|
||||||
assert_eq!(instance.phase, InstancePhase::Finished);
|
assert_eq!(instance.phase, InstancePhase::Finished);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::{HashMap};
|
||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
|
||||||
@ -9,7 +11,7 @@ use failure::err_msg;
|
|||||||
use account;
|
use account;
|
||||||
use account::Account;
|
use account::Account;
|
||||||
use construct::{Construct, Colours};
|
use construct::{Construct, Colours};
|
||||||
use vbox::{Vbox};
|
use vbox::{Vbox, ItemType, VboxIndices};
|
||||||
use item::{Item, ItemEffect};
|
use item::{Item, ItemEffect};
|
||||||
use effect::{Effect};
|
use effect::{Effect};
|
||||||
|
|
||||||
@ -155,125 +157,162 @@ impl Player {
|
|||||||
pub fn autobuy(&mut self) -> &mut Player {
|
pub fn autobuy(&mut self) -> &mut Player {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
// first check if any constructs have no skills
|
// skill buying phase
|
||||||
// if there is one find an item in vbox that gives a skill
|
while self.constructs.iter().any(|c| c.skills.len() < 3) {
|
||||||
while let Some(c) = self.constructs.iter().position(|c| c.skills.len() == 0) {
|
// find the construct with the smallest number of skills
|
||||||
if let Some(s) = self.vbox.bound.iter().position(|v| v.into_skill().is_some()) {
|
let construct_id = match self.constructs.iter().min_by_key(|c| c.skills.len()) {
|
||||||
let construct_id = self.constructs[c].id;
|
None => panic!("no constructs in autobuy"),
|
||||||
self.vbox_apply(s, construct_id).expect("could not apply");
|
Some(c) => c.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let i = self.vbox.stash.iter()
|
||||||
|
.find(|(_i, v)| v.into_skill().is_some())
|
||||||
|
.map(|(i, _v)| i.clone());
|
||||||
|
|
||||||
|
// got a skill in stash
|
||||||
|
if let Some(i) = i {
|
||||||
|
// AAAAAAAAAAAAAAAAAAAA
|
||||||
|
// there's a bad bug here where if this apply fails
|
||||||
|
// the item in question will be silently dropped
|
||||||
|
let item = self.vbox.stash.remove(&i).unwrap();
|
||||||
|
self.vbox_apply(item, construct_id).ok();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
info!("no skills available...");
|
// need to buy one
|
||||||
}
|
else {
|
||||||
|
|
||||||
// now keep buying and applying items cause whynot
|
// do we have any colours in store?
|
||||||
// inb4 montecarlo gan
|
let colours = self.vbox.store[&ItemType::Colours].keys()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
loop {
|
// how about a base skill?
|
||||||
let (target_construct_i, target_construct_id) = match self.constructs.iter().any(|c| c.skills.len() < 3) {
|
let base = match self.vbox.store[&ItemType::Skills].iter().next() {
|
||||||
true => {
|
Some(b) => Some(b.0.clone()),
|
||||||
let mut target_construct_i = 0;
|
None => None,
|
||||||
for (j, c) in self.constructs.iter().enumerate() {
|
};
|
||||||
if c.skills.len() < self.constructs[target_construct_i].skills.len() {
|
|
||||||
target_construct_i = j;
|
// if no: try to refill and start again
|
||||||
|
match colours.len() < 2 || base.is_none() {
|
||||||
|
true => match self.vbox_refill() {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break, // give up
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
let mut vbox_items = HashMap::new();
|
||||||
|
vbox_items.insert(ItemType::Colours, colours);
|
||||||
|
vbox_items.insert(ItemType::Skills, vec![base.unwrap()]);
|
||||||
|
|
||||||
|
match self.vbox_combine(vec![], Some(vbox_items)) {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break, // give up
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(target_construct_i, self.constructs[target_construct_i].id)
|
|
||||||
},
|
|
||||||
false => {
|
|
||||||
let i = rng.gen_range(0, 3);
|
|
||||||
(i, self.constructs[i].id)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let needs_skills = self.constructs[target_construct_i].skills.len() < 3;
|
|
||||||
let group_i = match needs_skills {
|
|
||||||
true => 1,
|
|
||||||
false => 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let num_colours = self.vbox.bound
|
|
||||||
.iter()
|
|
||||||
.filter(|v| [Item::Red, Item::Green, Item::Blue].contains(v))
|
|
||||||
.count();
|
|
||||||
|
|
||||||
if self.vbox.bound.len() < 3 || num_colours < 2 {
|
|
||||||
if (needs_skills && self.vbox.bits < 4) || self.vbox.bits < 5 {
|
|
||||||
// info!("insufficient balance");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get 2 colours and something else
|
|
||||||
let free_colours = self.vbox.free[0].iter().fold(0, |count, item| {
|
|
||||||
match item.is_some() {
|
|
||||||
true => count + 1,
|
|
||||||
false => count
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if free_colours < 2 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
self.bot_vbox_accept(0).expect("could't accept colour item");
|
|
||||||
self.bot_vbox_accept(0).expect("could't accept colour item");
|
|
||||||
self.bot_vbox_accept(group_i).expect("could't accept group item");
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// info!("{:?}", self.vbox.bound);
|
// spec buying phase
|
||||||
|
while self.constructs.iter().any(|c| c.specs.len() < 3) {
|
||||||
let skills = [Item::Attack, Item::Block, Item::Buff, Item::Debuff, Item::Stun];
|
// find the construct with the smallest number of skills
|
||||||
let combo_i = match group_i {
|
let construct_id = match self.constructs.iter().min_by_key(|c| c.specs.len()) {
|
||||||
1 => self.vbox.bound.iter().position(|v| skills.contains(v)).expect("no skill found"),
|
None => panic!("no constructs in autobuy"),
|
||||||
2 => self.vbox.bound.iter().position(|v| v.into_spec().is_some()).expect("no spec found"),
|
Some(c) => c.id,
|
||||||
_ => panic!("unknown group_i"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// first 2 colours can be whatever
|
let i = self.vbox.stash.iter()
|
||||||
self.vbox_combine(vec![0, 1, combo_i], vec![]).ok();
|
.find(|(_i, v)| v.into_spec().is_some())
|
||||||
let item_i = self.vbox.bound.len() - 1;
|
.map(|(i, _v)| i.clone());
|
||||||
self.vbox_apply(item_i, target_construct_id).ok();
|
|
||||||
|
// got a skill in stash
|
||||||
|
if let Some(i) = i {
|
||||||
|
// AAAAAAAAAAAAAAAAAAAA
|
||||||
|
// there's a bad bug here where if this apply fails
|
||||||
|
// the item in question will be silently dropped
|
||||||
|
let item = self.vbox.stash.remove(&i).unwrap();
|
||||||
|
self.vbox_apply(item, construct_id).ok();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// need to buy one
|
||||||
|
else {
|
||||||
|
// do we have any colours in store?
|
||||||
|
let colours = self.vbox.store[&ItemType::Colours].keys()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
// how about a base spec?
|
||||||
|
let base = match self.vbox.store[&ItemType::Specs].iter().next() {
|
||||||
|
Some(b) => Some(b.0.clone()),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// if no: try to refill and start again
|
||||||
|
match colours.len() < 2 || base.is_none() {
|
||||||
|
true => match self.vbox_refill() {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break, // give up
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
let mut vbox_items = HashMap::new();
|
||||||
|
vbox_items.insert(ItemType::Colours, colours);
|
||||||
|
vbox_items.insert(ItemType::Specs, vec![base.unwrap()]);
|
||||||
|
|
||||||
|
match self.vbox_combine(vec![], Some(vbox_items)) {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break, // give up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// upgrading phase
|
||||||
|
// NYI
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_discard(&mut self) -> Result<&mut Player, Error> {
|
pub fn vbox_refill(&mut self) -> Result<&mut Player, Error> {
|
||||||
self.vbox.balance_sub(DISCARD_COST)?;
|
self.vbox.balance_sub(DISCARD_COST)?;
|
||||||
self.vbox.fill();
|
self.vbox.fill();
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bot_vbox_accept(&mut self, group: usize) -> Result<&mut Player, Error> {
|
pub fn bot_vbox_accept(&mut self, group: ItemType) -> Result<&mut Player, Error> {
|
||||||
self.vbox.bot_accept(group)?;
|
let item = self.vbox.bot_buy(group)?;
|
||||||
|
self.vbox.stash_add(item, None)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_accept(&mut self, group: usize, index: usize, construct_id: Option<Uuid>) -> Result<&mut Player, Error> {
|
pub fn vbox_buy(&mut self, group: ItemType, index: String, construct_id: Option<Uuid>) -> Result<&mut Player, Error> {
|
||||||
self.vbox.accept(group, index, construct_id)?;
|
let item = self.vbox.buy(group, &index)?;
|
||||||
if construct_id.is_some() {
|
|
||||||
let equip_index = self.vbox.bound.len() - 1;
|
match construct_id {
|
||||||
self.vbox_apply(equip_index, construct_id.expect("no construct"))?;
|
Some(id) => { self.vbox_apply(item, id)?; },
|
||||||
}
|
None => { self.vbox.stash_add(item, None)?; },
|
||||||
|
};
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_combine(&mut self, inv_indices: Vec<usize>, vbox_indices: Vec<Vec<usize>>) -> Result<&mut Player, Error> {
|
pub fn vbox_combine(&mut self, inv_indices: Vec<String>, vbox_indices: VboxIndices) -> Result<&mut Player, Error> {
|
||||||
self.vbox.combine(inv_indices, vbox_indices)?;
|
self.vbox.combine(inv_indices, vbox_indices)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_reclaim(&mut self, index: usize) -> Result<&mut Player, Error> {
|
pub fn vbox_refund(&mut self, index: String) -> Result<&mut Player, Error> {
|
||||||
self.vbox.reclaim(index)?;
|
self.vbox.refund(index)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_apply(&mut self, index: usize, construct_id: Uuid) -> Result<&mut Player, Error> {
|
pub fn vbox_equip(&mut self, index: String, construct_id: Uuid) -> Result<&mut Player, Error> {
|
||||||
if self.vbox.bound.get(index).is_none() {
|
let item = self.vbox.stash.remove(&index)
|
||||||
return Err(format_err!("no item at index {:?}", index));
|
.ok_or(format_err!("no item at index {:?} {:}", self, &index))?;
|
||||||
}
|
|
||||||
|
|
||||||
let item = self.vbox.bound.remove(index);
|
self.vbox_apply(item, construct_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_apply(&mut self, item: Item, construct_id: Uuid) -> Result<&mut Player, Error> {
|
||||||
match item.effect() {
|
match item.effect() {
|
||||||
Some(ItemEffect::Skill) => {
|
Some(ItemEffect::Skill) => {
|
||||||
let skill = item.into_skill().ok_or(format_err!("item {:?} has no associated skill", item))?;
|
let skill = item.into_skill().ok_or(format_err!("item {:?} has no associated skill", item))?;
|
||||||
@ -317,8 +356,8 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_unequip(&mut self, target: Item, construct_id: Uuid, target_construct_id: Option<Uuid>) -> Result<&mut Player, Error> {
|
pub fn vbox_unequip(&mut self, target: Item, construct_id: Uuid, target_construct_id: Option<Uuid>) -> Result<&mut Player, Error> {
|
||||||
if self.vbox.bound.len() >= 9 && !target_construct_id.is_some() {
|
if self.vbox.stash.len() >= 9 && !target_construct_id.is_some() {
|
||||||
return Err(err_msg("too many items bound"));
|
return Err(err_msg("too many items stash"));
|
||||||
}
|
}
|
||||||
|
|
||||||
match target.effect() {
|
match target.effect() {
|
||||||
@ -349,13 +388,10 @@ impl Player {
|
|||||||
construct.apply_modifiers(&player_colours);
|
construct.apply_modifiers(&player_colours);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vbox.bound.push(target);
|
match target_construct_id {
|
||||||
|
Some(cid) => { self.vbox_apply(target, cid)?; },
|
||||||
if target_construct_id.is_some() {
|
None => { self.vbox.stash_add(target, None)?; },
|
||||||
let equip_index = self.vbox.bound.len() - 1;
|
};
|
||||||
self.vbox_apply(equip_index, target_construct_id.expect("no construct"))?;
|
|
||||||
}
|
|
||||||
// self.vbox.bound.sort_unstable();
|
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
@ -408,8 +444,8 @@ mod tests {
|
|||||||
let player_account = Uuid::new_v4();
|
let player_account = Uuid::new_v4();
|
||||||
let constructs = instance_mobs(player_account);
|
let constructs = instance_mobs(player_account);
|
||||||
let mut player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true);
|
let mut player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true);
|
||||||
player.vbox.fill();
|
|
||||||
|
|
||||||
|
player.vbox.fill();
|
||||||
player.autobuy();
|
player.autobuy();
|
||||||
|
|
||||||
assert!(player.constructs.iter().all(|c| c.skills.len() >= 1));
|
assert!(player.constructs.iter().all(|c| c.skills.len() >= 1));
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use std::time::{Instant};
|
use std::time::{Instant};
|
||||||
use std::thread::{spawn};
|
use std::thread::{spawn};
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ use mail::Email;
|
|||||||
use pg::{Db};
|
use pg::{Db};
|
||||||
use pg::{PgPool};
|
use pg::{PgPool};
|
||||||
use skill::{Skill, dev_resolve, Resolutions};
|
use skill::{Skill, dev_resolve, Resolutions};
|
||||||
use vbox::{vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip};
|
use vbox::{ItemType, vbox_buy, vbox_apply, vbox_refill, vbox_combine, vbox_refund, vbox_unequip};
|
||||||
use http::{AUTH_CLEAR, TOKEN_HEADER};
|
use http::{AUTH_CLEAR, TOKEN_HEADER};
|
||||||
|
|
||||||
#[derive(Debug,Clone,Serialize)]
|
#[derive(Debug,Clone,Serialize)]
|
||||||
@ -114,14 +115,13 @@ pub enum RpcRequest {
|
|||||||
InstanceState { instance_id: Uuid },
|
InstanceState { instance_id: Uuid },
|
||||||
InstanceChat { instance_id: Uuid, index: usize },
|
InstanceChat { instance_id: Uuid, index: usize },
|
||||||
|
|
||||||
VboxAccept { instance_id: Uuid, group: usize, index: usize },
|
VboxBuy { instance_id: Uuid, group: ItemType, index: String, construct_id: Option<Uuid> },
|
||||||
VboxAcceptEquip { instance_id: Uuid, group: usize, index: usize, construct_id: Uuid },
|
VboxRefill { instance_id: Uuid },
|
||||||
VboxDiscard { instance_id: Uuid },
|
VboxCombine { instance_id: Uuid, inv_indices: Vec<String>, vbox_indices: Option<HashMap<ItemType, Vec<String>>> },
|
||||||
VboxCombine { instance_id: Uuid, inv_indices: Vec<usize>, vbox_indices: Vec<Vec<usize>> },
|
VboxApply { instance_id: Uuid, construct_id: Uuid, index: String },
|
||||||
VboxApply { instance_id: Uuid, construct_id: Uuid, index: usize },
|
|
||||||
VboxUnequip { instance_id: Uuid, construct_id: Uuid, target: Item },
|
VboxUnequip { instance_id: Uuid, construct_id: Uuid, target: Item },
|
||||||
VboxUnequipApply { instance_id: Uuid, construct_id: Uuid, target: Item, target_construct_id: Uuid },
|
VboxUnequipApply { instance_id: Uuid, construct_id: Uuid, target: Item, target_construct_id: Uuid },
|
||||||
VboxReclaim { instance_id: Uuid, index: usize },
|
VboxRefund { instance_id: Uuid, index: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Connection {
|
struct Connection {
|
||||||
@ -245,11 +245,8 @@ impl Connection {
|
|||||||
RpcRequest::InstanceAbandon { instance_id } =>
|
RpcRequest::InstanceAbandon { instance_id } =>
|
||||||
Ok(instance_abandon(&mut tx, account, instance_id)?),
|
Ok(instance_abandon(&mut tx, account, instance_id)?),
|
||||||
|
|
||||||
RpcRequest::VboxAccept { instance_id, group, index } =>
|
RpcRequest::VboxBuy { instance_id, group, index, construct_id } =>
|
||||||
Ok(RpcMessage::InstanceState(vbox_accept(&mut tx, account, instance_id, group, index, None)?)),
|
Ok(RpcMessage::InstanceState(vbox_buy(&mut tx, account, instance_id, group, index, construct_id)?)),
|
||||||
|
|
||||||
RpcRequest::VboxAcceptEquip { instance_id, group, index, construct_id } =>
|
|
||||||
Ok(RpcMessage::InstanceState(vbox_accept(&mut tx, account, instance_id, group, index, Some(construct_id))?)),
|
|
||||||
|
|
||||||
RpcRequest::VboxApply { instance_id, construct_id, index } =>
|
RpcRequest::VboxApply { instance_id, construct_id, index } =>
|
||||||
Ok(RpcMessage::InstanceState(vbox_apply(&mut tx, account, instance_id, construct_id, index)?)),
|
Ok(RpcMessage::InstanceState(vbox_apply(&mut tx, account, instance_id, construct_id, index)?)),
|
||||||
@ -257,11 +254,11 @@ impl Connection {
|
|||||||
RpcRequest::VboxCombine { instance_id, inv_indices, vbox_indices } =>
|
RpcRequest::VboxCombine { instance_id, inv_indices, vbox_indices } =>
|
||||||
Ok(RpcMessage::InstanceState(vbox_combine(&mut tx, account, instance_id, inv_indices, vbox_indices)?)),
|
Ok(RpcMessage::InstanceState(vbox_combine(&mut tx, account, instance_id, inv_indices, vbox_indices)?)),
|
||||||
|
|
||||||
RpcRequest::VboxDiscard { instance_id } =>
|
RpcRequest::VboxRefill { instance_id } =>
|
||||||
Ok(RpcMessage::InstanceState(vbox_discard(&mut tx, account, instance_id)?)),
|
Ok(RpcMessage::InstanceState(vbox_refill(&mut tx, account, instance_id)?)),
|
||||||
|
|
||||||
RpcRequest::VboxReclaim { instance_id, index } =>
|
RpcRequest::VboxRefund { instance_id, index } =>
|
||||||
Ok(RpcMessage::InstanceState(vbox_reclaim(&mut tx, account, instance_id, index)?)),
|
Ok(RpcMessage::InstanceState(vbox_refund(&mut tx, account, instance_id, index)?)),
|
||||||
|
|
||||||
RpcRequest::VboxUnequip { instance_id, construct_id, target } =>
|
RpcRequest::VboxUnequip { instance_id, construct_id, target } =>
|
||||||
Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, account, instance_id, construct_id, target, None)?)),
|
Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, account, instance_id, construct_id, target, None)?)),
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// reclaims
|
// refunds
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rand::{thread_rng};
|
use rand::{thread_rng};
|
||||||
use rand::distributions::{WeightedIndex};
|
use rand::distributions::{WeightedIndex};
|
||||||
@ -19,30 +20,48 @@ use construct::{Colours};
|
|||||||
|
|
||||||
use item::*;
|
use item::*;
|
||||||
|
|
||||||
|
pub type VboxIndices = Option<HashMap<ItemType, Vec<String>>>;
|
||||||
|
|
||||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
pub struct Vbox {
|
pub struct Vbox {
|
||||||
pub bits: usize,
|
pub bits: usize,
|
||||||
pub free: Vec<Vec<Option<Item>>>,
|
pub store: HashMap<ItemType, HashMap<String, Item>>,
|
||||||
pub bound: Vec<Item>,
|
pub stash: HashMap<String, Item>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Copy,Clone,Serialize,Deserialize,Hash,PartialEq,Eq)]
|
||||||
pub enum ItemType {
|
pub enum ItemType {
|
||||||
Colours,
|
Colours,
|
||||||
Skills,
|
Skills,
|
||||||
Specs,
|
Specs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STORE_COLOURS_CAPACITY: usize = 6;
|
||||||
|
const STORE_SKILLS_CAPACITY: usize = 3;
|
||||||
|
const STORE_SPECS_CAPACITY: usize = 3;
|
||||||
|
const STASH_CAPACITY: usize = 6;
|
||||||
|
const STARTING_ATTACK_COUNT: usize = 3;
|
||||||
|
|
||||||
impl Vbox {
|
impl Vbox {
|
||||||
pub fn new() -> Vbox {
|
pub fn new() -> Vbox {
|
||||||
let starting_items = vec![
|
let mut colours: HashMap<String, Item> = HashMap::new();
|
||||||
Item::Attack,
|
let mut skills: HashMap<String, Item> = HashMap::new();
|
||||||
Item::Attack,
|
let mut specs: HashMap<String, Item> = HashMap::new();
|
||||||
Item::Attack,
|
|
||||||
];
|
let store = [
|
||||||
|
(ItemType::Colours, colours),
|
||||||
|
(ItemType::Skills, skills),
|
||||||
|
(ItemType::Colours, specs),
|
||||||
|
].iter().cloned().collect();
|
||||||
|
|
||||||
|
let mut stash = HashMap::new();
|
||||||
|
for i in 0..STARTING_ATTACK_COUNT {
|
||||||
|
stash.insert(i.to_string(), Item::Attack);
|
||||||
|
}
|
||||||
|
|
||||||
Vbox {
|
Vbox {
|
||||||
free: vec![vec![], vec![], vec![]],
|
store,
|
||||||
bound: starting_items,
|
stash,
|
||||||
bits: 30,
|
bits: 30,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,103 +84,130 @@ impl Vbox {
|
|||||||
pub fn fill(&mut self) -> &mut Vbox {
|
pub fn fill(&mut self) -> &mut Vbox {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
self.free = [ItemType::Colours, ItemType::Skills, ItemType::Specs].iter()
|
let colours = vec![
|
||||||
.map(|item_type| {
|
(Item::Red, 1),
|
||||||
let items = match item_type {
|
(Item::Green, 1),
|
||||||
ItemType::Colours => vec![
|
(Item::Blue, 1),
|
||||||
(Some(Item::Red), 1),
|
];
|
||||||
(Some(Item::Green), 1),
|
let colour_dist = WeightedIndex::new(colours.iter().map(|item| item.1)).unwrap();
|
||||||
(Some(Item::Blue), 1),
|
|
||||||
],
|
|
||||||
ItemType::Skills => vec![
|
|
||||||
(Some(Item::Attack), 1),
|
|
||||||
(Some(Item::Block), 1),
|
|
||||||
(Some(Item::Buff), 1),
|
|
||||||
(Some(Item::Debuff), 1),
|
|
||||||
(Some(Item::Stun), 1),
|
|
||||||
],
|
|
||||||
ItemType::Specs => vec![
|
|
||||||
(Some(Item::Power), 1),
|
|
||||||
(Some(Item::Life), 1),
|
|
||||||
(Some(Item::Speed), 1),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let dist = WeightedIndex::new(items.iter().map(|item| item.1)).unwrap();
|
let skills = vec![
|
||||||
iter::repeat_with(|| {
|
(Item::Attack, 1),
|
||||||
items[dist.sample(&mut rng)].0}).take(match item_type {
|
(Item::Block, 1),
|
||||||
ItemType::Colours => 6,
|
(Item::Buff, 1),
|
||||||
_ => 3,
|
(Item::Debuff, 1),
|
||||||
}).collect::<Vec<Option<Item>>>()
|
(Item::Stun, 1),
|
||||||
})
|
];
|
||||||
.collect::<Vec<Vec<Option<Item>>>>();
|
let skill_dist = WeightedIndex::new(skills.iter().map(|item| item.1)).unwrap();
|
||||||
|
|
||||||
|
let specs = vec![
|
||||||
|
(Item::Power, 1),
|
||||||
|
(Item::Life, 1),
|
||||||
|
(Item::Speed, 1),
|
||||||
|
];
|
||||||
|
let spec_dist = WeightedIndex::new(specs.iter().map(|item| item.1)).unwrap();
|
||||||
|
|
||||||
|
for item_type in [ItemType::Colours, ItemType::Skills, ItemType::Specs].iter() {
|
||||||
|
let (items, num, dist) = match item_type {
|
||||||
|
ItemType::Colours => (&colours, STORE_COLOURS_CAPACITY, &colour_dist),
|
||||||
|
ItemType::Skills => (&skills, STORE_SKILLS_CAPACITY, &skill_dist),
|
||||||
|
ItemType::Specs => (&specs, STORE_SPECS_CAPACITY, &spec_dist),
|
||||||
|
};
|
||||||
|
|
||||||
|
let drops = iter::repeat_with(|| items[dist.sample(&mut rng)].0)
|
||||||
|
.take(num)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, item)| (i.to_string(), item))
|
||||||
|
.collect::<HashMap<String, Item>>();
|
||||||
|
|
||||||
|
self.store.insert(*item_type, drops);
|
||||||
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn accept(&mut self, i: usize, j: usize, construct_id: Option<Uuid>) -> Result<&mut Vbox, Error> {
|
pub fn buy(&mut self, item: ItemType, i: &String) -> Result<Item, Error> {
|
||||||
if self.bound.len() >= 9 && !construct_id.is_some() {
|
|
||||||
return Err(err_msg("too many items bound"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check item exists
|
// check item exists
|
||||||
self.free
|
let selection = self.store
|
||||||
.get(i).ok_or(format_err!("no item group at index {:?}", i))?
|
.get_mut(&item).ok_or(format_err!("no item group {:?}", item))?
|
||||||
.get(j).ok_or(format_err!("no item at index {:?}", j))?;
|
.remove(i).ok_or(format_err!("no item at index {:?} {:}", self, i))?;
|
||||||
|
|
||||||
// check can purchase
|
self.balance_sub(selection.cost())?;
|
||||||
let cost = match self.free[i][j] {
|
|
||||||
None => 0,
|
|
||||||
_ => self.free[i][j].unwrap().cost()
|
|
||||||
};
|
|
||||||
self.balance_sub(cost)?;
|
|
||||||
|
|
||||||
// actually move
|
Ok(selection)
|
||||||
match self.free[i][j] {
|
}
|
||||||
None => (),
|
|
||||||
_ => self.bound.push(self.free[i][j].unwrap())
|
pub fn stash_add(&mut self, item: Item, index: Option<&String>) -> Result<String, Error> {
|
||||||
|
if self.stash.len() >= STASH_CAPACITY {
|
||||||
|
return Err(err_msg("stash full"));
|
||||||
}
|
}
|
||||||
// self.bound.push(self.free[i][j].unwrap());
|
|
||||||
self.free[i][j] = None;
|
|
||||||
// self.bound.sort_unstable();
|
|
||||||
|
|
||||||
Ok(self)
|
if let Some(index) = index {
|
||||||
|
if self.stash.contains_key(index) {
|
||||||
|
return Err(format_err!("slot occupied {:?}", index));
|
||||||
|
}
|
||||||
|
self.stash.insert(index.clone(), item);
|
||||||
|
return Ok(index.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in (0..STASH_CAPACITY).map(|i| i.to_string()) {
|
||||||
|
if !self.stash.contains_key(&i) {
|
||||||
|
self.stash.insert(i.clone(), item);
|
||||||
|
return Ok(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(err_msg("stash full"));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bot_accept(&mut self, i: usize) -> Result<&mut Vbox, Error> {
|
pub fn bot_buy(&mut self, item: ItemType) -> Result<Item, Error> {
|
||||||
let buy_index = self.free[i].iter().position(|item| item.is_some());
|
let buy_index = self.store[&item]
|
||||||
self.accept(i, buy_index.expect("no valid buys"), None)
|
.keys()
|
||||||
|
.next()
|
||||||
|
.ok_or(format_err!("no item in group {:?}", item))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
self.buy(item, &buy_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reclaim(&mut self, i: usize) -> Result<&mut Vbox, Error> {
|
pub fn refund(&mut self, i: String) -> Result<&mut Vbox, Error> {
|
||||||
self.bound.get(i).ok_or(format_err!("no item at index {:?}", i))?;
|
let refunded = self.stash.remove(&i)
|
||||||
let reclaimed = self.bound.remove(i);
|
.ok_or(format_err!("no item at index {:?} {:?}", self.stash, i))?;
|
||||||
let refund = reclaimed.cost();
|
|
||||||
// info!("reclaiming {:?} for {:?}", refund, reclaimed);
|
let refund = refunded.cost();
|
||||||
|
// info!("refunding {:?} for {:?}", refund, refunded);
|
||||||
self.balance_add(refund);
|
self.balance_add(refund);
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn combine(&mut self, mut inv_indices: Vec<usize>, vbox_indicies: Vec<Vec<usize>>) -> Result<&mut Vbox, Error> {
|
pub fn combine(&mut self, stash_indices: Vec<String>, store_indices: Option<HashMap<ItemType, Vec<String>>>) -> Result<&mut Vbox, Error> {
|
||||||
if !inv_indices.iter().all(|i| self.bound.get(*i).is_some()) {
|
// find base item for index to insert into
|
||||||
return Err(err_msg("item missing index"));
|
let base_index = stash_indices.iter()
|
||||||
}
|
.find(|i| match self.stash.get(i.clone()) {
|
||||||
// try to buy up the vbox indicies and add them to the inventory indicies for combining
|
Some(item) => item.into_skill().is_some(),
|
||||||
for vi in vbox_indicies.iter() {
|
None => false,
|
||||||
inv_indices.push(self.bound.len());
|
});
|
||||||
self.accept(vi[0], vi[1], Some(Uuid::nil()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// have to sort the indices and keep track of the iteration
|
let mut input = stash_indices
|
||||||
// because when removing the elements the array shifts
|
|
||||||
inv_indices.sort_unstable();
|
|
||||||
let mut input = inv_indices
|
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.map(|i| self.stash.remove(i)
|
||||||
.map(|(i, index)| {
|
.ok_or(format_err!("no item at index {:?} {:?}", self.stash, i)))
|
||||||
self.bound.remove(index.saturating_sub(i))
|
.collect::<Result<Vec<Item>, Error>>()?;
|
||||||
})
|
|
||||||
.collect::<Vec<Item>>();
|
if let Some(store_indices) = store_indices {
|
||||||
|
let mut purchased = store_indices.iter()
|
||||||
|
.map(|(g, list)|
|
||||||
|
list.iter()
|
||||||
|
.map(|i| self.buy(*g, i))
|
||||||
|
.collect::<Result<Vec<Item>, Error>>()
|
||||||
|
)
|
||||||
|
.collect::<Result<Vec<Vec<Item>>, Error>>()?
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
input.append(&mut purchased);
|
||||||
|
}
|
||||||
|
|
||||||
// sort the input to align with the combinations
|
// sort the input to align with the combinations
|
||||||
// combos are sorted when created
|
// combos are sorted when created
|
||||||
@ -169,40 +215,37 @@ impl Vbox {
|
|||||||
let combos = get_combos();
|
let combos = get_combos();
|
||||||
let combo = combos.iter().find(|c| c.components == input).ok_or(err_msg("not a combo"))?;
|
let combo = combos.iter().find(|c| c.components == input).ok_or(err_msg("not a combo"))?;
|
||||||
|
|
||||||
self.bound.push(combo.item);
|
self.stash_add(combo.item, base_index)?;
|
||||||
// self.bound.sort_unstable();
|
|
||||||
if self.bound.len() >= 10 {
|
|
||||||
return Err(err_msg("too many items bound"));
|
|
||||||
}
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_discard(tx: &mut Transaction, account: &Account, instance_id: Uuid) -> Result<Instance, Error> {
|
pub fn vbox_refill(tx: &mut Transaction, account: &Account, instance_id: Uuid) -> Result<Instance, Error> {
|
||||||
let instance = instance_get(tx, instance_id)?
|
let instance = instance_get(tx, instance_id)?
|
||||||
.vbox_discard(account.id)?;
|
.vbox_refill(account.id)?;
|
||||||
return instance_update(tx, instance);
|
return instance_update(tx, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_accept(tx: &mut Transaction, account: &Account, instance_id: Uuid, group: usize, index: usize, construct_id: Option<Uuid>) -> Result<Instance, Error> {
|
pub fn vbox_buy(tx: &mut Transaction, account: &Account, instance_id: Uuid, group: ItemType, index: String, construct_id: Option<Uuid>) -> Result<Instance, Error> {
|
||||||
let instance = instance_get(tx, instance_id)?
|
let instance = instance_get(tx, instance_id)?
|
||||||
.vbox_accept(account.id, group, index, construct_id)?;
|
.vbox_buy(account.id, group, index, construct_id)?;
|
||||||
return instance_update(tx, instance);
|
return instance_update(tx, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_combine(tx: &mut Transaction, account: &Account, instance_id: Uuid, inv_indices: Vec<usize>, vbox_indices: Vec<Vec<usize>>) -> Result<Instance, Error> {
|
pub fn vbox_combine(tx: &mut Transaction, account: &Account, instance_id: Uuid, stash_indices: Vec<String>, vbox_indices: VboxIndices) -> Result<Instance, Error> {
|
||||||
let instance = instance_get(tx, instance_id)?
|
let instance = instance_get(tx, instance_id)?
|
||||||
.vbox_combine(account.id, inv_indices, vbox_indices)?;
|
.vbox_combine(account.id, stash_indices, vbox_indices)?;
|
||||||
return instance_update(tx, instance);
|
return instance_update(tx, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_reclaim(tx: &mut Transaction, account: &Account, instance_id: Uuid, index: usize) -> Result<Instance, Error> {
|
pub fn vbox_refund(tx: &mut Transaction, account: &Account, instance_id: Uuid, index: String) -> Result<Instance, Error> {
|
||||||
let instance = instance_get(tx, instance_id)?
|
let instance = instance_get(tx, instance_id)?
|
||||||
.vbox_reclaim(account.id, index)?;
|
.vbox_refund(account.id, index)?;
|
||||||
return instance_update(tx, instance);
|
return instance_update(tx, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vbox_apply(tx: &mut Transaction, account: &Account, instance_id: Uuid, construct_id: Uuid, index: usize) -> Result<Instance, Error> {
|
pub fn vbox_apply(tx: &mut Transaction, account: &Account, instance_id: Uuid, construct_id: Uuid, index: String) -> Result<Instance, Error> {
|
||||||
let instance = instance_get(tx, instance_id)?
|
let instance = instance_get(tx, instance_id)?
|
||||||
.vbox_apply(account.id, index, construct_id)?;
|
.vbox_apply(account.id, index, construct_id)?;
|
||||||
return instance_update(tx, instance);
|
return instance_update(tx, instance);
|
||||||
@ -221,9 +264,49 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn combine_test() {
|
fn combine_test() {
|
||||||
let mut vbox = Vbox::new();
|
let mut vbox = Vbox::new();
|
||||||
vbox.bound = vec![Item::Attack, Item::Green, Item::Green];
|
vbox.stash.insert(0.to_string(), Item::Attack);
|
||||||
vbox.combine(vec![1,2,0], vec![]).unwrap();
|
vbox.stash.insert(1.to_string(), Item::Green);
|
||||||
assert_eq!(vbox.bound[0], Item::Heal);
|
vbox.stash.insert(2.to_string(), Item::Green);
|
||||||
|
vbox.combine(vec![0.to_string(), 1.to_string(), 2.to_string()], None).unwrap();
|
||||||
|
assert_eq!(vbox.stash["0"], Item::Heal);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn buy_test() {
|
||||||
|
let mut vbox = Vbox::new();
|
||||||
|
vbox.fill();
|
||||||
|
|
||||||
|
// cannot rebuy same
|
||||||
|
vbox.buy(ItemType::Skills, &0.to_string()).unwrap();
|
||||||
|
assert!(vbox.store[&ItemType::Skills].get(&0.to_string()).is_none());
|
||||||
|
assert!(vbox.buy(ItemType::Skills, &0.to_string()).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn capacity_test() {
|
||||||
|
let mut vbox = Vbox::new();
|
||||||
|
vbox.fill();
|
||||||
|
vbox.stash_add(Item::Red, None).unwrap();
|
||||||
|
vbox.stash_add(Item::Red, None).unwrap();
|
||||||
|
vbox.stash_add(Item::Red, None).unwrap();
|
||||||
|
assert!(vbox.stash_add(Item::Red, None).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn store_and_stash_combine_test() {
|
||||||
|
let mut vbox = Vbox::new();
|
||||||
|
vbox.fill();
|
||||||
|
|
||||||
|
let mut skill_combine_args = HashMap::new();
|
||||||
|
skill_combine_args.insert(ItemType::Colours, vec![0.to_string(), 1.to_string()]);
|
||||||
|
skill_combine_args.insert(ItemType::Skills, vec![0.to_string()]);
|
||||||
|
|
||||||
|
let mut spec_combine_args = HashMap::new();
|
||||||
|
spec_combine_args.insert(ItemType::Colours, vec![2.to_string(), 3.to_string()]);
|
||||||
|
spec_combine_args.insert(ItemType::Specs, vec![0.to_string()]);
|
||||||
|
|
||||||
|
vbox.combine(vec![], Some(skill_combine_args)).unwrap();
|
||||||
|
vbox.combine(vec![], Some(spec_combine_args)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -239,10 +322,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reclaim_test() {
|
fn refund_test() {
|
||||||
let mut vbox = Vbox::new();
|
let mut vbox = Vbox::new();
|
||||||
vbox.bound = vec![Item::Strike];
|
vbox.stash.insert(0.to_string(), Item::Strike);
|
||||||
vbox.reclaim(0).unwrap();
|
vbox.refund(0.to_string()).unwrap();
|
||||||
assert_eq!(vbox.bits, 32);
|
assert_eq!(vbox.bits, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user