Merge branch 'construct-box-rework' into develop
This commit is contained in:
commit
e3b27ae6b2
82
WORKLOG.md
82
WORKLOG.md
@ -1,10 +1,43 @@
|
||||
# WORK WORK
|
||||
## NOW
|
||||
|
||||
*PRODUCTION*
|
||||
|
||||
_ntr_
|
||||
* 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_
|
||||
* 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
|
||||
* Img
|
||||
* Skill Icons
|
||||
@ -15,42 +48,17 @@
|
||||
- Speed (e.g. boots)
|
||||
- Life (e.g. heart)
|
||||
|
||||
* reduce inventory size and consolidate vbox and inventory on left side
|
||||
|
||||
* 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?)
|
||||
_tba_
|
||||
* 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
|
||||
- train a few games of with some round losses to get them into the game
|
||||
|
||||
* represent construct colours during game phase (coloured border?)
|
||||
* 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
|
||||
* 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
|
||||
|
||||
## 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
|
||||
- reduce number of items for creating t2/t3 items from 3 -> 2
|
||||
- add lost complexity by adding skill spec items
|
||||
@ -61,18 +69,14 @@
|
||||
- Strike + SpeedRR -> StrikeSpeed (strike has Y% more speed)
|
||||
- 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?
|
||||
* prince of peace
|
||||
* prince of peace
|
||||
* bonus healing / no damage -> Heal power spec?
|
||||
* fuck magic -> Some sort of reflect spec?
|
||||
* empower on ko -> Amplify + Power spec
|
||||
|
||||
* elo + leaderboards
|
||||
* reconnect based on time delta
|
||||
|
||||
* ACP
|
||||
* essential
|
||||
|
||||
## LATER
|
||||
|
||||
@ -95,8 +99,8 @@
|
||||
- Working as cooldowns in reverse by building up to the skill rather than waiting
|
||||
|
||||
* constants
|
||||
* (maybe) return of the combat log (last few events with condensed descriptions)
|
||||
- click in to scroll
|
||||
* return of the combat log (last few events with condensed descriptions)
|
||||
- button to switch context or overlay the combatlog
|
||||
|
||||
* mnml tv
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
border: 1px solid @yellow;
|
||||
border: 0.1em solid @yellow;
|
||||
color: @yellow;
|
||||
background: black;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
@white: #f5f5f5; // whitesmoke
|
||||
@purple: #9355b5; // 6lack - that far cover
|
||||
@yellow: #ffa100;
|
||||
@silver: #c0c0c0;
|
||||
|
||||
@black: black;
|
||||
@gray: #222;
|
||||
|
||||
@ -60,7 +60,7 @@ aside {
|
||||
width: 0.25em;
|
||||
max-width: 0.25em;
|
||||
|
||||
margin: 0 1em 0 0;
|
||||
margin: 0 0.5em 0 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@ -150,13 +150,13 @@ aside {
|
||||
&:hover {
|
||||
color: @red;
|
||||
border-color: @red;
|
||||
border: 2px solid @red;
|
||||
border: 0.1em solid @red;
|
||||
};
|
||||
|
||||
&:active, &.confirming {
|
||||
background: @red;
|
||||
color: black;
|
||||
border: 2px solid @red;
|
||||
border: 0.1em solid @red;
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ aside {
|
||||
&:active, &.confirming {
|
||||
background: @gray-hover;
|
||||
color: black;
|
||||
border: 2px solid @gray-hover;
|
||||
border: 0.1em solid @gray-hover;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
margin-bottom: 0.5em;
|
||||
height: 50%;
|
||||
|
||||
.avatar {
|
||||
@ -148,17 +149,13 @@
|
||||
z-index: 2;
|
||||
button {
|
||||
width: 100%;
|
||||
height: 2em;
|
||||
height: 3em;
|
||||
line-height: 1;
|
||||
margin-right: 1em;
|
||||
span {
|
||||
background-color: black;
|
||||
}
|
||||
background-color: black;
|
||||
}
|
||||
button.active {
|
||||
background: #2c2c2c;
|
||||
span {
|
||||
background-color: #2c2c2c;
|
||||
}
|
||||
background-color: #2c2c2c;
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,8 +233,8 @@
|
||||
|
||||
#targeting, .resolving-skill {
|
||||
position: absolute;
|
||||
top: 35%;
|
||||
height: 15%;
|
||||
top: calc(35% + 0.5em); // calc for 0.5em top gap
|
||||
height: calc(15% - 1em); // calc for 0.5em + 0.5em top / bottom gap
|
||||
width: calc(90% - 1.25em);
|
||||
z-index: 2;
|
||||
span {
|
||||
@ -266,9 +263,7 @@
|
||||
padding-right: 1em;
|
||||
text-align: center;
|
||||
z-index: 2;
|
||||
span {
|
||||
background-color: black;
|
||||
}
|
||||
background-color: black;
|
||||
svg {
|
||||
display: inline;
|
||||
height: 1em;
|
||||
@ -279,7 +274,7 @@
|
||||
/* some stupid bug in chrome makes it fill the entire screen */
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
#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 {
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.scoreboard {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.instance .info {
|
||||
margin: 0 0 0 1em;
|
||||
grid-area: info;
|
||||
|
||||
.instance {
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-rows: 13em min-content;
|
||||
grid-template-rows: min-content 1fr;
|
||||
|
||||
grid-template-areas:
|
||||
"item"
|
||||
"combos";
|
||||
"vbox"
|
||||
"constructs";
|
||||
|
||||
.combos {
|
||||
display: grid;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
.constructs {
|
||||
grid-area: constructs;
|
||||
}
|
||||
}
|
||||
|
||||
.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 {
|
||||
0% {
|
||||
color: palegoldenrod;
|
||||
@ -135,10 +27,6 @@
|
||||
|
||||
/* CONSTRUCT LIST */
|
||||
|
||||
.construct-list {
|
||||
grid-area: constructs;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.instance-construct {
|
||||
flex: 1;
|
||||
@ -152,20 +40,22 @@
|
||||
"stats ";
|
||||
|
||||
/*padding: 0.5em;*/
|
||||
border: 2px solid #222;
|
||||
border: 0.1em solid #222;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.instance-construct:first-child {
|
||||
margin-left: 0;
|
||||
border-left-width: 1px;
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.construct-list {
|
||||
grid-area: constructs;
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
&.highlight {
|
||||
color: black;
|
||||
background: @white;
|
||||
background: @silver;
|
||||
// border: 1px solid @white; (this bangs around the vbox)
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
animation: eq 0.75s cubic-bezier(0, 0, 1, 1) 0s infinite alternate;
|
||||
}
|
||||
@ -484,9 +289,4 @@
|
||||
to {
|
||||
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 {
|
||||
flex: 1;
|
||||
border-top: 0;
|
||||
border: 2px solid #222;
|
||||
border: 0.1em solid #222;
|
||||
&:not(:last-child) {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
|
||||
.msg {
|
||||
grid-area: msg;
|
||||
text-transform: uppercase;
|
||||
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;*/
|
||||
color: #555;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 38px;
|
||||
letter-spacing: .1rem;
|
||||
|
||||
@ -8,8 +8,6 @@
|
||||
@import 'vbox.less';
|
||||
@import 'game.less';
|
||||
@import 'player.less';
|
||||
@import 'styles.mobile.less';
|
||||
@import 'instance.mobile.less';
|
||||
|
||||
html body {
|
||||
margin: 0;
|
||||
@ -23,6 +21,7 @@ html body {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
@ -33,7 +32,6 @@ html body {
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
min-height: 100vh;
|
||||
/*padding: 0 20%;*/
|
||||
|
||||
/* stops inspector going skitz*/
|
||||
overflow-x: hidden;
|
||||
@ -125,14 +123,19 @@ button, input {
|
||||
font-family: 'Jura';
|
||||
color: whitesmoke;
|
||||
height: auto;
|
||||
border-width: 2px;
|
||||
border-width: 0.1em;
|
||||
border-color: @gray-exists;
|
||||
letter-spacing: 0.25em;
|
||||
box-sizing: border-box;
|
||||
font-size: 100%;
|
||||
font-size: 1em;
|
||||
flex: 1;
|
||||
border-radius: 0.5em;
|
||||
line-height: 2em;
|
||||
padding-right: 0.1em;
|
||||
padding-left: 0.1em;
|
||||
padding-bottom: 0.1em;
|
||||
padding-top: 0.1em;
|
||||
|
||||
|
||||
/*the transitions */
|
||||
transition-property: border-color, color, background;
|
||||
@ -250,6 +253,7 @@ figure.gray {
|
||||
|
||||
svg {
|
||||
height: 1em;
|
||||
stroke-width: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,3 +358,5 @@ li {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@import 'styles.mobile.less';
|
||||
|
||||
@ -1,26 +1,43 @@
|
||||
@media (max-width: 1000px) {
|
||||
@media (max-width: 800px) {
|
||||
body {
|
||||
overflow-y: initial;
|
||||
}
|
||||
|
||||
#mnml {
|
||||
font-size: 8pt;
|
||||
padding: 0.25em;
|
||||
padding: 0;
|
||||
|
||||
.instance {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: min-content 1fr;
|
||||
|
||||
grid-template-areas:
|
||||
"vbox"
|
||||
"constructs";
|
||||
"vbox vbox"
|
||||
"constructs constructs";
|
||||
|
||||
font-size: 7.5pt;
|
||||
|
||||
.stats {
|
||||
div:nth-child(4n) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
stroke-width: 1.25em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.game {
|
||||
font-size: 7.5pt;
|
||||
.stats {
|
||||
svg {
|
||||
stroke-width: 1.5em;
|
||||
}
|
||||
|
||||
div:nth-child(4n) {
|
||||
margin: 0 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.team, #targeting, .resolving-skill {
|
||||
width: calc(90% - 3em);
|
||||
}
|
||||
@ -29,7 +46,6 @@
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: min-content 1fr;
|
||||
|
||||
|
||||
.avatar {
|
||||
grid-area: initial;
|
||||
position: absolute;
|
||||
@ -45,17 +61,22 @@
|
||||
}
|
||||
|
||||
.skills {
|
||||
button[disabled] {
|
||||
display: none;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
button {
|
||||
font-size: 1em;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.effects {
|
||||
font-size: 1em;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.skill-description {
|
||||
font-size: 0.8em;
|
||||
svg {
|
||||
height: 1em;
|
||||
}
|
||||
@ -100,21 +121,31 @@
|
||||
.instance-construct {
|
||||
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 {
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
grid-area: initial;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: -1;
|
||||
.stats {
|
||||
svg {
|
||||
height: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
font-size: 75%;
|
||||
button {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
@ -123,8 +154,8 @@
|
||||
}
|
||||
|
||||
|
||||
// portrait menu
|
||||
@media (max-width: 600px) {
|
||||
// portrait menu or small size vertical in landscape
|
||||
@media (max-width: 550px) and (max-height: 800px) {
|
||||
#mnml {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
@ -166,7 +197,7 @@
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
button:not(:last-child) {
|
||||
border: 2px solid #222;
|
||||
border: 0.1em solid #222;
|
||||
}
|
||||
|
||||
button.logo {
|
||||
@ -191,9 +222,103 @@
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
font-size: 6pt;
|
||||
}
|
||||
|
||||
.skill-description {
|
||||
font-size: 6pt;
|
||||
}
|
||||
|
||||
section {
|
||||
.list {
|
||||
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 combiner combiner";
|
||||
|
||||
> div {
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.combos {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.combiner {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.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,79 @@
|
||||
.vbox {
|
||||
margin-bottom: 2em;
|
||||
line-height: 0;
|
||||
align-content: space-between;
|
||||
grid-area: vbox;
|
||||
display: grid;
|
||||
grid-template-rows: 3fr minmax(min-content, 2fr);
|
||||
grid-template-columns: 1fr 4fr 6fr 17em; // =\
|
||||
grid-template-areas:
|
||||
"store-hdr store info combos"
|
||||
"stash-hdr stash combiner combos";
|
||||
margin-bottom: 1em;
|
||||
|
||||
// immediate children
|
||||
> div {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.vbox-hdr {
|
||||
margin-bottom: 1em;
|
||||
height: 2em;
|
||||
@ -19,14 +92,12 @@
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 0.5em 1em;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.vbox-btn {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
background-color: @gray-box;
|
||||
height: 3em;
|
||||
line-height: 1em;
|
||||
border-width: 0;
|
||||
|
||||
@ -34,14 +105,6 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.reclaim {
|
||||
height: auto;
|
||||
|
||||
&:hover {
|
||||
color: @red;
|
||||
};
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background: black;
|
||||
border-width: 1px;
|
||||
@ -53,7 +116,7 @@
|
||||
&, &:hover, &:active {
|
||||
background: @red;
|
||||
color: black;
|
||||
border: 2px solid black;
|
||||
border: 0.1em solid black;
|
||||
}
|
||||
}
|
||||
svg {
|
||||
@ -65,19 +128,22 @@
|
||||
}
|
||||
|
||||
button {
|
||||
height: 4em;
|
||||
height: 3.5em;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
|
||||
// text-transform: none;
|
||||
|
||||
&.empty {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
&.fade {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
color: black;
|
||||
background: @white;
|
||||
background: @silver;
|
||||
// border: 1px solid @white; (this bangs around the vbox)
|
||||
|
||||
// overwrite the classes on white svg elements
|
||||
@ -94,7 +160,7 @@
|
||||
// figures don't scale well
|
||||
figure {
|
||||
svg {
|
||||
height: 2em;
|
||||
height: 1.5em;
|
||||
stroke-width: 0.5em;
|
||||
}
|
||||
|
||||
@ -102,63 +168,143 @@
|
||||
line-height: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
line-height: 1.6;
|
||||
|
||||
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;
|
||||
grid-area: info;
|
||||
}
|
||||
|
||||
.combiner {
|
||||
grid-area: combiner;
|
||||
// align-self: flex-end;
|
||||
height: 100%;
|
||||
margin: 0 0.5em;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.combos {
|
||||
display: grid;
|
||||
grid-area: combos;
|
||||
margin-left: 0.5em;
|
||||
grid-template-rows: min-content min-content;
|
||||
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 */
|
||||
.vbox {
|
||||
align-content: space-between;
|
||||
grid-area: vbox;
|
||||
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;
|
||||
}
|
||||
@media (min-width: 2000px) {
|
||||
.vbox {
|
||||
button {
|
||||
height: 4.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35,7 +35,6 @@ export const setMtxActive = value => ({ type: 'SET_MTX_ACTIVE', value });
|
||||
export const setNav = value => ({ type: 'SET_NAV', value });
|
||||
export const setPing = value => ({ type: 'SET_PING', 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 setShop = value => ({ type: 'SET_SHOP', value });
|
||||
export const setSubscription = value => ({ type: 'SET_SUBSCRIPTION', value });
|
||||
@ -48,7 +47,6 @@ export const setTeamSelect = value => ({ type: 'SET_TEAM_SELECT', value: Array.f
|
||||
export const setTutorial = value => ({ type: 'SET_TUTORIAL', value });
|
||||
export const setTutorialGame = value => ({ type: 'SET_TUTORIAL_GAME', value });
|
||||
|
||||
export const setVboxHighlight = value => ({ type: 'SET_VBOX_HIGHLIGHT', value });
|
||||
export const setVboxSelected = value => ({ type: 'SET_VBOX_SELECTED', value });
|
||||
|
||||
export const setWs = value => ({ type: 'SET_WS', value });
|
||||
|
||||
@ -33,11 +33,9 @@ const addState = connect(
|
||||
function accountPage() {
|
||||
dispatch(actions.setGame(null));
|
||||
dispatch(actions.setInstance(null));
|
||||
dispatch(actions.setReclaiming(false));
|
||||
dispatch(actions.setActiveSkill(null));
|
||||
dispatch(actions.setInfo(null));
|
||||
dispatch(actions.setItemUnequip([]));
|
||||
dispatch(actions.setVboxHighlight([]));
|
||||
return dispatch(actions.setNav('account'));
|
||||
}
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ function Demo(args) {
|
||||
const { combiner, items, equipping, equipped, players, combo } = demo;
|
||||
|
||||
const vboxDemo = () => {
|
||||
function inventoryBtn(i, j) {
|
||||
function stashBtn(i, j) {
|
||||
if (!i) return <button disabled class='empty' > </button>;
|
||||
const highlighted = combiner.indexOf(j) > -1;
|
||||
const classes = `${highlighted ? 'highlight' : ''}`;
|
||||
@ -82,7 +82,7 @@ function Demo(args) {
|
||||
);
|
||||
}
|
||||
|
||||
function inventoryElement() {
|
||||
function stashElement() {
|
||||
return (
|
||||
<div class="vbox">
|
||||
<div class='vbox-section'>
|
||||
@ -96,7 +96,7 @@ function Demo(args) {
|
||||
<div> </div>
|
||||
<div class='vbox-section'>
|
||||
<div class='vbox-items'>
|
||||
{items.map((i, j) => inventoryBtn(i, j))}
|
||||
{items.map((i, j) => stashBtn(i, j))}
|
||||
</div>
|
||||
{combinerBtn()}
|
||||
</div>
|
||||
@ -106,7 +106,7 @@ function Demo(args) {
|
||||
|
||||
return (
|
||||
<div class="news vbox-demo">
|
||||
{inventoryElement()}
|
||||
{stashElement()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -32,11 +32,9 @@ const addState = connect(
|
||||
function setNav(place) {
|
||||
dispatch(actions.setGame(null));
|
||||
dispatch(actions.setInstance(null));
|
||||
dispatch(actions.setReclaiming(false));
|
||||
dispatch(actions.setActiveSkill(null));
|
||||
dispatch(actions.setInfo(null));
|
||||
dispatch(actions.setItemUnequip([]));
|
||||
dispatch(actions.setVboxHighlight([]));
|
||||
dispatch(actions.setMtxActive(null));
|
||||
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 Vbox = require('./vbox.component');
|
||||
const InfoContainer = require('./info.container');
|
||||
const InstanceConstructsContainer = require('./instance.constructs');
|
||||
const Faceoff = require('./faceoff');
|
||||
|
||||
@ -27,10 +26,8 @@ const addState = connect(
|
||||
|
||||
|
||||
function clearItems() {
|
||||
dispatch(actions.setReclaiming(false));
|
||||
dispatch(actions.setItemUnequip([]));
|
||||
dispatch(actions.setVboxHighlight([]));
|
||||
dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
|
||||
dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -58,14 +55,9 @@ function Instance(args) {
|
||||
clearItems();
|
||||
}
|
||||
|
||||
function onTouchMove(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return (
|
||||
<main id="instance" class='instance' onClick={instanceClick}>
|
||||
<Vbox />
|
||||
<InfoContainer />
|
||||
<InstanceConstructsContainer />
|
||||
</main>
|
||||
);
|
||||
|
||||
@ -25,7 +25,7 @@ const addState = connect(
|
||||
} = state;
|
||||
|
||||
function sendVboxAcceptEquip(constructId) {
|
||||
return ws.sendVboxAcceptEquip(instance.id, vboxSelected.shopSelect[0][0], vboxSelected.shopSelect[0][1], constructId);
|
||||
return ws.sendVboxAcceptEquip(instance.id, vboxSelected.storeSelect[0][0], vboxSelected.storeSelect[0][1], constructId);
|
||||
}
|
||||
|
||||
function sendVboxApply(constructId, i) {
|
||||
@ -60,7 +60,7 @@ const addState = connect(
|
||||
}
|
||||
|
||||
function setItemUnequip(v) {
|
||||
dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
|
||||
dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
|
||||
return dispatch(actions.setItemUnequip(v));
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ function Construct(props) {
|
||||
|
||||
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]
|
||||
: -1;
|
||||
|
||||
@ -106,7 +106,7 @@ function Construct(props) {
|
||||
e.preventDefault();
|
||||
if (duplicateSkill || tutorialDisableEquip) return true;
|
||||
if (itemEquip !== -1) return sendVboxApply(construct.id, itemEquip);
|
||||
if (vboxSelected.shopSelect.length === 1) return sendVboxAcceptEquip(construct.id);
|
||||
if (vboxSelected.storeSelect.length === 1) return sendVboxAcceptEquip(construct.id);
|
||||
if (itemUnequip.length && itemUnequip[0] !== construct.id) return sendVboxUnequipApply(construct.id);
|
||||
setItemUnequip([]);
|
||||
return true;
|
||||
@ -114,7 +114,7 @@ function Construct(props) {
|
||||
function hoverInfo(e, info) {
|
||||
e.stopPropagation();
|
||||
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);
|
||||
}
|
||||
|
||||
@ -129,8 +129,10 @@ function Construct(props) {
|
||||
|
||||
function skillClick(e) {
|
||||
if (!skill) return false;
|
||||
setItemUnequip([construct.id, skill.skill, i]);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -177,8 +179,11 @@ function Construct(props) {
|
||||
}
|
||||
|
||||
function specClick(e) {
|
||||
if (!s) return false;
|
||||
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];
|
||||
@ -258,7 +263,6 @@ class InstanceConstructs extends preact.Component {
|
||||
sendVboxApply,
|
||||
sendVboxAcceptEquip,
|
||||
sendVboxUnequipApply,
|
||||
setVboxHighlight,
|
||||
setItemUnequip,
|
||||
} = props;
|
||||
|
||||
@ -281,7 +285,6 @@ class InstanceConstructs extends preact.Component {
|
||||
sendVboxUnequipApply,
|
||||
setInfo,
|
||||
itemInfo,
|
||||
setVboxHighlight,
|
||||
vboxSelected,
|
||||
tutorial,
|
||||
});
|
||||
|
||||
@ -26,6 +26,9 @@ const addState = connect(
|
||||
dispatch(actions.setNav('play'));
|
||||
dispatch(actions.setGame(null));
|
||||
dispatch(actions.setInstance(null));
|
||||
dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
|
||||
dispatch(actions.setInfo(null));
|
||||
dispatch(actions.setItemUnequip([]));
|
||||
if (tutorial) dispatch(actions.setTutorial(1));
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ function Mnml(args) {
|
||||
instance,
|
||||
} = args;
|
||||
|
||||
const rotateClass = (game || instance) && window.innerWidth < window.innerHeight
|
||||
const rotateClass = (game || instance) && window.innerHeight < 900 && window.innerWidth < window.innerHeight
|
||||
? 'show'
|
||||
: '';
|
||||
|
||||
|
||||
@ -33,11 +33,9 @@ const addState = connect(
|
||||
function setNav(place) {
|
||||
dispatch(actions.setGame(null));
|
||||
dispatch(actions.setInstance(null));
|
||||
dispatch(actions.setReclaiming(false));
|
||||
dispatch(actions.setActiveSkill(null));
|
||||
dispatch(actions.setInfo(null));
|
||||
dispatch(actions.setItemUnequip([]));
|
||||
dispatch(actions.setVboxHighlight([]));
|
||||
|
||||
return dispatch(actions.setNav(place));
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ function Skill(props) {
|
||||
// return false;
|
||||
// }
|
||||
const cdText = construct.skills[i].cd > 0
|
||||
? `- ${s.cd}T`
|
||||
? `${s.cd}T`
|
||||
: '';
|
||||
|
||||
const highlight = activeSkill
|
||||
@ -91,7 +91,7 @@ function Skill(props) {
|
||||
onMouseOut={e => hoverInfo(e, null)}
|
||||
type="submit"
|
||||
onClick={onClick}>
|
||||
<span>{s.skill} {cdText}</span>
|
||||
<span>{s.skill} <br /> {cdText}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@ -110,8 +110,11 @@ class TargetSvg extends Component {
|
||||
? playerTeam.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 sourceX = (source * width / 3) + width / 24;
|
||||
const sourceX = (source * width / 3) + width / 18 + skillNumber * (width / 9);
|
||||
const targetX = (target * width / 3) + width / 6
|
||||
+ (defensive ? width / 64 : 0)
|
||||
+ (source * width / 18);
|
||||
|
||||
104
client/src/components/vbox.combiner.jsx
Normal file
104
client/src/components/vbox.combiner.jsx
Normal file
@ -0,0 +1,104 @@
|
||||
const preact = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
const countBy = require('lodash/countBy');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const {
|
||||
ws,
|
||||
itemInfo,
|
||||
instance,
|
||||
player,
|
||||
vboxSelected,
|
||||
} = state;
|
||||
|
||||
function sendVboxAccept(group, index) {
|
||||
document.activeElement.blur();
|
||||
return ws.sendVboxAccept(instance.id, group, index);
|
||||
}
|
||||
|
||||
function sendVboxCombine() {
|
||||
return ws.sendVboxCombine(instance.id, vboxSelected.stashSelect, vboxSelected.storeSelect);
|
||||
}
|
||||
|
||||
return {
|
||||
itemInfo,
|
||||
player,
|
||||
vboxSelected,
|
||||
sendVboxAccept,
|
||||
sendVboxCombine,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
function Combiner(args) {
|
||||
const {
|
||||
// Variables that will change
|
||||
vboxHighlight,
|
||||
vboxSelected,
|
||||
// Static
|
||||
player,
|
||||
itemInfo,
|
||||
// functions
|
||||
sendVboxAccept,
|
||||
sendVboxCombine,
|
||||
} = args;
|
||||
|
||||
const { vbox } = player;
|
||||
const { stashSelect, storeSelect } = vboxSelected;
|
||||
|
||||
const vboxCombo = () => {
|
||||
if (!(vboxHighlight && vboxHighlight.length === 0)) return false;
|
||||
// The selected items can't be combined with additional items therefore valid combo
|
||||
const stashItems = stashSelect.map(j => vbox.bound[j]);
|
||||
const shopItems = storeSelect.map(j => vbox.free[j[0]][j[1]]);
|
||||
const selectedItems = stashItems.concat(shopItems);
|
||||
const combinerCount = countBy(selectedItems, co => co);
|
||||
|
||||
const comboItemObj = itemInfo.combos.find(combo => selectedItems.every(c => {
|
||||
if (!combo.components.includes(c)) return false;
|
||||
const comboCount = countBy(combo.components, co => co);
|
||||
if (combinerCount[c] > comboCount[c]) return false;
|
||||
return true;
|
||||
}));
|
||||
return comboItemObj.item;
|
||||
};
|
||||
|
||||
const combinerCombo = vboxCombo();
|
||||
|
||||
function vboxBuySelected() {
|
||||
if (!(storeSelect.length === 1 && stashSelect.length === 0)) return false;
|
||||
document.activeElement.blur();
|
||||
sendVboxAccept(storeSelect[0][0], storeSelect[0][1]);
|
||||
return true;
|
||||
}
|
||||
let text = '';
|
||||
let mouseEvent = false;
|
||||
if (combinerCombo) {
|
||||
const combinerComboText = combinerCombo.replace('Plus', '+');
|
||||
let bits = 0;
|
||||
storeSelect.forEach(item => bits += item[0] + 1);
|
||||
text = bits
|
||||
? `Buy ${combinerComboText} ${bits}b`
|
||||
: `Combine ${combinerComboText}`;
|
||||
if (vbox.bits >= bits) mouseEvent = sendVboxCombine;
|
||||
} else if (stashSelect.length === 0 && storeSelect.length === 1) {
|
||||
const item = storeSelect[0];
|
||||
text = `Buy ${vbox.free[item[0]][item[1]]} ${item[0] + 1}b`;
|
||||
mouseEvent = vboxBuySelected;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
class='combiner vbox-btn'
|
||||
disabled={!mouseEvent}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onMouseDown={() => mouseEvent()}>
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = addState(Combiner);
|
||||
141
client/src/components/vbox.combos.jsx
Normal file
141
client/src/components/vbox.combos.jsx
Normal file
@ -0,0 +1,141 @@
|
||||
const preact = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
const reactStringReplace = require('react-string-replace');
|
||||
const countBy = require('lodash/countBy');
|
||||
|
||||
const { convertItem, removeTier } = require('../utils');
|
||||
const shapes = require('./shapes');
|
||||
|
||||
const actions = require('../actions');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const {
|
||||
ws,
|
||||
info,
|
||||
itemInfo,
|
||||
itemUnequip,
|
||||
instance,
|
||||
player,
|
||||
account,
|
||||
tutorial,
|
||||
vboxSelected,
|
||||
} = state;
|
||||
|
||||
function sendVboxAccept(group, index) {
|
||||
document.activeElement.blur();
|
||||
return ws.sendVboxAccept(instance.id, group, index);
|
||||
}
|
||||
|
||||
function sendVboxCombine() {
|
||||
return ws.sendVboxCombine(instance.id, vboxSelected.stashSelect, vboxSelected.storeSelect);
|
||||
}
|
||||
|
||||
return {
|
||||
ws,
|
||||
info,
|
||||
itemInfo,
|
||||
itemUnequip,
|
||||
instance,
|
||||
player,
|
||||
account,
|
||||
tutorial,
|
||||
vboxSelected,
|
||||
sendVboxAccept,
|
||||
sendVboxCombine,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
function Combos(args) {
|
||||
const {
|
||||
// Variables that will change
|
||||
info,
|
||||
itemUnequip,
|
||||
tutorial,
|
||||
vboxHighlight,
|
||||
vboxSelected,
|
||||
// Static
|
||||
player, // Only used for colour calcs which will be update if info changes
|
||||
ws,
|
||||
itemInfo,
|
||||
instance, // Only used for instance id
|
||||
// functions
|
||||
sendVboxAccept,
|
||||
sendVboxCombine,
|
||||
setTutorialNull,
|
||||
} = args;
|
||||
const { comboItem } = this.state;
|
||||
|
||||
const { vbox } = player;
|
||||
const { stashSelect, storeSelect } = vboxSelected;
|
||||
|
||||
const vboxCombo = () => {
|
||||
if (!(vboxHighlight && vboxHighlight.length === 0)) return false;
|
||||
// The selected items can't be combined with additional items therefore valid combo
|
||||
const stashItems = stashSelect.map(j => vbox.bound[j]);
|
||||
const shopItems = storeSelect.map(j => vbox.free[j[0]][j[1]]);
|
||||
const selectedItems = stashItems.concat(shopItems);
|
||||
const combinerCount = countBy(selectedItems, co => co);
|
||||
|
||||
const comboItemObj = itemInfo.combos.find(combo => selectedItems.every(c => {
|
||||
if (!combo.components.includes(c)) return false;
|
||||
const comboCount = countBy(combo.components, co => co);
|
||||
if (combinerCount[c] > comboCount[c]) return false;
|
||||
return true;
|
||||
}));
|
||||
return comboItemObj.item;
|
||||
};
|
||||
|
||||
const combinerCombo = vboxCombo();
|
||||
const checkVboxInfo = () => {
|
||||
if (combinerCombo) return combinerCombo;
|
||||
const stashBase = stashSelect.find(i => !(['Red', 'Blue', 'Green'].includes(vbox.bound[i])));
|
||||
if (stashBase > -1) return vbox.bound[stashBase];
|
||||
const storeBase = storeSelect.find(j => !(['Red', 'Blue', 'Green'].includes(vbox.free[j[0]][j[1]])));
|
||||
if (storeBase) return vbox.free[storeBase[0]][storeBase[1]];
|
||||
if (stashSelect.length > 0) return vbox.bound[stashSelect[0]];
|
||||
if (storeSelect.length > 0) return vbox.free[storeSelect[0][0]][storeSelect[0][1]];
|
||||
return false;
|
||||
};
|
||||
let vboxInfo = false;
|
||||
if (itemUnequip.length) [, vboxInfo] = itemUnequip;
|
||||
else if (stashSelect.length > 0 || storeSelect.length > 0) vboxInfo = checkVboxInfo();
|
||||
|
||||
if (tutorial && instance.time_control === 'Practice' && instance.rounds.length === 1) return false;
|
||||
if (!info && !vboxInfo) return false;
|
||||
const comboInfo = vboxInfo || info;
|
||||
const vboxCombos = itemInfo.combos.filter(c => c.components.includes(comboInfo));
|
||||
if (vboxCombos.length > 6 || vboxCombos.length === 0) return false;
|
||||
|
||||
const comboTable = vboxCombos.map((c, i) => {
|
||||
const mouseOver = e => {
|
||||
e.stopPropagation();
|
||||
this.setState({ comboItem: c.item });
|
||||
};
|
||||
const componentTable = (c.components.some(ci => ['Red', 'Blue', 'Green'].includes(ci)))
|
||||
? [<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>
|
||||
);
|
||||
});
|
||||
const comboList = comboTable.length > 0 ? <div class="combo-list">{comboTable}</div> : false;
|
||||
return (
|
||||
<div class="combos">
|
||||
<div class="combo-header">
|
||||
<h2>COMBOS</h2>
|
||||
Combine colours and items.
|
||||
</div>
|
||||
{comboList}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = addState(Combos);
|
||||
@ -1,14 +1,15 @@
|
||||
const preact = require('preact');
|
||||
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 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 addState = connect(
|
||||
function receiveState(state) {
|
||||
@ -16,8 +17,8 @@ const addState = connect(
|
||||
ws,
|
||||
instance,
|
||||
player,
|
||||
reclaiming,
|
||||
vboxSelected,
|
||||
info,
|
||||
itemInfo,
|
||||
itemUnequip,
|
||||
tutorial,
|
||||
@ -32,10 +33,6 @@ const addState = connect(
|
||||
return ws.sendVboxAccept(instance.id, group, index);
|
||||
}
|
||||
|
||||
function sendVboxCombine() {
|
||||
return ws.sendVboxCombine(instance.id, vboxSelected.stashSelect, vboxSelected.shopSelect);
|
||||
}
|
||||
|
||||
function sendVboxReclaim(i) {
|
||||
return ws.sendVboxReclaim(instance.id, i);
|
||||
}
|
||||
@ -46,10 +43,9 @@ const addState = connect(
|
||||
|
||||
return {
|
||||
instance,
|
||||
info,
|
||||
player,
|
||||
reclaiming,
|
||||
sendVboxAccept,
|
||||
sendVboxCombine,
|
||||
sendVboxDiscard,
|
||||
sendVboxReclaim,
|
||||
vboxSelected,
|
||||
@ -61,12 +57,6 @@ const addState = connect(
|
||||
},
|
||||
|
||||
function receiveDispatch(dispatch) {
|
||||
function setReclaiming(v) {
|
||||
dispatch(actions.setItemUnequip([]));
|
||||
dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
|
||||
return dispatch(actions.setReclaiming(v));
|
||||
}
|
||||
|
||||
function setInfo(item) {
|
||||
return dispatch(actions.setInfo(item));
|
||||
}
|
||||
@ -78,20 +68,19 @@ const addState = connect(
|
||||
}
|
||||
|
||||
return {
|
||||
setReclaiming,
|
||||
setInfo,
|
||||
setVboxSelected,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
function validVboxSelect(vbox, itemInfo, shopSelect, stashSelect) {
|
||||
if (shopSelect.length === 0 && stashSelect.length === 0) return false;
|
||||
function validVboxSelect(vbox, itemInfo, storeSelect, stashSelect) {
|
||||
if (storeSelect.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 shopItems = storeSelect.map(j => vbox.free[j[0]][j[1]]);
|
||||
|
||||
const selectedItems = stashItems.concat(shopItems);
|
||||
const itemCount = countBy(selectedItems, co => co);
|
||||
@ -119,7 +108,6 @@ class Vbox extends preact.Component {
|
||||
shouldComponentUpdate(newProps) {
|
||||
// Single variable props
|
||||
if (newProps.itemUnequip !== this.props.itemUnequip) return true;
|
||||
if (newProps.reclaiming !== this.props.reclaiming) return true;
|
||||
if (newProps.tutorial !== this.props.tutorial) return true;
|
||||
if (newProps.vboxSelected !== this.props.vboxSelected) return true;
|
||||
if (newProps.player !== this.props.player) return true;
|
||||
@ -132,7 +120,6 @@ class Vbox extends preact.Component {
|
||||
// Changing state variables
|
||||
itemUnequip,
|
||||
player,
|
||||
reclaiming,
|
||||
tutorial,
|
||||
vboxSelected,
|
||||
instance,
|
||||
@ -142,299 +129,141 @@ class Vbox extends preact.Component {
|
||||
// Function Calls
|
||||
sendItemUnequip,
|
||||
sendVboxAccept,
|
||||
sendVboxCombine,
|
||||
sendVboxDiscard,
|
||||
sendVboxReclaim,
|
||||
setVboxSelected,
|
||||
setInfo,
|
||||
setReclaiming,
|
||||
} = args;
|
||||
|
||||
if (!player) return false;
|
||||
const { vbox } = player;
|
||||
const { shopSelect, stashSelect } = vboxSelected;
|
||||
const vboxSelecting = shopSelect.length === 1 && stashSelect.length === 0;
|
||||
const { storeSelect, stashSelect } = vboxSelected;
|
||||
const vboxSelecting = storeSelect.length === 1 && stashSelect.length === 0;
|
||||
|
||||
function combinerChange(newStashSelect) {
|
||||
return setVboxSelected({ shopSelect, stashSelect: newStashSelect });
|
||||
return setVboxSelected({ storeSelect, stashSelect: newStashSelect });
|
||||
}
|
||||
|
||||
const vboxHighlight = validVboxSelect(vbox, itemInfo, shopSelect, stashSelect);
|
||||
const vboxHighlight = validVboxSelect(vbox, itemInfo, storeSelect, stashSelect);
|
||||
//
|
||||
// VBOX
|
||||
//
|
||||
function vboxHover(e, v) {
|
||||
if (v) {
|
||||
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) {
|
||||
const base = stashSelect.find(c => !['Red', 'Blue', 'Green'].includes(vbox.bound[c]));
|
||||
if (base || base === 0) return true;
|
||||
}
|
||||
if (stashSelect.length !== 0 || storeSelect.length !== 0) return true;
|
||||
setInfo(v);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function clearVboxSelected() {
|
||||
setVboxSelected({ shopSelect: [], stashSelect: [] });
|
||||
setVboxSelected({ storeSelect: [], stashSelect: [] });
|
||||
}
|
||||
|
||||
function vboxBuySelected() {
|
||||
if (!vboxSelecting) return false;
|
||||
document.activeElement.blur();
|
||||
sendVboxAccept(shopSelect[0][0], shopSelect[0][1]);
|
||||
sendVboxAccept(storeSelect[0][0], storeSelect[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;
|
||||
function storeHdr() {
|
||||
return (
|
||||
<label draggable='true'
|
||||
onDragStart={ev => ev.dataTransfer.setData('text', '')}
|
||||
key={group * 10 + index}
|
||||
onDragEnd={clearVboxSelected}>
|
||||
<button
|
||||
class={classes}
|
||||
disabled={disabled}
|
||||
onMouseOver={e => vboxHover(e, v)}
|
||||
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>
|
||||
<div class="store-hdr">
|
||||
<h2
|
||||
onTouchStart={e => e.target.scrollIntoView(true)}
|
||||
onMouseOver={e => vboxHover(e, 'store')}> STORE
|
||||
</h2>
|
||||
<h1 class={`bits ${vbox.bits < 3 ? 'red' : false}`} onMouseOver={e => vboxHover(e, 'bits')}>
|
||||
{vbox.bits}b
|
||||
</h1>
|
||||
<button
|
||||
class='vbox-btn'
|
||||
onMouseOver={e => hoverInfo(e, 'refill')}
|
||||
onMouseOver={e => vboxHover(e, 'refill')}
|
||||
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()}
|
||||
onMouseDown={() => sendVboxDiscard()}>
|
||||
refill - 2b
|
||||
refill <br />
|
||||
2b
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// INVENTORY
|
||||
//
|
||||
function reclaimClick(e) {
|
||||
e.stopPropagation();
|
||||
return setReclaiming(!reclaiming);
|
||||
}
|
||||
|
||||
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 (
|
||||
function stashHdr() {
|
||||
const refund = storeSelect.length === 0 && stashSelect.length === 1
|
||||
? itemInfo.items.find(i => i.item === vbox.bound[stashSelect[0]]).cost
|
||||
: 0;
|
||||
const tutorialDisabled = tutorial && tutorial < 8
|
||||
&& instance.time_control === 'Practice' && instance.rounds.length === 1;
|
||||
const refundBtn = (
|
||||
<button
|
||||
disabled={tutorialDisabled || !refund}
|
||||
class='vbox-btn'
|
||||
disabled={!mouseEvent}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onMouseDown={() => mouseEvent()}>
|
||||
{text}
|
||||
onMouseDown={e => {
|
||||
e.stopPropagation();
|
||||
sendVboxReclaim(vboxSelected.stashSelect[0]);
|
||||
}}>
|
||||
refund <br />
|
||||
{refund}b
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function inventoryElement() {
|
||||
function inventoryClick(e) {
|
||||
e.stopPropagation();
|
||||
if (itemUnequip.length) return sendItemUnequip(itemUnequip);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseDown={inventoryClick}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onDragOver={ev => ev.preventDefault()}
|
||||
onDrop={inventoryClick}
|
||||
>
|
||||
<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 class='stash-hdr'>
|
||||
<h2 onTouchStart={e => e.target.scrollIntoView(true)}
|
||||
onMouseOver={e => vboxHover(e, 'stash')}> STASH
|
||||
</h2>
|
||||
{refundBtn}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// 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 (
|
||||
<div class={classes}>
|
||||
{vboxElement()}
|
||||
<div class="vbox-arrow">⮞</div>
|
||||
{inventoryElement()}
|
||||
<div class='vbox'>
|
||||
{storeHdr()}
|
||||
{stashHdr()}
|
||||
<StoreElement
|
||||
clearVboxSelected = {clearVboxSelected}
|
||||
setInfo = {setInfo}
|
||||
setVboxSelected = {setVboxSelected}
|
||||
storeSelect = {storeSelect}
|
||||
stashSelect = {stashSelect}
|
||||
vbox = {vbox}
|
||||
vboxHighlight = {vboxHighlight}
|
||||
vboxHover = {vboxHover}
|
||||
/>
|
||||
<StashElement
|
||||
combinerChange = {combinerChange}
|
||||
itemUnequip = {itemUnequip}
|
||||
vbox = {vbox}
|
||||
vboxBuySelected = {vboxBuySelected}
|
||||
vboxHighlight = {vboxHighlight}
|
||||
vboxHover = {vboxHover}
|
||||
vboxSelecting = {vboxSelecting}
|
||||
stashSelect = {stashSelect}
|
||||
sendItemUnequip = {sendItemUnequip}
|
||||
setInfo = {setInfo}
|
||||
setVboxSelected = {setVboxSelected}
|
||||
/>
|
||||
<InfoContainer
|
||||
vboxHighlight={vboxHighlight}
|
||||
vboxBuySelected={vboxBuySelected}
|
||||
/>
|
||||
<Combiner
|
||||
vboxHighlight={vboxHighlight}
|
||||
vboxBuySelected={vboxBuySelected}
|
||||
/>
|
||||
<Combos
|
||||
vboxHighlight={vboxHighlight}
|
||||
vboxBuySelected={vboxBuySelected}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
195
client/src/components/vbox.info.jsx
Normal file
195
client/src/components/vbox.info.jsx
Normal file
@ -0,0 +1,195 @@
|
||||
const preact = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
const reactStringReplace = require('react-string-replace');
|
||||
const countBy = require('lodash/countBy');
|
||||
|
||||
const specThresholds = require('./vbox.info.thresholds');
|
||||
const { INFO } = require('./../constants');
|
||||
const { convertItem, removeTier } = require('../utils');
|
||||
const { tutorialStage } = require('../tutorial.utils');
|
||||
const shapes = require('./shapes');
|
||||
|
||||
const actions = require('../actions');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const {
|
||||
ws,
|
||||
info,
|
||||
itemInfo,
|
||||
itemUnequip,
|
||||
instance,
|
||||
player,
|
||||
account,
|
||||
tutorial,
|
||||
vboxSelected,
|
||||
} = state;
|
||||
|
||||
return {
|
||||
ws,
|
||||
info,
|
||||
itemInfo,
|
||||
itemUnequip,
|
||||
instance,
|
||||
player,
|
||||
account,
|
||||
tutorial,
|
||||
vboxSelected,
|
||||
};
|
||||
},
|
||||
|
||||
function receiveDispatch(dispatch) {
|
||||
function setTutorialNull() {
|
||||
dispatch(actions.setTutorial(null));
|
||||
}
|
||||
|
||||
function setInfo(info) {
|
||||
dispatch(actions.setInfo(info));
|
||||
}
|
||||
return { setTutorialNull, setInfo };
|
||||
}
|
||||
);
|
||||
|
||||
function Info(args) {
|
||||
const {
|
||||
// Variables that will change
|
||||
info,
|
||||
itemUnequip,
|
||||
tutorial,
|
||||
vboxHighlight,
|
||||
vboxSelected,
|
||||
// Static
|
||||
player, // Only used for colour calcs which will be update if info changes
|
||||
ws,
|
||||
itemInfo,
|
||||
instance, // Only used for instance id
|
||||
// functions
|
||||
setTutorialNull,
|
||||
} = args;
|
||||
const { comboItem } = this.state;
|
||||
|
||||
const { vbox } = player;
|
||||
const { stashSelect, storeSelect } = vboxSelected;
|
||||
|
||||
const vboxCombo = () => {
|
||||
if (!(vboxHighlight && vboxHighlight.length === 0)) return false;
|
||||
// The selected items can't be combined with additional items therefore valid combo
|
||||
const stashItems = stashSelect.map(j => vbox.bound[j]);
|
||||
const shopItems = storeSelect.map(j => vbox.free[j[0]][j[1]]);
|
||||
const selectedItems = stashItems.concat(shopItems);
|
||||
const combinerCount = countBy(selectedItems, co => co);
|
||||
|
||||
const comboItemObj = itemInfo.combos.find(combo => selectedItems.every(c => {
|
||||
if (!combo.components.includes(c)) return false;
|
||||
const comboCount = countBy(combo.components, co => co);
|
||||
if (combinerCount[c] > comboCount[c]) return false;
|
||||
return true;
|
||||
}));
|
||||
return comboItemObj.item;
|
||||
};
|
||||
|
||||
const combinerCombo = vboxCombo();
|
||||
const checkVboxInfo = () => {
|
||||
if (combinerCombo) return combinerCombo;
|
||||
const stashBase = stashSelect.find(i => !(['Red', 'Blue', 'Green'].includes(vbox.bound[i])));
|
||||
if (stashBase > -1) return vbox.bound[stashBase];
|
||||
const storeBase = storeSelect.find(j => !(['Red', 'Blue', 'Green'].includes(vbox.free[j[0]][j[1]])));
|
||||
if (storeBase) return vbox.free[storeBase[0]][storeBase[1]];
|
||||
if (stashSelect.length > 0) return vbox.bound[stashSelect[0]];
|
||||
if (storeSelect.length > 0) return vbox.free[storeSelect[0][0]][storeSelect[0][1]];
|
||||
return false;
|
||||
};
|
||||
let vboxInfo = false;
|
||||
if (itemUnequip.length) [, vboxInfo] = itemUnequip;
|
||||
else if (stashSelect.length > 0 || storeSelect.length > 0) vboxInfo = checkVboxInfo();
|
||||
|
||||
if (tutorial) {
|
||||
const tutorialStageInfo = tutorialStage(tutorial, ws, setTutorialNull, instance);
|
||||
if (tutorialStageInfo) return tutorialStageInfo;
|
||||
}
|
||||
|
||||
function genItemInfo(item) {
|
||||
const fullInfo = itemInfo.items.find(i => i.item === item) || INFO[item];
|
||||
const isSkill = fullInfo.skill;
|
||||
const isSpec = fullInfo.spec;
|
||||
const itemDescription = () => {
|
||||
const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat|LIFE|SPEED|POWER)/;
|
||||
const infoDescription = reactStringReplace(fullInfo.description, regEx, m => shapes[m]());
|
||||
return <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={`info ${isSkill ? 'info-item' : 'info-item'}`}>
|
||||
<h2>{infoName}</h2>
|
||||
{header}
|
||||
{itemSourceInfo}
|
||||
{cooldown}
|
||||
{itemDescription()}
|
||||
{speed}
|
||||
{thresholds}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div class="info info-item">
|
||||
<h2>{fullInfo.item}</h2>
|
||||
{itemDescription()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const stateFullInfo = comboItem || vboxInfo;
|
||||
if (stateFullInfo) return genItemInfo(stateFullInfo);
|
||||
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);
|
||||
}
|
||||
|
||||
module.exports = addState(Info);
|
||||
@ -3,6 +3,7 @@ const range = require('lodash/range');
|
||||
const shapes = require('./shapes');
|
||||
|
||||
function specThresholds(player, fullInfo, info) {
|
||||
if (!info) return false;
|
||||
let red = 0;
|
||||
let blue = 0;
|
||||
let green = 0;
|
||||
@ -87,9 +88,12 @@ function specThresholds(player, fullInfo, info) {
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div class="thresholds">
|
||||
{thresholds}
|
||||
<div>
|
||||
<div class="thresholds">
|
||||
{thresholds}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
109
client/src/components/vbox.stash.jsx
Normal file
109
client/src/components/vbox.stash.jsx
Normal file
@ -0,0 +1,109 @@
|
||||
const preact = require('preact');
|
||||
const range = require('lodash/range');
|
||||
const without = require('lodash/without');
|
||||
|
||||
const shapes = require('./shapes');
|
||||
const buttons = require('./buttons');
|
||||
const { removeTier } = require('../utils');
|
||||
|
||||
function stashElement(props) {
|
||||
const {
|
||||
combinerChange,
|
||||
itemUnequip,
|
||||
vbox,
|
||||
vboxBuySelected,
|
||||
vboxHighlight,
|
||||
vboxHover,
|
||||
vboxSelecting,
|
||||
stashSelect,
|
||||
|
||||
sendItemUnequip,
|
||||
setInfo,
|
||||
setVboxSelected,
|
||||
|
||||
} = props;
|
||||
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) {
|
||||
const combinerContainsIndex = stashSelect.indexOf(i) > -1;
|
||||
// removing
|
||||
if (combinerContainsIndex) {
|
||||
if (type === 'click') {
|
||||
return combinerChange(without(stashSelect, i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (notValidCombo) {
|
||||
setInfo(vbox.bound[i]);
|
||||
return setVboxSelected({ storeSelect: [], 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} ${notValidCombo ? 'fade' : ''}`;
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class='stash'
|
||||
onMouseDown={stashClick}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onDragOver={ev => ev.preventDefault()}
|
||||
onDrop={stashClick}
|
||||
>
|
||||
{range(0, 6).map(i => stashBtn(vbox.bound[i], i))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = stashElement;
|
||||
78
client/src/components/vbox.store.jsx
Normal file
78
client/src/components/vbox.store.jsx
Normal file
@ -0,0 +1,78 @@
|
||||
const preact = require('preact');
|
||||
const range = require('lodash/range');
|
||||
|
||||
const shapes = require('./shapes');
|
||||
|
||||
|
||||
function storeElement(props) {
|
||||
const {
|
||||
clearVboxSelected,
|
||||
setVboxSelected,
|
||||
storeSelect,
|
||||
stashSelect,
|
||||
vbox,
|
||||
vboxHighlight,
|
||||
vboxHover,
|
||||
} = props;
|
||||
|
||||
function availableBtn(v, group, index) {
|
||||
if (!v) return <button disabled class='empty' key={(group * 10) + 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 * 10 + 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.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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = storeElement;
|
||||
@ -24,22 +24,18 @@ module.exports = {
|
||||
},
|
||||
|
||||
INFO: {
|
||||
vbox: {
|
||||
item: 'VBOX',
|
||||
description: <p><b>ITEMS</b> that are available to buy.<br />
|
||||
The <b>VBOX</b> is refilled every round.<br />Click <b>REFILL</b> at the bottom to purchase a refill. </p>,
|
||||
store: {
|
||||
item: 'STORE',
|
||||
description: <p>Contains items that are available to buy.<br />
|
||||
The store is refilled every round.<br />Click <b>REFILL</b> to purchase a refill for 2 bits. </p>,
|
||||
},
|
||||
inventory: {
|
||||
item: 'INVENTORY',
|
||||
stash: {
|
||||
item: 'STASH',
|
||||
description: <p>Holds <b>ITEMS</b><br /><b>ITEMS</b> carry over each round.</p>,
|
||||
},
|
||||
bits: {
|
||||
item: 'BITS',
|
||||
description: <p>The VBOX currency.<br />
|
||||
Colours - 1b<br />
|
||||
Skills - 2b<br />
|
||||
Specs - 3b<br />
|
||||
At the beginning of each round you receive 30 bits.</p>,
|
||||
description: <p>Currency to buy items.<br />At the beginning of each round you receive 30 bits.</p>,
|
||||
},
|
||||
ready: {
|
||||
item: 'READY',
|
||||
@ -60,7 +56,7 @@ module.exports = {
|
||||
},
|
||||
constructSkills: {
|
||||
item: 'SKILLS',
|
||||
description: 'Skills are used by constructs in the game phase.\nBase skills can be bought from the VBOX.\nEquip skills from the inventory. Double-click to unequip.',
|
||||
description: 'Skills are used by constructs in the game phase.\nBase skills can be bought from the VBOX.\nEquip skills from the stash. Double-click to unequip.',
|
||||
},
|
||||
constructSpecs: {
|
||||
item: 'SPECS',
|
||||
|
||||
@ -179,12 +179,10 @@ function registerEvents(store) {
|
||||
}
|
||||
|
||||
function clearInstance() {
|
||||
store.dispatch(actions.setReclaiming(false));
|
||||
store.dispatch(actions.setActiveSkill(null));
|
||||
store.dispatch(actions.setInfo(null));
|
||||
store.dispatch(actions.setItemUnequip([]));
|
||||
store.dispatch(actions.setVboxHighlight([]));
|
||||
store.dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] }));
|
||||
store.dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] }));
|
||||
}
|
||||
|
||||
function setAccountInstances(v) {
|
||||
|
||||
@ -6,12 +6,10 @@ function setupKeys(store) {
|
||||
key.unbind('esc');
|
||||
|
||||
key('esc', () => document.activeElement.blur());
|
||||
key('esc', () => store.dispatch(actions.setReclaiming(false)));
|
||||
key('esc', () => store.dispatch(actions.setActiveSkill(null)));
|
||||
key('esc', () => store.dispatch(actions.setInfo(null)));
|
||||
key('esc', () => store.dispatch(actions.setItemUnequip([])));
|
||||
key('esc', () => store.dispatch(actions.setVboxHighlight([])));
|
||||
key('esc', () => store.dispatch(actions.setVboxSelected({ shopSelect: [], stashSelect: [] })));
|
||||
key('esc', () => store.dispatch(actions.setVboxSelected({ storeSelect: [], stashSelect: [] })));
|
||||
key('esc', () => store.dispatch(actions.setMtxActive(null)));
|
||||
}
|
||||
|
||||
|
||||
@ -44,7 +44,6 @@ module.exports = {
|
||||
nav: createReducer(null, 'SET_NAV'),
|
||||
ping: createReducer(null, 'SET_PING'),
|
||||
player: createReducer(null, 'SET_PLAYER'),
|
||||
reclaiming: createReducer(false, 'SET_RECLAIMING'),
|
||||
shop: createReducer(false, 'SET_SHOP'),
|
||||
pvp: createReducer(null, 'SET_PVP'),
|
||||
|
||||
@ -57,7 +56,7 @@ module.exports = {
|
||||
tutorial: createReducer(1, 'SET_TUTORIAL'),
|
||||
tutorialGame: createReducer(1, 'SET_TUTORIAL_GAME'),
|
||||
|
||||
vboxSelected: createReducer({ shopSelect: [], stashSelect: [] }, 'SET_VBOX_SELECTED'),
|
||||
vboxSelected: createReducer({ storeSelect: [], stashSelect: [] }, 'SET_VBOX_SELECTED'),
|
||||
|
||||
ws: createReducer(null, 'SET_WS'),
|
||||
};
|
||||
|
||||
@ -138,7 +138,7 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
||||
<h2>Tutorial</h2>
|
||||
<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> Click the newly combined skill item in the top right of the inventory. <br />
|
||||
<p> Click the newly combined skill from the stash. <br />
|
||||
Once selected click the construct <b>SKILL</b> slot to equip the skill. </p>
|
||||
</div>
|
||||
);
|
||||
@ -161,7 +161,7 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
|
||||
<h2>Tutorial</h2>
|
||||
<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> Click the specialisation item in the top right of the inventory.<br />
|
||||
<p> Click the specialisation item from the stash.<br />
|
||||
Once selected click the construct <b>SPEC</b> slot to equip the specialisation. </p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -323,8 +323,6 @@ impl 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_life.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);
|
||||
let red_damage_amount = red_current_green_life - self.green_life();
|
||||
|
||||
events.push(Event::Damage {
|
||||
events.push(Event::Damage {
|
||||
skill,
|
||||
amount: red_damage_amount,
|
||||
mitigation: red_mitigation,
|
||||
colour: Colour::Red
|
||||
colour: Colour::Red
|
||||
});
|
||||
}
|
||||
|
||||
@ -629,11 +627,11 @@ impl Construct {
|
||||
self.reduce_green_life(blue_remainder);
|
||||
let blue_damage_amount = blue_current_green_life - self.green_life();
|
||||
|
||||
events.push(Event::Damage {
|
||||
events.push(Event::Damage {
|
||||
skill,
|
||||
amount: blue_damage_amount,
|
||||
mitigation: blue_mitigation,
|
||||
colour: Colour::Blue
|
||||
colour: Colour::Blue
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ impl Vbox {
|
||||
}
|
||||
|
||||
pub fn accept(&mut self, i: usize, j: usize, construct_id: Option<Uuid>) -> Result<&mut Vbox, Error> {
|
||||
if self.bound.len() >= 9 && !construct_id.is_some() {
|
||||
if self.bound.len() >= 6 && !construct_id.is_some() {
|
||||
return Err(err_msg("too many items bound"));
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ impl Vbox {
|
||||
for vi in vbox_indicies.iter() {
|
||||
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
|
||||
// because when removing the elements the array shifts
|
||||
@ -171,7 +171,7 @@ impl Vbox {
|
||||
|
||||
self.bound.push(combo.item);
|
||||
// self.bound.sort_unstable();
|
||||
if self.bound.len() >= 10 {
|
||||
if self.bound.len() > 6 {
|
||||
return Err(err_msg("too many items bound"));
|
||||
}
|
||||
Ok(self)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user