Merge branch 'release/1.8.0'

This commit is contained in:
ntr 2019-11-07 16:12:15 +11:00
commit 2ea86fd6b8
58 changed files with 3502 additions and 1019 deletions

View File

@ -2,6 +2,53 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [1.8.0] - 2019-10-31
# Added
- Drag and drop for vbox interactions can be used instead of single click / double click
- Base white items and be directly equipped from vbox rather than going through the inventory
- Useful if you want to equip an item without further combining with colours
- You can swap skills and specs between constructs without using the inventory
# Changed
- Construct life changed
- You now start with 800 green life (down from 950)
- You now start with 125 red life and blue life (up from 0)
- Game phase layout
- Enemy team effects appear under your construct instead of next to it
- Game phase constructs line up symmetrically now
- Mobile
- Tutorial is disabled for mobile view
- Landscape is now default view
- Vbox phase everything is now in one view
- Game constructs and animations are much larger in mobile view
- Amplify
Now increases green power
- Absorb
- Reduced duration and cooldown from 2T -> 1T (Absorption duration unchanged)
- Absorption damage is now based on all damage taken (previously only green damage taken)
- Now recharges blue life based on 95 / 120 / 155 blue power
- Banish
Reduced cooldown to 1T
- Decay
Removed cooldown
- Haste / Hybrid
Fixed issue when hybridblast and hastestrike wouldn't trigger from upgraded + skills
- Intercept
- Reduced duration to 1T down from 2T
- Reduced cooldown to 1T down from 2T
## [1.7.0] - 2019-10-31 ## [1.7.0] - 2019-10-31
# Added # Added
- Step by step tutorial - Step by step tutorial

View File

@ -1 +1 @@
1.7.1 1.8.0

View File

@ -3,22 +3,33 @@
*PRODUCTION* *PRODUCTION*
* vbox phase skill list navigator (overlay maybe?)
* can't reset password without knowing password =\ * can't reset password without knowing password =\
* ws gzip encoding
* mobile styles
* mobile info page * mobile info page
* fix info page for tablet layout
* Invert recharge * Invert recharge
## SOON ## SOON
* equip from shop (buy and equip without putting in your inventory) for bases * change cooldowns to delay & recharge
* move item from one construct to another - 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
- Created by combining a skill with corresponding spec
e.g.
- Strike + PowerRR -> StrikePower (Will be the power symbol with strike text under)
- Construct does Y% more damage with Strike
- Strike + SpeedRR -> StrikeSpeed (strike has Y% more speed)
- Strike + LifeRR -> StrikeLife (Strike recharges X% of damage as red life)
* ACP * ACP
* essential * essential
* audio * audio
* treats * treats
* susbcriber gold name in instance * susbcriber gold name in instance
@ -66,7 +77,6 @@ $$$
* Highlight (dota) colour * Highlight (dota) colour
* fx colours + styles * fx colours + styles
* ??? (PROBS NOT) drag and drop buy / equip / unequip items ???
* modules * modules
* troll life -> dmg * troll life -> dmg
* prince of peace * prince of peace

View File

@ -1,6 +1,6 @@
{ {
"name": "mnml-client", "name": "mnml-client",
"version": "1.7.1", "version": "1.8.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

2593
client/assets/rotate.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -4,7 +4,7 @@
grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr;
div { div {
padding-right: 2em; padding-right: 1em;
} }
button { button {

View File

@ -169,3 +169,15 @@ button {
color: @green; color: @green;
} }
} }
@keyframes rb-text {
0% {
color: @red;
}
50% {
color: @white;
}
100% {
color: @blue;
}
}

View File

@ -96,6 +96,20 @@ aside {
border-color: forestgreen; border-color: forestgreen;
} }
} }
// all green
// &:enabled {
// background: forestgreen;
// color: black;
// border-color: forestgreen;
// &:hover {
// color: forestgreen;
// border-color: forestgreen;
// background: 0;
// }
// }
} }
.team-page-ctrl { .team-page-ctrl {

View File

@ -10,15 +10,22 @@
// "opponent" // "opponent"
// "target " // "target "
// "player "; // "player ";
.skill-description {
font-size: 75%;
}
} }
.game .team { .game .team, .faceoff .team {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
/* stops overflow */ /* stops overflow */
min-height: 0; min-height: 0;
min-width: 0; min-width: 0;
// timer is 0.25 w/ 1em margin
width: calc(90% - 1.25em);
} }
.player { .player {
@ -28,7 +35,6 @@
position: absolute; position: absolute;
bottom: 0; bottom: 0;
height: 50%; height: 50%;
width: 90%;
} }
.opponent { .opponent {
@ -37,13 +43,15 @@
position: absolute; position: absolute;
top: 0; top: 0;
height: 35%; height: 35%;
width: 90%;
margin-top: 0.5em; margin-top: 0.5em;
.game-construct { .game-construct {
align-items: flex-start; align-items: flex-start;
grid-template-columns: 1fr 2fr; grid-template-rows: 2fr min-content;
grid-template-rows: 1fr; grid-template-rows: 1fr;
grid-template-areas:
"right"
"left";
.right { .right {
height: 100%; height: 100%;
@ -78,12 +86,16 @@
justify-items: center; justify-items: center;
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: min-content 1fr; grid-template-rows: 1fr 2fr;
grid-template-areas:
"left"
"right";
.left { .left {
width: 100%; width: 100%;
display: grid; display: grid;
grid-area: left;
grid-template-columns: 1fr 2fr; grid-template-columns: 1fr 2fr;
grid-template-areas: grid-template-areas:
"skills effects"; "skills effects";
@ -91,6 +103,7 @@
.right { .right {
display: grid; display: grid;
grid-area: right;
grid-template-rows: minmax(min-content, 1fr) min-content min-content; grid-template-rows: minmax(min-content, 1fr) min-content min-content;
grid-template-areas: grid-template-areas:
"avatar" "avatar"
@ -120,6 +133,7 @@
} }
.skills { .skills {
z-index: 2;
button { button {
width: 100%; width: 100%;
height: 2em; height: 2em;
@ -131,6 +145,7 @@
} }
.effects { .effects {
z-index: 2;
grid-area: effects; grid-area: effects;
white-space: nowrap; white-space: nowrap;
width: 100%; width: 100%;
@ -198,7 +213,7 @@
position: absolute; position: absolute;
top: 35%; top: 35%;
height: 15%; height: 15%;
width: 90%; width: calc(90% - 1.25em);
} }
.resolving-skill { .resolving-skill {
@ -238,6 +253,9 @@
overflow: hidden; overflow: hidden;
max-height: 100%; max-height: 100%;
max-width: 100%; max-width: 100%;
display: flex;
flex-flow: column;
} }
.combat-anim svg { .combat-anim svg {
@ -257,14 +275,6 @@
color: #a52a2a; color: #a52a2a;
} }
.red-damage text {
fill: #a52a2a;
}
.red-damage .stats {
/*border-top: 1px solid #a52a2a;*/
}
.game-construct.blue-damage { .game-construct.blue-damage {
color: #3050f8; color: #3050f8;
opacity: 1; opacity: 1;
@ -274,13 +284,6 @@
color: #3050f8; color: #3050f8;
} }
.blue-damage text {
fill: #3050f8;
}
.blue-damage .stats {
}
.game-construct.green-damage { .game-construct.green-damage {
color: #1FF01F; color: #1FF01F;
opacity: 1; opacity: 1;
@ -290,31 +293,8 @@
color: #1FF01F; color: #1FF01F;
} }
.green-damage text { .game-construct.rb-damage {
fill: #1FF01F; animation: rb-text 1s cubic-bezier(0.5, 0, 0.5, 1) 0s infinite;
}
.green-damage .stats {
/*border-top: 1px solid #1FF01F;*/
}
.game-construct.purple-damage {
/* filter: drop-shadow(0 0 0.2em purple);
*/ color: purple;
border-color: purple;
}
.purple-damage button {
border: 1px solid purple;
color: purple;
}
.purple-damage text {
fill: purple;
}
.purple-damage .stats {
border-top: 1px solid purple;
} }
.game .img, .faceoff .img { .game .img, .faceoff .img {
@ -345,104 +325,4 @@
overflow: hidden; overflow: hidden;
max-height: 100%; max-height: 100%;
max-width: 100%; max-width: 100%;
// height: 5em;
}
@media (max-width: 1000px) {
.game {
grid-template-areas:
"opponent"
"target "
"player ";
grid-template-rows: 1fr 0.2fr 1.5fr;
grid-template-columns: 1fr;
.game-construct {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: min-content 1fr;
.left {
width: 100%;
grid-template-areas:
"skills"
"effects";
grid-template-columns: 1fr;
grid-template-rows: min-content min-content;
}
.skills {
button {
padding: 0 0.5em ;
margin: 0;
}
button.active {
background: #2c2c2c;
}
}
.stats div {
padding: 0;
}
.stats .max {
display: none;
}
.stats .value {
display: flex;
}
.stats svg {
height: 1em;
}
.stats div {
margin: 0 0.2em;
}
.effects {
font-size: 100%;
}
.stats, .name {
font-size: 75%;
}
.skills button {
font-size: 50%;
}
.skill-description {
font-size: 65%;
}
}
.player {
width: calc(100% - 1em);
bottom: 3em;
height: calc(50% - 3em);
}
.opponent {
width: calc(100% - 1em);
.game-construct {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: min-content 1fr;
}
}
#targeting, .resolving-skill {
width: calc(100% - 1em);
}
.player {
width: calc(100% - 1em);
bottom: 3em;
height: calc(50% - 3em);
}
}
} }

View File

@ -9,10 +9,6 @@
grid-template-areas: grid-template-areas:
"vbox info" "vbox info"
"constructs constructs"; "constructs constructs";
hr {
grid-area: rule;
}
} }
@media (max-width: 1920px) { @media (max-width: 1920px) {
@ -88,65 +84,6 @@
border-right-width: 0; border-right-width: 0;
} }
/* 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;
}
@keyframes action { @keyframes action {
0% { 0% {
color: palegoldenrod; color: palegoldenrod;
@ -195,6 +132,7 @@
grid-area: skills; grid-area: skills;
padding: 0 0.5em; padding: 0 0.5em;
margin-bottom: 0.75em; margin-bottom: 0.75em;
z-index: 2;
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
@ -203,12 +141,16 @@
button { button {
height: 3em; height: 3em;
} }
label {
display: flex;
}
} }
.specs { .specs {
grid-area: specs; grid-area: specs;
padding: 0 0.5em; padding: 0 0.5em;
margin-bottom: 0.75em; margin-bottom: 0.75em;
z-index: 2;
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
@ -225,9 +167,13 @@
} }
figcaption { figcaption {
font-size: 75%; // font-size: 75%;
line-height: initial; line-height: initial;
} }
label {
display: flex;
}
} }
.stats { .stats {
@ -239,7 +185,7 @@
text-align: center; text-align: center;
figcaption { figcaption {
font-size: 75%; // font-size: 75%;
} }
// give speed some space // give speed some space

View File

@ -17,9 +17,7 @@
@media (max-width: 800px) { @media (max-width: 800px) {
.instance { .instance {
overflow-y: scroll; font-size: 6pt;
font-size: 8pt;
display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: min-content 1fr; grid-template-rows: min-content 1fr;
grid-template-areas: grid-template-areas:
@ -30,141 +28,4 @@
display: none; display: none;
} }
} }
.instance .nav-btn { display: initial; }
.vbox {
grid-area: vbox;
margin-bottom: 0;
display: grid;
grid-template-rows: min-content min-content min-content min-content;
grid-template-columns: 1fr;
grid-template-areas:
"vbox"
"varrow"
"inventory"
"combiner";
&:not(.visible) {
display: none;
}
.vbox-vbox {
margin-bottom: 1em;
}
}
.vbox-arrow {
display: none;
}
.vbox-inventory .vbox-hdr {
display: block;
}
.vbox-arrow-mobile {
display: block;
grid-area: varrow;
width: 100%;
text-align: center;
margin: 0;
}
.construct-list {
display: grid;
grid-auto-rows: 1fr;
.instance-construct:not(.visible) {
display: none;
};
}
.vbox-inventory {
margin-left: 0;
}
.vbox-combiner {
margin-left: 0;
}
.equip .specs {
margin-top: 0.5em;
border: none;
border-bottom: 1px solid #444;
}
.equip .skills {
border: none;
border-bottom: 1px solid #444;
}
.instance-construct {
display: grid;
grid-template-rows: min-content min-content min-content 1fr min-content;
grid-template-areas:
"name "
"skills "
"specs "
"avatar "
"stats ";
border: 0;
padding: 0;
transition-property: all;
transition-duration: 0.25s;
transition-delay: 0;
transition-timing-function: ease;
}
.instance-construct:first-child {
border-left-width: 0;
}
.instance-construct:not(:last-child) {
border-right: 1px solid #222;
}
.instance-construct .skills {
flex-flow: column;
}
.instance-construct .skills button {
margin: 0;
}
.instance-construct .specs {
margin: 0;
}
.construct-list .stats {
flex-flow: row wrap;
text-align: center;
}
.instance-construct .stats div.speed {
order: -1;
flex: 1 0 100%;
margin: 0;
}
.instance-construct .stats div {
flex: 1 1 33%;
}
.instance-nav {
display: flex;
grid-area: nav;
margin-right: 0;
border-top: 2px solid #444;
}
.instance-nav button {
font-size: 150%;
border-right: 2px solid #444;
}
.instance-nav button:last-child {
border: none;
}
} }

View File

@ -142,6 +142,19 @@ section {
border-color: forestgreen; border-color: forestgreen;
} }
} }
// // all green
// button.ready:enabled {
// background: forestgreen;
// color: black;
// border-color: forestgreen;
// &:hover {
// color: forestgreen;
// border-color: forestgreen;
// background: 0;
// }
// }
} }
} }
@ -215,29 +228,3 @@ section {
} }
} }
@media (max-width: 800px) {
section {
grid-template-columns: 1fr;
.list {
grid-template-columns: 1fr 1fr;
&.play {
grid-template-columns: 1fr;
}
}
}
.menu .team {
grid-template-columns: 1fr;
.construct {
height: 10em;
}
}
.account {
grid-template-columns: 1fr;
}
}

View File

@ -309,6 +309,7 @@ li {
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
z-index: 0;
// pointer-events: none; // pointer-events: none;
} }
@ -317,3 +318,28 @@ li {
height: 1px; height: 1px;
padding: 0px; padding: 0px;
} }
#rotate {
display: none;
z-index: 1000;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-image: url("./../rotate.svg");
background-size: cover;
background-repeat: no-repeat;
background-position: center;
&.show {
display: flex;
}
h1 {
flex: 1;
}
}

View File

@ -1,115 +1,187 @@
@media (max-width: 800px) { @media (max-width: 1000px) {
body { body {
overflow-y: initial; overflow-y: initial;
} }
#mnml { #mnml {
font-size: 12pt; font-size: 8pt;
padding: 0; padding: 0.25em;
.instance {
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: 10fr 1fr; grid-template-rows: min-content 1fr;
grid-template-areas: grid-template-areas:
"main" "vbox"
"footer"; "constructs";
height: 100vh; svg {
max-height: initial; stroke-width: 1.25em;
min-height: initial; }
} }
table td { .game {
height: 2.5em; .team, #targeting, .resolving-skill {
width: calc(90% - 3em);
} }
nav { .game-construct {
grid-template-columns: 1fr;
grid-template-rows: min-content 1fr;
.avatar {
grid-area: initial;
position: absolute;
top: 0;
height: 100%;
width: 100%;
z-index: -1;
}
svg {
height: 1em;
}
}
.skills {
button[disabled] {
display: none; display: none;
} }
}
.effects {
font-size: 1em;
}
.player {
.game-construct {
grid-template-areas:
"left"
"right";
.left {
grid-template-columns: 1fr;
grid-template-rows: 1fr min-content;
grid-template-areas:
"skills"
"effects";
}
}
}
.opponent {
.game-construct {
grid-template-rows: 2fr min-content;
grid-template-rows: 1fr;
grid-template-areas:
"right"
"left";
.left {
grid-template-columns: min-content 1fr;
}
}
.avatar {
bottom: 0px;
}
}
}
.instance-construct {
position: relative;
.skills, .specs {
font-size: 75%;
}
.avatar {
grid-area: initial;
position: absolute;
top: 0;
height: 100%;
width: 100%;
z-index: -1;
}
}
aside { aside {
display: none;
}
footer {
display: flex;
position: fixed;
bottom: 0;
width: 100%;
button { button {
font-size: 90%; margin-bottom: 0.5em;
}
}
} }
} }
#nav-btn, #instance-nav {
display: unset;
}
#mnml.nav-visible nav {
padding: 0.5em;
margin: 0;
display: block;
grid-area: main;
}
#mnml.nav-visible main {
display: none;
}
main {
overflow-x: hidden;
overflow-y: initial;
max-height: 100vh;
padding: 0 0.5em;
}
.welcome .login {
width: 100%;
}
.welcome .options {
width: 100%;
flex-flow: row wrap;
}
.welcome .options button {
flex: 1 0 50%;
}
.timer-container {
margin: 0.5em 0 0 0;
}
.mobile-title {
display: block;
margin-bottom: 1em;
}
.menu-instances {
display: grid;
// portrait menu
@media (max-width: 600px) {
#mnml {
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: 1fr;
grid-template-areas: grid-template-areas:
"constructs" "main"
"inventory" }
"games";
section {
grid-template-columns: 1fr;
.list {
grid-template-columns: 1fr 1fr;
&.play {
grid-template-columns: 1fr;
}
}
}
.account {
grid-template-columns: 1fr;
div {
padding: 0;
}
}
.play-ctrl {
display: none;
} }
.menu { .menu {
height: auto;
display: block;
.options { .options {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: repeat(2, 1fr);
button:not(:last-child) { button:not(:last-child) {
border: 2px solid #222; border: 2px solid #222;
} }
button.logo { button.logo {
grid-column-end: span 2;
border: none; border: none;
margin-right: 0; margin-right: 0;
margin-top: 0.5em; margin-top: 0.5em;
background-position: center; background-position: center;
} }
} }
.team {
grid-template-columns: 1fr;
.construct {
height: 10em;
}
}
.news {
padding: 0;
}
} }
section { section {
@ -117,14 +189,4 @@
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
.account {
div {
padding: 0;
}
}
.play-p {
display: none;
}
} }

View File

@ -22,10 +22,6 @@
grid-gap: 0.5em 1em; grid-gap: 0.5em 1em;
align-items: center; align-items: center;
margin-bottom: 0.5em; margin-bottom: 0.5em;
button {
width: 100%;
}
} }
.vbox-btn { .vbox-btn {
@ -73,6 +69,7 @@
button { button {
height: 4em; height: 4em;
margin: 0; margin: 0;
width: 100%;
// text-transform: none; // text-transform: none;
@ -83,7 +80,7 @@
&.highlight { &.highlight {
color: black; color: black;
background: @white; background: @white;
border: 1px solid @white; // 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 {
@ -100,7 +97,7 @@
figure { figure {
svg { svg {
height: 2em; height: 2em;
stroke-width: 8px; stroke-width: 0.5em;
} }
figcaption { figcaption {
@ -108,3 +105,62 @@
} }
} }
} }
/* 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;
}

View File

@ -15,6 +15,8 @@
<link href="https://fonts.googleapis.com/css?family=Jura" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Jura" rel="stylesheet">
<link rel="stylesheet" href="assets/styles/normalize.css"> <link rel="stylesheet" href="assets/styles/normalize.css">
<link rel="stylesheet" href="assets/styles/skeleton.css"> <link rel="stylesheet" href="assets/styles/skeleton.css">
<link rel="icon" sizes="512x512" href="assets/icons/mnml.png">
<link rel="apple-touch-icon" href="assets/icons/mnml.png">
</head> </head>
</head> </head>
<body> <body>

View File

@ -1,7 +1,7 @@
{ {
"name": "mnml", "name": "MNML",
"description": "mnml pvp atbs", "description": "abstract strategy",
"short_name": "mnml", "short_name": "MNML",
"icons": [ "icons": [
{ {
"src": "./assets/icons/mnml.png", "src": "./assets/icons/mnml.png",
@ -15,7 +15,8 @@
} }
], ],
"start_url": "/index.html", "start_url": "/index.html",
"display": "fullscreen", "display": "standalone",
"orientation": "landscape",
"theme_color": "#000000", "theme_color": "#000000",
"background_color": "#000000" "background_color": "#000000"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "mnml-client", "name": "mnml-client",
"version": "1.7.1", "version": "1.8.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -36,12 +36,10 @@ export const setItemInfo = value => ({ type: 'SET_ITEM_INFO', value });
export const setItemUnequip = value => ({ type: 'SET_ITEM_UNEQUIP', value }); export const setItemUnequip = value => ({ type: 'SET_ITEM_UNEQUIP', value });
export const setMtxActive = value => ({ type: 'SET_MTX_ACTIVE', value }); 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 setNavInstance = value => ({ type: 'SET_NAV_INSTANCE', 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 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 setShowNav = value => ({ type: 'SET_SHOW_NAV', value });
export const setSkip = value => ({ type: 'SET_SKIP', value }); export const setSkip = value => ({ type: 'SET_SKIP', 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 });

View File

@ -160,7 +160,7 @@ 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: 'purple-damage' }; if (red > 0 && blue > 0) return { text: [`+${red}R +${blue}B`, ''], css: 'rb-damage' };
if (red > 0) return { text: [`+${red}R`, ''], css: 'red-damage' }; if (red > 0) return { text: [`+${red}R`, ''], css: 'red-damage' };
if (blue > 0) return { text: [`+${blue}B`, ''], css: 'blue-damage' }; if (blue > 0) return { text: [`+${blue}B`, ''], css: 'blue-damage' };
return nullText; return nullText;

View File

@ -38,7 +38,7 @@ class SiphonTick extends Component {
render() { render() {
return ( return (
<svg <svg
class={'skill-anim'} class='skill-anim'
version="1.1" version="1.1"
id="Layer_1" id="Layer_1"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@ -1,4 +1,5 @@
module.exports = { // This will need to be edited if we change server recipes // This will need to be edited if we change server recipes
module.exports = {
// Attack // Attack
Strike: () => 'red-border', Strike: () => 'red-border',
Blast: () => 'blue-border', Blast: () => 'blue-border',
@ -7,7 +8,7 @@ module.exports = { // This will need to be edited if we change server recipes
Slay: () => 'red-green-border', Slay: () => 'red-green-border',
Siphon: () => 'blue-green-border', Siphon: () => 'blue-green-border',
// Stun // Stun
Link: () => 'blue-greenborder', Link: () => 'blue-green-border',
Bash: () => 'red-border', Bash: () => 'red-border',
Sleep: () => 'green-border', Sleep: () => 'green-border',
Ruin: () => 'blue-border', Ruin: () => 'blue-border',
@ -31,9 +32,9 @@ module.exports = { // This will need to be edited if we change server recipes
Restrict: () => 'red-border', Restrict: () => 'red-border',
Purge: () => 'green-border', Purge: () => 'green-border',
Silence: () => 'blue-border', Silence: () => 'blue-border',
Curse: () => 'red-green-border', Invert: () => 'red-green-border',
Decay: () => 'blue-green-border', Decay: () => 'blue-green-border',
Invert: () => 'red-blue-border', Curse: () => 'red-blue-border',
// // Lifes Upgrades // // Lifes Upgrades
// LifeGG: () => 'green-border', // LifeGG: () => 'green-border',

View File

@ -84,7 +84,7 @@ function Demo(args) {
function inventoryElement() { function inventoryElement() {
return ( return (
<div class="vbox visible"> <div class="vbox">
<div class='vbox-section'> <div class='vbox-section'>
<h2 class='colour-info'> <h2 class='colour-info'>
VBOX PHASE {shapes.Red()} {shapes.Green()} {shapes.Blue()} VBOX PHASE {shapes.Red()} {shapes.Green()} {shapes.Blue()}
@ -117,7 +117,7 @@ function Demo(args) {
: 'empty gray'; : 'empty gray';
const constructEl = c => ( const constructEl = c => (
<div class="instance-construct visible"> <div class="instance-construct">
<h2 class="name" >{c.name}</h2> <h2 class="name" >{c.name}</h2>
<ConstructAvatar construct={c} /> <ConstructAvatar construct={c} />
<div class="skills"> <div class="skills">

View File

@ -1,9 +1,6 @@
const { Component } = require('preact');
const preact = require('preact'); const preact = require('preact');
const { connect } = require('preact-redux'); const { connect } = require('preact-redux');
const Hammer = require('hammerjs');
const Vbox = require('./vbox.component'); const Vbox = require('./vbox.component');
const InfoContainer = require('./info.container'); const InfoContainer = require('./info.container');
const InstanceConstructsContainer = require('./instance.constructs'); const InstanceConstructsContainer = require('./instance.constructs');
@ -16,12 +13,10 @@ const addState = connect(
const { const {
instance, instance,
nav, nav,
navInstance,
} = state; } = state;
return { return {
instance, instance,
nav, nav,
navInstance,
}; };
}, },
@ -30,10 +25,6 @@ const addState = connect(
return dispatch(actions.setInfo(c)); return dispatch(actions.setInfo(c));
} }
function setNavInstance(c) {
return dispatch(actions.setNavInstance(c));
}
function clearItems() { function clearItems() {
dispatch(actions.setCombiner([])); dispatch(actions.setCombiner([]));
@ -48,18 +39,11 @@ const addState = connect(
return { return {
setInfo, setInfo,
clearItems, clearItems,
setNavInstance,
}; };
} }
); );
class Instance extends Component { function Instance(args) {
shouldComponentUpdate(newProps) {
if (newProps.instance !== this.props.instance) return true;
return false;
}
render(args) {
const { const {
instance, instance,
clearItems, clearItems,
@ -89,39 +73,4 @@ class Instance extends Component {
); );
} }
componentDidMount() {
if (!this.h) this.bindSwipes();
}
componentDidUpdate() {
if (!this.h) this.bindSwipes();
}
bindSwipes() {
const instance = document.getElementById('instance');
if (!instance) {
return setTimeout(this.bindSwipes, 50);
}
if (this.h) this.h.destroy();
this.h = new Hammer(instance);
this.h.on('swiperight', () => {
const {
navInstance,
setNavInstance,
} = this.props;
setNavInstance(navInstance === 0 ? 3 : navInstance - 1);
});
this.h.on('swipeleft', () => {
const {
navInstance,
setNavInstance,
} = this.props;
setNavInstance((navInstance + 1) % 4);
});
return true;
}
}
module.exports = addState(Instance); module.exports = addState(Instance);

View File

@ -1,5 +1,6 @@
const { connect } = require('preact-redux'); const { connect } = require('preact-redux');
const preact = require('preact'); const preact = require('preact');
const range = require('lodash/range'); const range = require('lodash/range');
const buttons = require('./buttons'); const buttons = require('./buttons');
@ -19,10 +20,15 @@ const addState = connect(
account, account,
itemInfo, itemInfo,
itemEquip, itemEquip,
navInstance, itemUnequip,
vboxSelected,
tutorial, tutorial,
} = state; } = state;
function sendVboxAcceptEquip(constructId) {
return ws.sendVboxAcceptEquip(instance.id, vboxSelected[0], vboxSelected[1], constructId);
}
function sendVboxApply(constructId, i) { function sendVboxApply(constructId, i) {
return ws.sendVboxApply(instance.id, constructId, i); return ws.sendVboxApply(instance.id, constructId, i);
} }
@ -31,15 +37,22 @@ const addState = connect(
return ws.sendVboxUnequip(instance.id, constructId, item); return ws.sendVboxUnequip(instance.id, constructId, item);
} }
function sendVboxUnequipApply(targetConstructId) {
return ws.sendVboxUnequipApply(instance.id, itemUnequip[0], itemUnequip[1], targetConstructId);
}
return { return {
instance, instance,
player, player,
account, account,
sendVboxAcceptEquip,
sendVboxUnequipApply,
sendVboxApply, sendVboxApply,
itemInfo, itemInfo,
itemEquip, itemEquip,
navInstance, itemUnequip,
sendUnequip, sendUnequip,
vboxSelected,
tutorial, tutorial,
}; };
}, },
@ -76,21 +89,23 @@ function Construct(props) {
construct, construct,
iter, iter,
itemEquip, itemEquip,
itemUnequip,
instance, instance,
mobileVisible,
player, player,
vboxSelected,
tutorial, tutorial,
// Static Info // Static Info
itemInfo, itemInfo,
// Function Calls // Function Calls
sendVboxApply, sendVboxApply,
sendVboxAcceptEquip,
sendVboxUnequipApply,
sendUnequip, sendUnequip,
setActiveConstruct, setActiveConstruct,
setItemUnequip, setItemUnequip,
setItemEquip, setItemEquip,
setInfo, setInfo,
} = props; } = props;
const { vbox } = player; const { vbox } = player;
const duplicateSkill = construct.skills.length !== 0 && construct.skills.every(sk => { const duplicateSkill = construct.skills.length !== 0 && construct.skills.every(sk => {
@ -99,14 +114,16 @@ function Construct(props) {
return sk.skill === vbox.bound[itemEquip]; return sk.skill === vbox.bound[itemEquip];
}); });
const tutorialDisableEquip = tutorialShouldDisableEquip(tutorial, iter, instance, construct); const tutorialDisableEquip = tutorialShouldDisableEquip(tutorial, iter, instance, construct);
function onClick(e) { function onClick(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (duplicateSkill || tutorialDisableEquip) return true; if (duplicateSkill || tutorialDisableEquip) return true;
if (itemEquip !== null) sendVboxApply(construct.id, itemEquip); if (itemEquip !== null) return sendVboxApply(construct.id, itemEquip);
if (vboxSelected[0]) return sendVboxAcceptEquip(construct.id);
if (itemUnequip.length && itemUnequip[0] !== construct.id) return sendVboxUnequipApply(construct.id);
setItemEquip(null); setItemEquip(null);
return setActiveConstruct(construct); setItemUnequip([]);
return true;
} }
function hoverInfo(e, info) { function hoverInfo(e, info) {
@ -153,6 +170,10 @@ function Construct(props) {
const classes = `${equipping ? 'equipping' : ''} ${!skill ? 'empty' : ''} ${border()}`; const classes = `${equipping ? 'equipping' : ''} ${!skill ? 'empty' : ''} ${border()}`;
return ( return (
<label onDragStart={ev => {
ev.dataTransfer.setData('text', '');
skillClick(ev);
}} key={i} draggable="true" onDragEnd={() => setItemUnequip([])}>
<button <button
key={i} key={i}
disabled={!skill && !equipping} disabled={!skill && !equipping}
@ -162,6 +183,7 @@ function Construct(props) {
onMouseOver={e => hoverInfo(e, skill && skill.skill)} > onMouseOver={e => hoverInfo(e, skill && skill.skill)} >
{s} {s}
</button> </button>
</label>
); );
}); });
@ -195,6 +217,10 @@ function Construct(props) {
return ( return (
<label onDragStart={ev => {
ev.dataTransfer.setData('text', '');
specClick(ev);
}} key={i} draggable="true" onDragEnd={() => setItemUnequip([])}>
<button <button
key={i} key={i}
onClick={specClick} onClick={specClick}
@ -202,6 +228,7 @@ function Construct(props) {
onMouseOver={e => hoverInfo(e, s)} > onMouseOver={e => hoverInfo(e, s)} >
{shapes[s]()} {shapes[s]()}
</button> </button>
</label>
); );
}); });
@ -220,10 +247,10 @@ function Construct(props) {
</div>; </div>;
}); });
const classes = `instance-construct ${mobileVisible ? 'visible' : ''}`; const classes = `instance-construct`;
const avatarMouseOver = e => hoverInfo(e, `constructAvatar ${construct.name}`); const avatarMouseOver = e => hoverInfo(e, `constructAvatar ${construct.name}`);
return ( return (
<div key={construct.id} class={classes} onClick={onClick}> <div key={construct.id} class={classes} onClick={onClick} onDragOver={ev => ev.preventDefault()} onDrop={onClick}>
<ConstructAvatar construct={construct} mouseOver={avatarMouseOver}/> <ConstructAvatar construct={construct} mouseOver={avatarMouseOver}/>
<h2 class="name" onMouseOver={e => hoverInfo(e, `constructName ${construct.name}`)}>{construct.name}</h2> <h2 class="name" onMouseOver={e => hoverInfo(e, `constructName ${construct.name}`)}>{construct.name}</h2>
<div class="skills" onMouseOver={e => hoverInfo(e, 'constructSkills')} > <div class="skills" onMouseOver={e => hoverInfo(e, 'constructSkills')} >
@ -242,11 +269,12 @@ function Construct(props) {
class InstanceConstructs extends preact.Component { class InstanceConstructs extends preact.Component {
shouldComponentUpdate(newProps) { shouldComponentUpdate(newProps) {
if (newProps.itemEquip !== this.props.itemEquip) return true; if (newProps.itemEquip !== this.props.itemEquip) return true;
if (newProps.itemUnequip !== this.props.itemUnequip) return true;
if (newProps.tutorial !== this.props.tutorial) return true; if (newProps.tutorial !== this.props.tutorial) return true;
if (newProps.navInstance !== this.props.navInstance) return true;
// JSON or Array objects // JSON or Array objects
if (newProps.player !== this.props.player) return true; if (newProps.player !== this.props.player) return true;
if (newProps.instance !== this.props.instance) return true; if (newProps.instance !== this.props.instance) return true;
if (newProps.vboxSelected !== this.props.vboxSelected) return true;
return false; return false;
} }
@ -254,16 +282,19 @@ class InstanceConstructs extends preact.Component {
const { const {
// Changing state variables // Changing state variables
itemEquip, itemEquip,
itemUnequip,
instance, instance,
navInstance,
player, player,
tutorial, tutorial,
vboxSelected,
// Static data // Static data
itemInfo, itemInfo,
// Function calls // Function calls
setInfo, setInfo,
setActiveConstruct, setActiveConstruct,
sendVboxApply, sendVboxApply,
sendVboxAcceptEquip,
sendVboxUnequipApply,
setVboxHighlight, setVboxHighlight,
setItemUnequip, setItemUnequip,
setItemEquip, setItemEquip,
@ -274,25 +305,28 @@ class InstanceConstructs extends preact.Component {
if (instance.phase === 'Lobby') return false; if (instance.phase === 'Lobby') return false;
const constructs = range(0, 3).map(i => { const constructs = range(0, 3).map(i => {
const tutorialConstruct = tutorialConstructDisplay(player, instance, tutorial, navInstance, i); const tutorialConstruct = tutorialConstructDisplay(player, instance, tutorial, i);
if (tutorialConstruct) return (tutorialConstruct); if (tutorialConstruct) return (tutorialConstruct);
return Construct({ return Construct({
iter: i, iter: i,
construct: player.constructs[i], construct: player.constructs[i],
itemEquip, itemEquip,
itemUnequip,
instance, instance,
setItemUnequip, setItemUnequip,
setItemEquip, setItemEquip,
player, player,
sendVboxApply, sendVboxApply,
sendVboxAcceptEquip,
sendVboxUnequipApply,
setInfo, setInfo,
setActiveConstruct, setActiveConstruct,
itemInfo, itemInfo,
setVboxHighlight, setVboxHighlight,
sendUnequip, sendUnequip,
vboxSelected,
tutorial, tutorial,
mobileVisible: navInstance === i + 1,
}); });
}); });

View File

@ -7,24 +7,28 @@ const Controls = require('./controls');
const Footer = require('./footer'); const Footer = require('./footer');
const addState = connect( const addState = connect(
state => ({ showNav: state.showNav }) ({ game, instance }) => ({ game, instance })
); );
class Mnml extends preact.Component { function Mnml(args) {
shouldComponentUpdate(newProps) { const {
if (newProps.showNav !== this.props.showNav) return true; game,
return false; instance,
} } = args;
const rotateClass = (game || instance) && window.innerWidth < window.innerHeight
? 'show'
: '';
render(args) {
return ( return (
<div id="mnml" class={args.showNav ? 'nav-visible' : ''}> <div id="mnml">
<Main /> <Main />
<Controls /> <Controls />
<Footer /> <Footer />
<div id="rotate" class={rotateClass} >
</div>
</div> </div>
); );
} }
}
module.exports = addState(Mnml); module.exports = addState(Mnml);

View File

@ -60,10 +60,10 @@ function Scoreboard(args) {
} = args; } = args;
const scoreText = () => { const scoreText = () => {
if (player.score === 'Zero' || player.score === 'Lose') return [<span i={0}></span>, <span i={1}></span>, <span i={2}></span>]; if (player.score === 'Zero' || player.score === 'Lose') return [<span i={0}>&#x25AB;</span>, <span i={1}>&#x25AB;</span>, <span i={2}>&#x25AB;</span>];
if (player.score === 'One') return [<span i={0}></span>, <span i={1}></span>, <span i={2}></span>]; if (player.score === 'One') return [<span i={0}>&#x25A0;</span>, <span i={1}>&#x25AB;</span>, <span i={2}>&#x25AB;</span>];
if (player.score === 'Two') return [<span i={0}></span>, <span i={1}></span>, <span i={2}></span>]; if (player.score === 'Two') return [<span i={0}>&#x25A0;</span>, <span i={1}>&#x25A0;</span>, <span i={2}>&#x25AB;</span>];
if (player.score === 'Win') return [<span i={0}></span>, <span i={1}></span>, <span i={2}></span>]; if (player.score === 'Win') return [<span i={0}>&#x25A0;</span>, <span i={1}>&#x25A0;</span>, <span i={2}>&#x25A0;</span>];
return ''; return '';
}; };

View File

@ -117,8 +117,6 @@ function Reshape(args) {
</div> </div>
<div class='list'> <div class='list'>
{shop.owned.map(useMtx)} {shop.owned.map(useMtx)}
</div>
<div class='list'>
{shop.available.map(availableMtx)} {shop.available.map(availableMtx)}
</div> </div>
</div> </div>

View File

@ -59,7 +59,7 @@ class TargetSvg extends Component {
if (tutorialGame) { if (tutorialGame) {
return ( return (
<div class="resolving-skill"> <div class="resolving-skill">
<h2> Select your skills, click on targets and then hit <b>ready</b>.</h2> <h2> Select your skills, click on targets and then hit <b>READY</b>.</h2>
</div> </div>
); );
} }

View File

@ -20,7 +20,6 @@ const addState = connect(
vboxSelected, vboxSelected,
itemInfo, itemInfo,
itemUnequip, itemUnequip,
navInstance,
tutorial, tutorial,
} = state; } = state;
@ -29,6 +28,7 @@ const addState = connect(
} }
function sendVboxAccept(group, index) { function sendVboxAccept(group, index) {
document.activeElement.blur();
return ws.sendVboxAccept(instance.id, group, index); return ws.sendVboxAccept(instance.id, group, index);
} }
@ -57,7 +57,6 @@ const addState = connect(
itemInfo, itemInfo,
itemUnequip, itemUnequip,
sendItemUnequip, sendItemUnequip,
navInstance,
tutorial, tutorial,
}; };
}, },
@ -100,7 +99,6 @@ class Vbox extends preact.Component {
if (newProps.combiner !== this.props.combiner) return true; if (newProps.combiner !== this.props.combiner) return true;
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.reclaiming !== this.props.reclaiming) return true;
if (newProps.navInstance !== this.props.navInstance) 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.player !== this.props.player) return true;
@ -116,7 +114,6 @@ class Vbox extends preact.Component {
player, player,
reclaiming, reclaiming,
tutorial, tutorial,
navInstance,
vboxSelected, vboxSelected,
instance, instance,
@ -180,7 +177,7 @@ class Vbox extends preact.Component {
} }
function availableBtn(v, group, index) { function availableBtn(v, group, index) {
if (!v) return <button disabled class='empty' >&nbsp;</button>; if (!v) return <button disabled class='empty' key={(group * 10) + index} >&nbsp;</button>;
const selected = vboxSelected[0] === group && vboxSelected[1] === index; const selected = vboxSelected[0] === group && vboxSelected[1] === index;
// state not yet set in double click handler // state not yet set in double click handler
@ -194,8 +191,6 @@ class Vbox extends preact.Component {
e.stopPropagation(); e.stopPropagation();
setItemEquip(null); setItemEquip(null);
setCombiner([]); setCombiner([]);
if (selected) return clearVboxSelected();
setInfo(vbox.free[group][index]); setInfo(vbox.free[group][index]);
return setVboxSelected([group, index]); return setVboxSelected([group, index]);
} }
@ -217,28 +212,24 @@ class Vbox extends preact.Component {
const classes = `${v.toLowerCase()} ${selected ? 'highlight' : ''} ${comboHighlight}`; const classes = `${v.toLowerCase()} ${selected ? 'highlight' : ''} ${comboHighlight}`;
if (shapes[v]) { const vboxObject = shapes[v] ? shapes[v]() : v;
return ( return (
<label draggable='true'
onDragStart={ev => {
onClick(ev);
ev.dataTransfer.setData('text', '')
}}
key={group * 10 + index}
onDragEnd={clearVboxSelected}>
<button <button
class={classes} class={classes}
onMouseOver={e => vboxHover(e, v)} onMouseOver={e => vboxHover(e, v)}
onMouseDown={onClick} onMouseDown={onClick}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
onDblClick={onDblClick} >
{shapes[v]()}
</button>
);
}
return (
<button
class={classes}
onMouseDown={onClick}
onClick={e => e.stopPropagation()}
onDblClick={onDblClick} onDblClick={onDblClick}
onMouseOver={e => vboxHover(e, v)}> > {vboxObject}
{v}
</button> </button>
</label>
); );
} }
@ -288,7 +279,7 @@ class Vbox extends preact.Component {
const inventoryHighlight = vboxSelecting || itemUnequip.length; const inventoryHighlight = vboxSelecting || itemUnequip.length;
if (!v && v !== 0) { if (!v && v !== 0) {
return <button disabled={!inventoryHighlight} class={inventoryHighlight ? 'receiving' : 'empty'} >&nbsp;</button>; return <button key={i} disabled={!inventoryHighlight} class={inventoryHighlight ? 'receiving' : 'empty'} >&nbsp;</button>;
} }
const combinerItems = combiner.map(j => vbox.bound[j]); const combinerItems = combiner.map(j => vbox.bound[j]);
@ -306,54 +297,55 @@ class Vbox extends preact.Component {
} return false; } return false;
}) ? 'combo-border' : ''; }) ? 'combo-border' : '';
function onClick(e) { function onClick(type) {
if (vboxSelecting) clearVboxSelected(); if (vboxSelecting) clearVboxSelected();
if (reclaiming) return sendVboxReclaim(i); if (reclaiming) return sendVboxReclaim(i);
const combinerContainsIndex = combiner.indexOf(i) > -1;
// 4 things selected // 4 things selected
if (combiner.length > 2) { if (combiner.length > 2 && !combinerContainsIndex) {
setInfo(vbox.bound[i]); setInfo(vbox.bound[i]);
return combinerChange([i]); return combinerChange([i]);
} }
// removing // removing
const combinerIndex = combiner.indexOf(i); if (combinerContainsIndex) {
if (combinerIndex > -1) { if (type === 'click') {
return combinerChange(without(combiner, i)); return combinerChange(without(combiner, i));
} }
return true;
}
combiner.push(i); if (!comboHighlight && !combinerContainsIndex) {
if (!comboHighlight) {
setInfo(vbox.bound[i]); setInfo(vbox.bound[i]);
return combinerChange([i]); return combinerChange([i]);
} }
combiner.push(i);
return combinerChange(combiner); return combinerChange(combiner);
} }
const highlighted = combiner.indexOf(i) > -1; const highlighted = combiner.indexOf(i) > -1;
const border = buttons[removeTier(v)] ? buttons[removeTier(v)]() : ''; const border = buttons[removeTier(v)] ? buttons[removeTier(v)]() : '';
const classes = `${highlighted ? 'highlight' : border} ${comboHighlight}`; const classes = `${highlighted ? 'highlight' : border} ${comboHighlight}`;
if (shapes[v]) {
const invObject = shapes[v] ? shapes[v]() : v;
return ( return (
<label
key={i}
draggable='true'
onDragStart={ev => {
onClick('drag');
ev.dataTransfer.setData('text', '');
}}>
<button <button
class={classes} class={classes}
onMouseOver={e => vboxHover(e, v)} onMouseOver={e => vboxHover(e, v)}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
onMouseDown={onClick}> onMouseDown={() => onClick('click')}>
{shapes[v]()} {invObject}
</button>
);
}
return (
<button
class={classes}
onMouseDown={onClick}
onClick={e => e.stopPropagation()}
onMouseOver={e => vboxHover(e, v)}>
{v}
</button> </button>
</label>
); );
} }
@ -408,7 +400,10 @@ class Vbox extends preact.Component {
<div class={inventoryClass} <div class={inventoryClass}
onMouseDown={inventoryClick} onMouseDown={inventoryClick}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
style={vboxSelecting || (itemUnequip.length) ? { cursor: 'pointer' } : null}> style={vboxSelecting || (itemUnequip.length) ? { cursor: 'pointer' } : null}
onDragOver={ev => ev.preventDefault()}
onDrop={inventoryClick}
>
<div class="vbox-hdr"> <div class="vbox-hdr">
<h3 <h3
onTouchStart={e => e.target.scrollIntoView(true)} onTouchStart={e => e.target.scrollIntoView(true)}
@ -439,7 +434,7 @@ class Vbox extends preact.Component {
return setInfo(newInfo); return setInfo(newInfo);
} }
const classes = `vbox ${navInstance === 0 ? 'visible' : ''}`; const classes = `vbox`;
return ( return (
<div class={classes}> <div class={classes}>
{vboxElement()} {vboxElement()}

View File

@ -12,6 +12,13 @@ const { tutorialVbox } = require('./tutorial.utils');
function registerEvents(store) { function registerEvents(store) {
function notify(msg) { function notify(msg) {
if (window.Notification && window.Notification.permission === 'granted') {
const n = new Notification('MNML', {
body: msg,
tag: 'MNML',
});
}
return infoToast(msg); return infoToast(msg);
} }
@ -127,9 +134,13 @@ function registerEvents(store) {
} }
function setAccount(account) { function setAccount(account) {
if (account) { if (account && process.env.NODE_ENV !== 'development') {
LogRocket.init('yh0dy3/mnml'); LogRocket.init('yh0dy3/mnml');
LogRocket.identify(account.id, account); LogRocket.identify(account.id, account);
if (window.Notification) {
window.Notification.requestPermission();
}
} }
store.dispatch(actions.setAccount(account)); store.dispatch(actions.setAccount(account));
@ -179,6 +190,7 @@ function registerEvents(store) {
store.dispatch(actions.setItemEquip(null)); store.dispatch(actions.setItemEquip(null));
store.dispatch(actions.setItemUnequip([])); store.dispatch(actions.setItemUnequip([]));
store.dispatch(actions.setVboxHighlight([])); store.dispatch(actions.setVboxHighlight([]));
store.dispatch(actions.setVboxSelected([]));
} }
function setAccountInstances(v) { function setAccountInstances(v) {
@ -212,7 +224,9 @@ function registerEvents(store) {
if (v.phase === 'Finished') { if (v.phase === 'Finished') {
ws.sendAccountInstances(); ws.sendAccountInstances();
} }
if (localStorage.getItem('tutorial-complete')) {
// instance.mobile.less hides info at @media 1000
if (localStorage.getItem('tutorial-complete') || window.innerWidth <= 1100) {
store.dispatch(actions.setTutorial(null)); store.dispatch(actions.setTutorial(null));
} else if (v.time_control === 'Practice' && v.rounds.length === 1 && tutorial) { } else if (v.time_control === 'Practice' && v.rounds.length === 1 && tutorial) {
tutorialVbox(player, store, tutorial); tutorialVbox(player, store, tutorial);

View File

@ -45,8 +45,6 @@ module.exports = {
itemUnequip: createReducer([], 'SET_ITEM_UNEQUIP'), itemUnequip: createReducer([], 'SET_ITEM_UNEQUIP'),
mtxActive: createReducer(null, 'SET_MTX_ACTIVE'), mtxActive: createReducer(null, 'SET_MTX_ACTIVE'),
nav: createReducer(null, 'SET_NAV'), nav: createReducer(null, 'SET_NAV'),
navInstance: createReducer(0, 'SET_NAV_INSTANCE'),
showNav: createReducer(null, 'SET_SHOW_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'), reclaiming: createReducer(false, 'SET_RECLAIMING'),

View File

@ -82,6 +82,11 @@ function createSocket(events) {
events.clearInstance(); events.clearInstance();
} }
function sendVboxAcceptEquip(instanceId, group, index, constructId) {
send(['VboxAcceptEquip', { instance_id: instanceId, group, index, construct_id: constructId }]);
events.clearInstance();
}
function sendVboxApply(instanceId, constructId, index) { function sendVboxApply(instanceId, constructId, index) {
send(['VboxApply', { instance_id: instanceId, construct_id: constructId, index }]); send(['VboxApply', { instance_id: instanceId, construct_id: constructId, index }]);
events.clearInstance(); events.clearInstance();
@ -92,6 +97,11 @@ function createSocket(events) {
events.clearInstance(); events.clearInstance();
} }
function sendVboxUnequipApply(instanceId, constructId, target, targetConstructId) {
send(['VboxUnequipApply', { instance_id: instanceId, construct_id: constructId, target, target_construct_id: targetConstructId }]);
events.clearInstance();
}
function sendVboxDiscard(instanceId) { function sendVboxDiscard(instanceId) {
send(['VboxDiscard', { instance_id: instanceId }]); send(['VboxDiscard', { instance_id: instanceId }]);
events.clearInstance(); events.clearInstance();
@ -99,7 +109,7 @@ function createSocket(events) {
function sendVboxCombine(instanceId, indices) { function sendVboxCombine(instanceId, indices) {
send(['VboxCombine', { instance_id: instanceId, indices }]); send(['VboxCombine', { instance_id: instanceId, indices }]);
events.clearCombiner(); events.clearInstance();
} }
function sendVboxReclaim(instanceId, index) { function sendVboxReclaim(instanceId, index) {
@ -264,13 +274,14 @@ function createSocket(events) {
Pong: onPong, Pong: onPong,
Demo: onDemo, Demo: onDemo,
QueueRequested: () => events.notify('pvp queue request received'), QueueRequested: () => events.notify('PVP queue request received.'),
QueueJoined: () => events.notify('you have joined the pvp queue'), QueueJoined: () => events.notify('You have joined the PVP queue.'),
InviteRequested: () => events.notify('pvp invite request received'), QueueFound: () => events.notify('Your PVP game has started.'),
InviteRequested: () => events.notify('PVP invite request received.'),
Invite: code => events.setInvite(code), Invite: code => events.setInvite(code),
InstanceChat: chat => events.setInstanceChat(chat), InstanceChat: chat => events.setInstanceChat(chat),
ChatWheel: wheel => events.setChatWheel(wheel), ChatWheel: wheel => events.setChatWheel(wheel),
Joining: () => events.notify('searching for instance...'), // Joining: () => events.notify('Searching for instance...'),
Processing: () => true, Processing: () => true,
Error: errHandler, Error: errHandler,
@ -307,8 +318,11 @@ function createSocket(events) {
return handlers[msgType](params); return handlers[msgType](params);
} }
let attempts = 1;
// Connection opened // Connection opened
function onOpen() { function onOpen() {
attempts = 0;
toast.info({ toast.info({
message: 'connected', message: 'connected',
position: 'topRight', position: 'topRight',
@ -327,12 +341,21 @@ function createSocket(events) {
} }
function onClose(event) { function onClose(event) {
attempts *= 2;
if (attempts > 10) {
toast.warning({
message: 'unable to connect, refreshing...',
position: 'topRight',
});
setTimeout(() => window.location.reload(true), 2000);
}
console.error('WebSocket closed', event); console.error('WebSocket closed', event);
toast.warning({ toast.warning({
message: 'disconnected', message: 'disconnected',
position: 'topRight', position: 'topRight',
}); });
return setTimeout(connect, 5000); return setTimeout(connect, attempts * 1000);
} }
function connect() { function connect() {
@ -381,11 +404,13 @@ function createSocket(events) {
sendInstanceChat, sendInstanceChat,
sendVboxAccept, sendVboxAccept,
sendVboxAcceptEquip,
sendVboxApply, sendVboxApply,
sendVboxReclaim, sendVboxReclaim,
sendVboxCombine, sendVboxCombine,
sendVboxDiscard, sendVboxDiscard,
sendVboxUnequip, sendVboxUnequip,
sendVboxUnequipApply,
sendItemInfo, sendItemInfo,

View File

@ -1,11 +1,10 @@
const preact = require('preact'); const preact = require('preact');
const actions = require('./actions'); const actions = require('./actions');
function tutorialConstructDisplay(player, instance, tutorial, navInstance, i) { function tutorialConstructDisplay(player, instance, tutorial, i) {
if (instance.time_control === 'Practice' && instance.rounds.length === 1 && tutorial && tutorial < 6) { if (instance.time_control === 'Practice' && instance.rounds.length === 1 && tutorial && tutorial < 6) {
if (tutorial <= 2 || (tutorial > 2 && i > 0)) { if (tutorial <= 2 || (tutorial > 2 && i > 0)) {
const mobileVisible = navInstance === i + 1; const classes = `instance-construct`;
const classes = `instance-construct ${mobileVisible ? 'visible' : ''}`;
return (<div key={player.constructs[i].id} class={classes}></div>); return (<div key={player.constructs[i].id} class={classes}></div>);
} }
} }
@ -200,13 +199,16 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
} }
if (tutorial === 8) { if (tutorial === 8) {
if (window.innerWidth < 1000) {
return exit();
}
return ( return (
<div> <div>
<h2>Tutorial</h2> <h2>Tutorial</h2>
<p>You've completed the tutorial! Try to create more skill and spec combinations. </p> <p>You've completed the tutorial! Try to create more skill and spec combinations. </p>
<p>You can unequip skills and specs back into the inventory by double clicking. <br /> <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> Reclaim can be used to refund the cost of items in your inventory. </p>
<p>Click the <b>EXIT TUTORIAL</b> button to replace this section with more information.</p>
</div> </div>
); );
} }
@ -214,10 +216,11 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) {
}; };
const classes = tutorial === 8 ? 'focus' : ''; const classes = tutorial === 8 ? 'focus' : '';
const text = tutorial === 8 ? 'Continue' : 'Close Tutorial'
const exitTutorial = <button const exitTutorial = <button
class={classes} class={classes}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
onMouseDown={exit}> Exit Tutorial </button>; onMouseDown={exit}> {text} </button>;
return ( return (
<div class='tutorial'> <div class='tutorial'>

View File

@ -264,7 +264,7 @@ function effectInfo(i) {
} }
switch (i.effect) { switch (i.effect) {
case 'Amplify': return `Increases construct RedPower and BluePower by ${i.meta[1] - 100}%`; case 'Amplify': return `Increases construct RedPower BluePower GreenPower by ${i.meta[1] - 100}%`;
case 'Banish': return 'Banished construct cannot cast or take damage'; case 'Banish': return 'Banished construct cannot cast or take damage';
case 'Block': return `Reduces construct red damage and blue damage taken by ${100 - i.meta[1]}%`; case 'Block': return `Reduces construct red damage and blue damage taken by ${100 - i.meta[1]}%`;
case 'Buff': return `Increases construct RedPower BluePower SpeedStat by ${i.meta[1] - 100}%`; case 'Buff': return `Increases construct RedPower BluePower SpeedStat by ${i.meta[1] - 100}%`;

View File

@ -15,6 +15,12 @@ map $http_upgrade $connection_upgrade {
server { server {
server_name mnml.gg; server_name mnml.gg;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
location / { location / {
root /var/lib/mnml/public/current; root /var/lib/mnml/public/current;
index index.html; index index.html;

View File

@ -15,8 +15,14 @@ map $http_upgrade $connection_upgrade {
server { server {
server_name sixtysix.pro; server_name sixtysix.pro;
auth_basic "who dis"; gzip on;
auth_basic_user_file /etc/mnml/htpasswd.users; gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
# auth_basic "who dis";
# auth_basic_user_file /etc/mnml/htpasswd.users;
location / { location / {
root /var/lib/mnml/public/current; root /var/lib/mnml/public/current;

View File

@ -1,82 +0,0 @@
const fs = require('fs');
const renderer = require('sdftosvg');
const SVG_OPTS = {
backgroundColor: 'none',
}
function convert(i) {
const output = `./../client/assets/molecules/${i}.svg`;
return new Promise((resolve, reject) => {
fs.readFile(`./molecules/mol${i}`, 'utf8', function rfCb(err, sdf) {
if (err) return reject(err);
// write svg
renderer.renderSdfToSvg(sdf, SVG_OPTS, function(err, svg) {
if (err) return reject(err);
fs.writeFile(output, svg, function written(err) {
if (err) reject(err);
resolve();
});
});
})
});
}
async function loop() {
for (var i = 10000; i >= 0; i--) {
try {
await convert(i);
console.log('finished record', i);
} catch (e) {
console.error('record error', i, e);
}
}
}
loop();
// const request = require('request');
// const fs = require('fs');
// const renderer = require('sdftosvg');
// const SVG_OPTS = {
// backgroundColor: 'none',
// }
// function fetch(i) {
// return new Promise((resolve, reject) => {
// const url = `https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/CID/${i}/record/SDF/?record_type=2d&response_type=save&response_basename=Structure2D_CID_2519`;
// const output = `./../client/assets/molecules/${i}.svg`;
// request(url, function cb(err, res, body) {
// if (err) reject(err);
// // write file
// fs.writeFile(`./molecules/Structure2D_CID_${i}.sdf`, body, function written(err) {
// if (err) reject(err);
// });
// // write svg
// renderer.renderSdfToSvg(body, SVG_OPTS, function(err, svg) {
// if (err) reject(err);
// fs.writeFile(output, svg, function written(err) {
// if (err) reject(err);
// resolve();
// });
// });
// });
// });
// }
// async function loop() {
// for (var i = 10000; i >= 0; i--) {
// try {
// await fetch(i);
// console.log('finished record', i);
// } catch (e) {
// console.error(e);
// }
// }
// }

View File

@ -1,12 +0,0 @@
const fs = require('fs');
for (let i = 10000; i >= 0; i--) {
fs.access(`./../client/assets/molecules/${i}.svg`, (err) => {
if (err) {
fs.copyFileSync(`./../client/assets/molecules/726.svg`, `./../client/assets/molecules/${i}.svg`);
console.log('defaulted', i);
}
return true;
})
}

View File

@ -0,0 +1,10 @@
const uuidv4 = require('uuid/v4');
// give everybody the shapes mtx
exports.up = async knex => {
await knex.raw(`
DELETE from mtx
WHERE variant = 'Molecular';
`);
};
exports.down = async () => {};

View File

@ -1,18 +0,0 @@
const renderer = require('sdftosvg');
const uuidv4 = require('uuid/v4');
const fs = require('fs');
function convert(f) {
const uuid = uuidv4();
const input = `./molecules/${f}`;
const output = `./../client/assets/molecules/${uuid}.svg`;
fs.readFile(input, 'utf8', (err, sdf) => {
renderer.renderSdfToSvg(sdf, {}, function(err, svg) {
if (err) console.error(input, err);
fs.writeFile(output, svg, 'utf8', err => {
if (err) console.error(err);
});
});
});
}

View File

@ -1,6 +1,6 @@
{ {
"name": "mnml-ops", "name": "mnml-ops",
"version": "1.7.1", "version": "1.8.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "mnml" name = "mnml"
version = "1.7.1" version = "1.8.0"
authors = ["ntr <ntr@smokestack.io>"] authors = ["ntr <ntr@smokestack.io>"]
[dependencies] [dependencies]

View File

@ -67,13 +67,13 @@ pub fn select(db: &Db, id: Uuid) -> Result<Account, Error> {
pub fn chat_wheel(_db: &Db, _id: Uuid) -> Result<Vec<String>, Error> { pub fn chat_wheel(_db: &Db, _id: Uuid) -> Result<Vec<String>, Error> {
return Ok(vec![ return Ok(vec![
"gl".to_string(),
"hf".to_string(),
"gg".to_string(), "gg".to_string(),
"thx".to_string(), "glhf".to_string(),
"nice".to_string(),
"hmm".to_string(), "hmm".to_string(),
"ok".to_string(), "ok".to_string(),
"rekt".to_string(),
"thx".to_string(),
"nice".to_string(),
"...".to_string(), "...".to_string(),
]) ])
} }

View File

@ -220,11 +220,11 @@ impl Construct {
account: id, account: id,
img: Uuid::new_v4(), img: Uuid::new_v4(),
red_power: ConstructStat { base: 320, value: 320, max: 320, stat: Stat::RedPower }, red_power: ConstructStat { base: 320, value: 320, max: 320, stat: Stat::RedPower },
red_life: ConstructStat { base: 0, value: 0, max: 0, stat: Stat::RedLife }, red_life: ConstructStat { base: 125, value: 125, max: 125, stat: Stat::RedLife },
blue_power: ConstructStat { base: 320, value: 320, max: 320, stat: Stat::BluePower }, blue_power: ConstructStat { base: 320, value: 320, max: 320, stat: Stat::BluePower },
blue_life: ConstructStat { base: 0, value: 0, max: 0, stat: Stat::BlueLife }, blue_life: ConstructStat { base: 125, value: 125, max: 125, stat: Stat::BlueLife },
green_power: ConstructStat { base: 300, value: 300, max: 300, stat: Stat::GreenPower }, green_power: ConstructStat { base: 300, value: 300, max: 300, stat: Stat::GreenPower },
green_life: ConstructStat { base: 950, value: 950, max: 950, stat: Stat::GreenLife }, green_life: ConstructStat { base: 800, value: 800, max: 800, stat: Stat::GreenLife },
speed: ConstructStat { base: 100, value: 100, max: 100, stat: Stat::Speed }, speed: ConstructStat { base: 100, value: 100, max: 100, stat: Stat::Speed },
// evasion: ConstructStat { base: 0, value: 0, max: 0, stat: Stat::Evasion }, // evasion: ConstructStat { base: 0, value: 0, max: 0, stat: Stat::Evasion },
skills: vec![], skills: vec![],

View File

@ -105,7 +105,7 @@ impl Effect {
Effect::Absorption => vec![Stat::RedPower, Stat::BluePower], Effect::Absorption => vec![Stat::RedPower, Stat::BluePower],
Effect::Amplify => vec![Stat::RedPower, Stat::BluePower], Effect::Amplify => vec![Stat::GreenPower, Stat::RedPower, Stat::BluePower],
Effect::Curse => vec![Stat::RedDamageTaken, Stat::BlueDamageTaken], Effect::Curse => vec![Stat::RedDamageTaken, Stat::BlueDamageTaken],
Effect::Hybrid => vec![Stat::GreenPower], Effect::Hybrid => vec![Stat::GreenPower],

View File

@ -1229,7 +1229,7 @@ mod tests {
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].is_stunned() == false); assert!(game.player_by_id(x_player.id).unwrap().constructs[0].is_stunned() == false);
// riposte // riposte
assert_eq!(game.player_by_id(y_player.id).unwrap().constructs[0].green_life(), ( assert_eq!(game.player_by_id(y_player.id).unwrap().constructs[0].green_life(), (
y_construct.green_life() - x_construct.red_power().pct(Skill::CounterAttack.multiplier()))); y_construct.green_life() + y_construct.red_life() - x_construct.red_power().pct(Skill::CounterAttack.multiplier())));
} }
#[test] #[test]

View File

@ -1,5 +1,4 @@
use std::f64; use std::f64;
use std::fs::copy;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::char::from_u32; use std::char::from_u32;
@ -9,25 +8,7 @@ use rand::prelude::*;
use rand::distributions::{Normal, WeightedIndex}; use rand::distributions::{Normal, WeightedIndex};
use failure::Error; use failure::Error;
use failure::err_msg; // use failure::err_msg;
pub fn molecular_write(id: Uuid) -> Result<Uuid, Error> {
let mut rng = thread_rng();
for _i in 0..100 {
let mol: u32 = rng.gen_range(0, 10000);
let src = format!("/var/lib/mnml/data/molecules/{}.svg", mol);
let dest = format!("/var/lib/mnml/public/imgs/{}.svg", id);
debug!("molecule src={:?}", src);
debug!("molecule dest={:?}", dest);
if let Ok(_bytes) = copy(&src, &dest) {
info!("new molecule img generated src={:?} dest={:?}", src, dest);
return Ok(id);
}
}
return Err(err_msg("too many missing molecules. wrong directory?"))
}
pub fn invader_write(id: Uuid) -> Result<Uuid, Error> { pub fn invader_write(id: Uuid) -> Result<Uuid, Error> {
let mut rng = thread_rng(); let mut rng = thread_rng();
@ -257,6 +238,118 @@ fn _hieroglyph() -> String {
return s; return s;
} }
pub fn smile(id: Uuid) -> Result<Uuid, Error> {
let mut rng = thread_rng();
let mut svg = Vec::new();
// distribution for lightness
// bellcurve around 75%
let l_dist = Normal::new(50.0, 10.0);
let s_dist = Normal::new(50.0, 20.0);
// let stroke_width = rng.gen_range(1, 3);
let stroke_width = 3;
let h = rng.gen_range(0, 360);
let s = s_dist.sample(&mut rng) as usize;
let l = l_dist.sample(&mut rng) as usize;
let eye_colour = format!("hsl({:}, {:}%, {:}%)", h, s, l);
let h = rng.gen_range(0, 360);
let s = s_dist.sample(&mut rng) as usize;
let l = l_dist.sample(&mut rng) as usize;
let mouth_colour = format!("hsl({:}, {:}%, {:}%)", h, s, l);
// basic layout is 200x200 box w/ 100 padding
// 2:1 for each x,y
// left eye is at 0,0
// 50W 25H
let eyes_left = [
("M0,0 L25,25 L50,0", 1), // v
("M0,25 L25,0 L50,25", 1), // ^
("M0,0 L50,0", 1), // -
("M0,25 L50,25", 1), // _
("M0,0 L50,25 M50,0 L0,25", 1), // x
("M0,0 L50,12.5 L0,25", 1), // >
("M50,0 L0,12.5 L50,25", 1), // <
("M0,0 L0,25 L50,25", 1), // L
("M50,0 L50,25 L0,25", 1), // J
("M0,0 L50,0 M50,6.25 L50,12.5 M50,18.75 L50,25", 1), // ;
("M12.5,0 L37.5,0 L37.5,25 L12.5,25 L12.5,0", 1), // o
];
let eye_left_dist = WeightedIndex::new(eyes_left.iter().map(|v| v.1))?;
// right eye is 150,0
// 50W 25H
let eyes_right = [
("M150,0 L175,25 L200,0", 1), // v
("M150,25 L175,0 L200,25", 1), // ^
("M150,0 L200,0", 1), // -
("M150,25 L200,25", 1), // _
("M150,0 L200,25 M200,0 L150,25", 1), // x
("M150,0 L200,12.5 L150,25", 1), // >
("M200,0 L150,12.5 L200,25", 1), // <
("M150,0 L150,25 L200,25", 1), // L
("M200,0 L200,25 L150,25", 1), // J
("M150,0 L200,0 M150,6.25 L150,12.5 M150,18.75 L150,25", 1), // ;
("M162.5,0 L187.5,0 L187.5,25 L162.5,25 L162.5,0", 1), // o
];
let eye_right_dist= WeightedIndex::new(eyes_right.iter().map(|v| v.1))?;
// mouth is 50,75
// 100W 25H
let mouths = [
("M50,100 L150,100", 1), // _
("M50,75 L150,75 L125,100 L75,100 L50,75", 1), // D
("M50,75 L75,100 L100,75 L125,100 L150,75", 1), // w
("M50,75 L75,75 L75,87.5 M75,75 L125,75 L125,87.5 M125,75 L150,75", 1), // vamp
("M50,75 L150,75 M50,75 L50,87.5 M75,75 L75,87.5 M100,75 L100,87.5 M125,75 L125,87.5 M150,75 L150,87.5", 1), // mm
("M75,75 L125,75 L125,100 L75,100 L75,75", 1), // o
("M50,75 L150,100 M150,75 L50,100", 1), // x
("M50,75 L150,75 L150,100 L125,100 L125,75", 1), // p
("M50,75 L150,75 M50,75 L50,100 L75,100 L75,75", 1), // d
// ("M50,75 L50,100 L150,100 L150,75", 1), // u
("M50,100 L50,75 L150,75 L150,100", 1), // n
("M50,75 L50,100 L150,75 L150,100", 1), // Z
("M50,87.5 L100,75 L150,87.5 L100,100 L50,87.5", 1), // Z
];
let mouth_dist = WeightedIndex::new(mouths.iter().map(|v| v.1))?;
write!(&mut svg, "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='-50 -100 300 300' width='200' height='200'><g>")?;
let left_eye_path = eyes_left[eye_left_dist.sample(&mut rng)].0;
// left eye
write!(&mut svg,
"<path fill=\"none\" stroke=\"{:}\" stroke-width=\"{:}px\" d=\"{:}\" />",
eye_colour, stroke_width, left_eye_path)?;
let right_eye_path = eyes_right[eye_right_dist.sample(&mut rng)].0;
// right eye
write!(&mut svg,
"<path fill=\"none\" stroke=\"{:}\" stroke-width=\"{:}px\" d=\"{:}\" />",
eye_colour, stroke_width, right_eye_path)?;
let mouth_path = mouths[mouth_dist.sample(&mut rng)].0;
// mouth
write!(&mut svg,
"<path fill=\"none\" stroke=\"{:}\" stroke-width=\"{:}px\" d=\"{:}\" />",
mouth_colour, stroke_width, mouth_path)?;
write!(&mut svg, "</g></svg>")?;
// let dest = format!("/var/lib/mnml/face.svg");
let dest = format!("/var/lib/mnml/public/imgs/{}.svg", id);
let mut file = File::create(dest)?;
file.write_all(&svg)?;
Ok(id)
}
pub fn exists(id: Uuid) -> bool { pub fn exists(id: Uuid) -> bool {
std::path::Path::new(&format!("/var/lib/mnml/public/imgs/{}.svg", id)).exists() std::path::Path::new(&format!("/var/lib/mnml/public/imgs/{}.svg", id)).exists()
} }
@ -264,7 +357,6 @@ pub fn exists(id: Uuid) -> bool {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
// #[test] // #[test]
// fn invader_img_test() { // fn invader_img_test() {
// for i in 0..100 { // for i in 0..100 {
@ -283,107 +375,9 @@ mod tests {
// shapes_write(Uuid::new_v4()).unwrap(); // shapes_write(Uuid::new_v4()).unwrap();
// } // }
// } // }
#[test]
fn smile_test() {
smile(Uuid::new_v4()).unwrap();
}
} }
// function createColor() {
// //saturation is the whole color spectrum
// var h = Math.floor(rand() * 360);
// //saturation goes from 40 to 100, it avoids greyish colors
// var s = ((rand() * 60) + 40) + '%';
// //lightness can be anything from 0 to 100, but probabilities are a bell curve around 75%
// var l = ((rand()+rand()+rand()+rand()) * 25) + '%';
// var color = 'hsl(' + h + ',' + s + ',' + l + ')';
// return color;
// }
// function createImageData(size) {
// var width = size; // Only support square icons for now
// var height = size;
// var dataWidth = Math.ceil(width / 2);
// var mirrorWidth = width - dataWidth;
// var data = [];
// for(var y = 0; y < height; y++) {
// var row = [];
// for(var x = 0; x < dataWidth; x++) {
// // this makes foreground and background color to have a 43% (1/2.3) probability
// // spot color has 13% chance
// row[x] = Math.floor(rand()*2.3);
// }
// var r = row.slice(0, mirrorWidth);
// r.reverse();
// row = row.concat(r);
// for(var i = 0; i < row.length; i++) {
// data.push(row[i]);
// }
// }
// return data;
// }
// function buildOpts(opts) {
// var newOpts = {};
// newOpts.seed = opts.seed || Math.floor((Math.random()*Math.pow(10,16))).toString(16);
// seedrand(newOpts.seed);
// newOpts.size = opts.size || 8;
// newOpts.scale = opts.scale || 4;
// newOpts.color = opts.color || createColor();
// newOpts.bgcolor = opts.bgcolor || createColor();
// newOpts.spotcolor = opts.spotcolor || createColor();
// return newOpts;
// }
// ],
// - _ => vec![
// - (ItemAction::RerollStamina, 1),
// - (ItemAction::RerollPhysDamage, 1),
// - (ItemAction::RerollSpellDamage, 1),
// - (ItemAction::RerollSpeed, 1),
// - (ItemAction::RerollArmour, 1),
// - (ItemAction::RerollSpellShield, 1),
// - (ItemAction::RerollEvasion, 1),
// - ],
// + // _ => vec![
// + // (ItemAction::RerollStamina, 1),
// + // (ItemAction::RerollPhysDamage, 1),
// + // (ItemAction::RerollSpellDamage, 1),
// + // (ItemAction::RerollSpeed, 1),
// + // (ItemAction::RerollArmour, 1),
// + // (ItemAction::RerollSpellShield, 1),
// + // (ItemAction::RerollEvasion, 1),
// + // ],
// }
// }
// -pub fn item_drop(tx: &mut Transaction, account_id: Uuid, mode: GameMode) -> Result<Item, Error> {
// +pub fn item_drop(tx: &mut Transaction, account_id: Uuid, mode: GameMode) -> Result<(), Error> {
// let mut rng = thread_rng();
// - let actions = mode_drops(mode);
// + let log_normal = LogNormal::new(1.0, 1.0);
// + let num_drops = log_normal.sample(&mut rng).floor() as usize;
// +
// + println!("{:?} drops", num_drops);
// - let dist = WeightedIndex::new(actions.iter().map(|item| item.1)).unwrap();
// - let kind = actions[dist.sample(&mut rng)].0;
// - let item = Item::new(kind, account_id);
// + for _i in 0..num_drops {
// + let actions = mode_drops(mode);
// - println!("{:?} dropped {:?}", account_id, item);
// + let dist = WeightedIndex::new(actions.iter().map(|item| item.1)).unwrap();
// + let kind = actions[dist.sample(&mut rng)].0;
// + let item = Item::new(kind, account_id);
// - return item_create(item, tx, account_id);
// + println!("{:?} dropped {:?}", account_id, item);
// + item_create(item, tx, account_id)?;V

View File

@ -473,10 +473,10 @@ impl Instance {
Ok(self) Ok(self)
} }
pub fn vbox_accept(mut self, account: Uuid, group: usize, index: usize) -> Result<Instance, Error> { pub fn vbox_accept(mut self, account: Uuid, group: usize, index: usize, 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)?; .vbox_accept(group, index, construct_id)?;
Ok(self) Ok(self)
} }
@ -501,10 +501,10 @@ impl Instance {
Ok(self) Ok(self)
} }
pub fn vbox_unequip(mut self, account: Uuid, target: Item, construct_id: Uuid) -> Result<Instance, Error> { pub fn vbox_unequip(mut self, account: Uuid, target: Item, construct_id: Uuid, target_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_unequip(target, construct_id)?; .vbox_unequip(target, construct_id, target_construct_id)?;
Ok(self) Ok(self)
} }
} }

View File

@ -712,14 +712,14 @@ impl Item {
// Skills <- need to move effect mulltipliers into skills // Skills <- need to move effect mulltipliers into skills
Item::Amplify| Item::Amplify|
Item::AmplifyPlus | Item::AmplifyPlus |
Item::AmplifyPlusPlus => format!("Increase RedPower and BluePower by {:?}%. Lasts {:?}T.", Item::AmplifyPlusPlus => format!("Increase RedPower BluePower GreenPower by {:?}%. Lasts {:?}T.",
self.into_skill().unwrap().effect()[0].get_multiplier() - 100, self.into_skill().unwrap().effect()[0].get_multiplier() - 100,
self.into_skill().unwrap().effect()[0].get_duration()), self.into_skill().unwrap().effect()[0].get_duration()),
Item::Banish| Item::Banish|
Item::BanishPlus | Item::BanishPlus |
Item::BanishPlusPlus => format!("Banish target for {:?}T. Item::BanishPlusPlus => format!("Banish target for {:?}T.
Deal blue damage and red damage equal to {:?}% target red and blue life. Deal {:?}% target RedLife and BlueLife as red and blue damage respectively.
Banished constructs are immune to all skills and effects.", Banished constructs are immune to all skills and effects.",
self.into_skill().unwrap().effect()[0].get_duration(), self.into_skill().unwrap().effect()[0].get_duration(),
self.into_skill().unwrap().multiplier()), self.into_skill().unwrap().multiplier()),
@ -773,11 +773,12 @@ impl Item {
Item::Absorb| Item::Absorb|
Item::AbsorbPlus | Item::AbsorbPlus |
Item::AbsorbPlusPlus => format!( Item::AbsorbPlusPlus => format!(
"Gain Absorb for {:?}T. When attacked with Absorb you gain Absorption. "Gain Absorb for {:?}T. Taking damage replaces Absorb with Absorption.
Absorption increases RedPower and BluePower based on Damage taken. Absorption increases RedPower and BluePower based on damage taken.
Absorption lasts {:?}T.", Absorption lasts {:?}T. Recharges BlueLife based on {:?}% BluePower.",
self.into_skill().unwrap().effect()[0].get_duration(), self.into_skill().unwrap().effect()[0].get_duration(),
self.into_skill().unwrap().effect()[0].get_skill().unwrap().effect()[0].get_duration()), self.into_skill().unwrap().effect()[0].get_skill().unwrap().effect()[0].get_duration(),
self.into_skill().unwrap().multiplier()),
Item::Haste| Item::Haste|
Item::HastePlus | Item::HastePlus |
@ -943,6 +944,11 @@ impl Item {
} }
} }
// !!!!!!
// IF YOU CHANGE A COMBO
// BE SURE TO EDIT BUTTONS.JSX TOO
// !!!!!!
fn combo(&self) -> Vec<Item> { fn combo(&self) -> Vec<Item> {
match self { match self {
Item::Intercept => vec![Item::Buff, Item::Red, Item::Red], Item::Intercept => vec![Item::Buff, Item::Red, Item::Red],

View File

@ -20,8 +20,8 @@ pub const FREE_MTX: [MtxVariant; 2] = [
]; ];
pub const SHOP_LISTINGS: [Listing; 2] = [ pub const SHOP_LISTINGS: [Listing; 2] = [
Listing { variant: MtxVariant::Molecular, credits: 10 },
Listing { variant: MtxVariant::Invader, credits: 10 }, Listing { variant: MtxVariant::Invader, credits: 10 },
Listing { variant: MtxVariant::Smile, credits: 10 },
]; ];
const NEW_IMAGE_COST: i64 = 1; const NEW_IMAGE_COST: i64 = 1;
@ -43,9 +43,9 @@ pub struct Listing {
#[derive(Debug,Copy,Clone,PartialEq,Serialize,Deserialize)] #[derive(Debug,Copy,Clone,PartialEq,Serialize,Deserialize)]
pub enum MtxVariant { pub enum MtxVariant {
Rename, Rename,
Molecular,
Invader, Invader,
Shapes, Shapes,
Smile,
} }
impl MtxVariant { impl MtxVariant {
@ -61,8 +61,8 @@ impl TryFrom<String> for MtxVariant {
fn try_from(v: String) -> Result<MtxVariant, Error> { fn try_from(v: String) -> Result<MtxVariant, Error> {
match v.as_ref() { match v.as_ref() {
"Rename" => Ok(MtxVariant::Rename), "Rename" => Ok(MtxVariant::Rename),
"Molecular" => Ok(MtxVariant::Molecular),
"Invader" => Ok(MtxVariant::Invader), "Invader" => Ok(MtxVariant::Invader),
"Smile" => Ok(MtxVariant::Smile),
"Shapes" => Ok(MtxVariant::Shapes), "Shapes" => Ok(MtxVariant::Shapes),
_ => Err(format_err!("mtx variant not found variant={:?}", v)), _ => Err(format_err!("mtx variant not found variant={:?}", v)),
} }
@ -81,7 +81,6 @@ impl Mtx {
match variant { match variant {
_ => Mtx { id: Uuid::new_v4(), account, variant }, _ => Mtx { id: Uuid::new_v4(), account, variant },
// MtxVariant::Invader => Mtx { id: Uuid::new_v4(), account, variant: self }, // MtxVariant::Invader => Mtx { id: Uuid::new_v4(), account, variant: self },
// MtxVariant::Molecular => Mtx { id: Uuid::new_v4(), account, variant: self },
} }
} }
@ -158,8 +157,8 @@ pub fn apply(tx: &mut Transaction, account: &Account, variant: MtxVariant, const
match mtx.variant { match mtx.variant {
MtxVariant::Invader => img::invader_write(construct.img)?, MtxVariant::Invader => img::invader_write(construct.img)?,
MtxVariant::Molecular => img::molecular_write(construct.img)?,
MtxVariant::Shapes => img::shapes_write(construct.img)?, MtxVariant::Shapes => img::shapes_write(construct.img)?,
MtxVariant::Smile => img::smile(construct.img)?,
_ => construct.img, _ => construct.img,
}; };
@ -179,8 +178,8 @@ pub fn account_apply(tx: &mut Transaction, account: &Account, variant: MtxVarian
match mtx.variant { match mtx.variant {
MtxVariant::Invader => img::invader_write(account.img)?, MtxVariant::Invader => img::invader_write(account.img)?,
MtxVariant::Molecular => img::molecular_write(account.img)?,
MtxVariant::Shapes => img::shapes_write(account.img)?, MtxVariant::Shapes => img::shapes_write(account.img)?,
MtxVariant::Smile => img::smile(account.img)?,
_ => account.img, _ => account.img,
}; };

View File

@ -256,8 +256,12 @@ impl Player {
Ok(self) Ok(self)
} }
pub fn vbox_accept(&mut self, group: usize, index: usize) -> Result<&mut Player, Error> { pub fn vbox_accept(&mut self, group: usize, index: usize, construct_id: Option<Uuid>) -> Result<&mut Player, Error> {
self.vbox.accept(group, index)?; self.vbox.accept(group, index, construct_id)?;
if construct_id.is_some() {
let equip_index = self.vbox.bound.len() - 1;
self.vbox_apply(equip_index, construct_id.expect("no construct"))?;
}
Ok(self) Ok(self)
} }
@ -320,8 +324,8 @@ impl Player {
Ok(self) Ok(self)
} }
pub fn vbox_unequip(&mut self, target: Item, construct_id: 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 { if self.vbox.bound.len() >= 9 && !target_construct_id.is_some() {
return Err(err_msg("too many items bound")); return Err(err_msg("too many items bound"));
} }
@ -354,7 +358,12 @@ impl Player {
} }
self.vbox.bound.push(target); self.vbox.bound.push(target);
self.vbox.bound.sort_unstable();
if target_construct_id.is_some() {
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)
} }

View File

@ -1,6 +1,5 @@
use std::time::{Instant}; use std::time::{Instant};
use std::thread::{spawn, sleep}; use std::thread::{spawn};
use std::time;
use std::str; use std::str;
@ -65,6 +64,7 @@ pub enum RpcMessage {
QueueRequested(()), QueueRequested(()),
QueueJoined(()), QueueJoined(()),
QueueCancelled(()), QueueCancelled(()),
QueueFound(()),
InviteRequested(()), InviteRequested(()),
Invite(String), Invite(String),
@ -112,10 +112,12 @@ pub enum RpcRequest {
InstanceChat { instance_id: Uuid, index: usize }, InstanceChat { instance_id: Uuid, index: usize },
VboxAccept { instance_id: Uuid, group: usize, index: usize }, VboxAccept { instance_id: Uuid, group: usize, index: usize },
VboxAcceptEquip { instance_id: Uuid, group: usize, index: usize, construct_id: Uuid },
VboxDiscard { instance_id: Uuid }, VboxDiscard { instance_id: Uuid },
VboxCombine { instance_id: Uuid, indices: Vec<usize> }, VboxCombine { instance_id: Uuid, indices: Vec<usize> },
VboxApply { instance_id: Uuid, construct_id: Uuid, index: usize }, 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 },
VboxReclaim { instance_id: Uuid, index: usize }, VboxReclaim { instance_id: Uuid, index: usize },
} }
@ -234,7 +236,10 @@ impl Connection {
Ok(instance_abandon(&mut tx, account, instance_id)?), Ok(instance_abandon(&mut tx, account, instance_id)?),
RpcRequest::VboxAccept { instance_id, group, index } => RpcRequest::VboxAccept { instance_id, group, index } =>
Ok(RpcMessage::InstanceState(vbox_accept(&mut tx, account, instance_id, group, index)?)), Ok(RpcMessage::InstanceState(vbox_accept(&mut tx, account, instance_id, group, index, None)?)),
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)?)),
@ -249,7 +254,10 @@ impl Connection {
Ok(RpcMessage::InstanceState(vbox_reclaim(&mut tx, account, instance_id, index)?)), Ok(RpcMessage::InstanceState(vbox_reclaim(&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)?)), Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, account, instance_id, construct_id, target, None)?)),
RpcRequest::VboxUnequipApply { instance_id, construct_id, target, target_construct_id } =>
Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, account, instance_id, construct_id, target, Some(target_construct_id))?)),
RpcRequest::MtxConstructSpawn {} => RpcRequest::MtxConstructSpawn {} =>
Ok(RpcMessage::ConstructSpawn(mtx::new_construct(&mut tx, account)?)), Ok(RpcMessage::ConstructSpawn(mtx::new_construct(&mut tx, account)?)),

View File

@ -89,10 +89,15 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut
if source.affected(Effect::Haste) { if source.affected(Effect::Haste) {
match skill { match skill {
Skill::Attack |
Skill::Slay | Skill::Slay |
Skill::SlayPlus |
Skill::SlayPlusPlus |
Skill::Chaos | Skill::Chaos |
Skill::Strike=> { Skill::ChaosPlus |
Skill::ChaosPlusPlus |
Skill::Strike |
Skill::StrikePlus |
Skill::StrikePlusPlus => {
let amount = source.speed().pct(Skill::HasteStrike.multiplier()); let amount = source.speed().pct(Skill::HasteStrike.multiplier());
target.deal_red_damage(Skill::HasteStrike, amount) target.deal_red_damage(Skill::HasteStrike, amount)
.into_iter() .into_iter()
@ -105,8 +110,14 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut
if source.affected(Effect::Hybrid) { if source.affected(Effect::Hybrid) {
match skill { match skill {
Skill::Blast| Skill::Blast|
Skill::BlastPlus |
Skill::BlastPlusPlus |
Skill::Chaos | Skill::Chaos |
Skill::Siphon=> { Skill::ChaosPlus |
Skill::ChaosPlusPlus |
Skill::Siphon |
Skill::SiphonPlus |
Skill::SiphonPlusPlus => {
let amount = source.green_power().pct(Skill::HybridBlast.multiplier()); let amount = source.green_power().pct(Skill::HybridBlast.multiplier());
target.deal_blue_damage(Skill::HybridBlast, amount) target.deal_blue_damage(Skill::HybridBlast, amount)
.into_iter() .into_iter()
@ -293,7 +304,7 @@ fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) ->
let mut target = game.construct_by_id(target.id).unwrap().clone(); let mut target = game.construct_by_id(target.id).unwrap().clone();
match event { match event {
Event::Damage { amount, skill, mitigation: _, colour: c } => { Event::Damage { amount, skill, mitigation, colour: c } => {
if target.affected(Effect::Electric) && !skill.is_tick() { if target.affected(Effect::Electric) && !skill.is_tick() {
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
.find(|e| e.effect == Effect::Electric).unwrap().clone(); .find(|e| e.effect == Effect::Electric).unwrap().clone();
@ -320,7 +331,7 @@ fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) ->
.find(|e| e.effect == Effect::Absorb).unwrap().clone(); .find(|e| e.effect == Effect::Absorb).unwrap().clone();
match meta { match meta {
Some(EffectMeta::Skill(s)) => { Some(EffectMeta::Skill(s)) => {
resolutions = absorption(&mut source, &mut target, resolutions, skill, amount, s); resolutions = absorption(&mut source, &mut target, resolutions, skill, amount + mitigation, s);
}, },
_ => panic!("no absorb skill"), _ => panic!("no absorb skill"),
}; };
@ -834,6 +845,10 @@ impl Skill {
Skill::HasteStrike => 60, Skill::HasteStrike => 60,
Skill::Absorb=> 95,
Skill::AbsorbPlus => 120,
Skill::AbsorbPlusPlus => 155,
Skill::Intercept=> 80, Skill::Intercept=> 80,
Skill::InterceptPlus => 110, Skill::InterceptPlus => 110,
Skill::InterceptPlusPlus => 150, Skill::InterceptPlusPlus => 150,
@ -911,11 +926,11 @@ impl Skill {
Skill::HastePlusPlus => vec![ConstructEffect {effect: Effect::Haste, duration: 5, Skill::HastePlusPlus => vec![ConstructEffect {effect: Effect::Haste, duration: 5,
meta: Some(EffectMeta::Multiplier(225)), tick: None }], meta: Some(EffectMeta::Multiplier(225)), tick: None }],
Skill::Absorb => vec![ConstructEffect {effect: Effect::Absorb, duration: 2, Skill::Absorb => vec![ConstructEffect {effect: Effect::Absorb, duration: 1,
meta: Some(EffectMeta::Skill(Skill::Absorption)), tick: None}], meta: Some(EffectMeta::Skill(Skill::Absorption)), tick: None}],
Skill::AbsorbPlus => vec![ConstructEffect {effect: Effect::Absorb, duration: 3, Skill::AbsorbPlus => vec![ConstructEffect {effect: Effect::Absorb, duration: 1,
meta: Some(EffectMeta::Skill(Skill::AbsorptionPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::AbsorptionPlus)), tick: None}],
Skill::AbsorbPlusPlus => vec![ConstructEffect {effect: Effect::Absorb, duration: 4, Skill::AbsorbPlusPlus => vec![ConstructEffect {effect: Effect::Absorb, duration: 1,
meta: Some(EffectMeta::Skill(Skill::AbsorptionPlusPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::AbsorptionPlusPlus)), tick: None}],
Skill::Absorption => vec![ConstructEffect {effect: Effect::Absorption, duration: 3, meta: None, tick: None}], Skill::Absorption => vec![ConstructEffect {effect: Effect::Absorption, duration: 3, meta: None, tick: None}],
@ -993,9 +1008,9 @@ impl Skill {
meta: Some(EffectMeta::Skill(Skill::BashPlusPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::BashPlusPlus)), tick: None}],
Skill::Stun => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}], Skill::Stun => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
Skill::Intercept => vec![ConstructEffect {effect: Effect::Intercept, duration: 2, meta: None, tick: None}], Skill::Intercept => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}],
Skill::InterceptPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 3, meta: None, tick: None}], Skill::InterceptPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}],
Skill::InterceptPlusPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 4, meta: None, tick: None}], Skill::InterceptPlusPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 1, meta: None, tick: None}],
Skill::Triage => vec![ConstructEffect {effect: Effect::Triage, duration: 2, Skill::Triage => vec![ConstructEffect {effect: Effect::Triage, duration: 2,
meta: Some(EffectMeta::Skill(Skill::TriageTick)), tick: None}], meta: Some(EffectMeta::Skill(Skill::TriageTick)), tick: None}],
@ -1062,9 +1077,11 @@ impl Skill {
Skill::Invert=> Some(2), Skill::Invert=> Some(2),
Skill::InvertPlus => Some(2), Skill::InvertPlus => Some(2),
Skill::InvertPlusPlus => Some(2), Skill::InvertPlusPlus => Some(2),
Skill::Decay=> Some(1), // dot
Skill::DecayPlus => Some(1), Skill::Decay=> None, // dot
Skill::DecayPlusPlus => Some(1), Skill::DecayPlus => None,
Skill::DecayPlusPlus => None,
Skill::Siphon| Skill::Siphon|
Skill::SiphonPlus | Skill::SiphonPlus |
Skill::SiphonPlusPlus => None, Skill::SiphonPlusPlus => None,
@ -1091,7 +1108,7 @@ impl Skill {
Skill::Banish | Skill::Banish |
Skill::BanishPlus | Skill::BanishPlus |
Skill::BanishPlusPlus => Some(2), Skill::BanishPlusPlus => Some(1),
Skill::Haste=> Some(1), Skill::Haste=> Some(1),
Skill::HastePlus => Some(1), Skill::HastePlus => Some(1),
@ -1121,9 +1138,9 @@ impl Skill {
Skill::SustainPlus | Skill::SustainPlus |
Skill::SustainPlusPlus => Some(1), Skill::SustainPlusPlus => Some(1),
Skill::Intercept=> Some(2), Skill::Intercept=> Some(1),
Skill::InterceptPlus => Some(2), Skill::InterceptPlus => Some(1),
Skill::InterceptPlusPlus => Some(2), Skill::InterceptPlusPlus => Some(1),
Skill::Electrify | Skill::Electrify |
Skill::ElectrifyPlus | Skill::ElectrifyPlus |
@ -1394,7 +1411,7 @@ fn sustain(source: &mut Construct, target: &mut Construct, mut results: Resoluti
} }
_ => { _ => {
warn!("no recharge event found {:?}", e); warn!("no recharge event found {:?}", e);
return results; EventStages::NoStages
} }
}; };
@ -1416,7 +1433,7 @@ fn intercept(source: &mut Construct, target: &mut Construct, mut results: Resolu
} }
_ => { _ => {
warn!("no recharge event found {:?}", e); warn!("no recharge event found {:?}", e);
return results; EventStages::NoStages
} }
}; };
results.push(Resolution::new(source, target).event(e).stages(stages)); results.push(Resolution::new(source, target).event(e).stages(stages));
@ -1675,6 +1692,19 @@ fn ruin(source: &mut Construct, target: &mut Construct, mut results: Resolutions
fn absorb(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn absorb(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
let blue_amount = source.blue_power().pct(skill.multiplier());
let e = target.recharge(skill, 0, blue_amount);
let stages = match e {
Event::Recharge { red, blue, skill: _ } => {
if red > 0 || blue > 0 { EventStages::PostOnly }
else { EventStages::NoStages }
}
_ => {
warn!("no recharge event found {:?}", e);
EventStages::NoStages
}
};
results.push(Resolution::new(source, target).event(e).stages(stages));
return results;; return results;;
} }
@ -1721,7 +1751,7 @@ fn reflect(source: &mut Construct, target: &mut Construct, mut results: Resoluti
} }
_ => { _ => {
warn!("no recharge event found {:?}", e); warn!("no recharge event found {:?}", e);
return results; EventStages::NoStages
} }
}; };
results.push(Resolution::new(source, target).event(e).stages(stages)); results.push(Resolution::new(source, target).event(e).stages(stages));
@ -1739,7 +1769,7 @@ fn recharge(source: &mut Construct, target: &mut Construct, mut results: Resolut
} }
_ => { _ => {
warn!("no recharge event found {:?}", e); warn!("no recharge event found {:?}", e);
return results; EventStages::NoStages
} }
}; };
results.push(Resolution::new(source, target).event(e).stages(stages)); results.push(Resolution::new(source, target).event(e).stages(stages));
@ -1794,13 +1824,19 @@ fn link(source: &mut Construct, target: &mut Construct, mut results: Resolutions
None => 0 None => 0
}; };
target.deal_blue_damage(skill, swap) let link_events = target.deal_blue_damage(skill, swap);
.into_iter() for e in link_events {
.for_each(|e| results.push(Resolution::new(source, target).event(e))); match e {
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => {
source.deal_green_damage(skill, swap) results.push(Resolution::new(source, target).event(e));
.into_iter() let heal = source.deal_green_damage(skill, amount);
.for_each(|e| results.push(Resolution::new(source, source).event(e).stages(EventStages::PostOnly))); for h in heal {
results.push(Resolution::new(source, source).event(h).stages(EventStages::PostOnly));
};
},
_ => results.push(Resolution::new(source, target).event(e)),
}
}
results.push(Resolution::new(source, source) results.push(Resolution::new(source, source)
.event(source.add_effect(skill, skill.effect()[0])).stages(EventStages::PostOnly)); .event(source.add_effect(skill, skill.effect()[0])).stages(EventStages::PostOnly));
@ -2076,6 +2112,7 @@ mod tests {
x.blue_power.force(256); x.blue_power.force(256);
x.green_power.force(220); x.green_power.force(220);
x.green_life.force(1024); x.green_life.force(1024);
y.blue_life.force(0);
x.green_life.reduce(512); x.green_life.reduce(512);
let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]); let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]);

View File

@ -99,8 +99,8 @@ impl Vbox {
self self
} }
pub fn accept(&mut self, i: usize, j: usize) -> Result<&mut Vbox, Error> { pub fn accept(&mut self, i: usize, j: usize, construct_id: Option<Uuid>) -> Result<&mut Vbox, Error> {
if self.bound.len() >= 9 { if self.bound.len() >= 9 && !construct_id.is_some() {
return Err(err_msg("too many items bound")); return Err(err_msg("too many items bound"));
} }
@ -130,7 +130,7 @@ impl Vbox {
pub fn bot_accept(&mut self, i: usize) -> Result<&mut Vbox, Error> { pub fn bot_accept(&mut self, i: usize) -> Result<&mut Vbox, Error> {
let buy_index = self.free[i].iter().position(|item| item.is_some()); let buy_index = self.free[i].iter().position(|item| item.is_some());
self.accept(i, buy_index.expect("no valid buys")) self.accept(i, buy_index.expect("no valid buys"), None)
} }
pub fn reclaim(&mut self, i: usize) -> Result<&mut Vbox, Error> { pub fn reclaim(&mut self, i: usize) -> Result<&mut Vbox, Error> {
@ -181,9 +181,9 @@ pub fn vbox_discard(tx: &mut Transaction, account: &Account, instance_id: Uuid)
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) -> Result<Instance, Error> { pub fn vbox_accept(tx: &mut Transaction, account: &Account, instance_id: Uuid, group: usize, index: usize, 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)?; .vbox_accept(account.id, group, index, construct_id)?;
return instance_update(tx, instance); return instance_update(tx, instance);
} }
@ -205,9 +205,9 @@ pub fn vbox_apply(tx: &mut Transaction, account: &Account, instance_id: Uuid, co
return instance_update(tx, instance); return instance_update(tx, instance);
} }
pub fn vbox_unequip(tx: &mut Transaction, account: &Account, instance_id: Uuid, construct_id: Uuid, target: Item) -> Result<Instance, Error> { pub fn vbox_unequip(tx: &mut Transaction, account: &Account, instance_id: Uuid, construct_id: Uuid, target: Item, target_construct_id: Option<Uuid>) -> Result<Instance, Error> {
let instance = instance_get(tx, instance_id)? let instance = instance_get(tx, instance_id)?
.vbox_unequip(account.id, target, construct_id)?; .vbox_unequip(account.id, target, construct_id, target_construct_id)?;
return instance_update(tx, instance); return instance_update(tx, instance);
} }

View File

@ -112,6 +112,10 @@ impl Warden {
pair.0.tx.send(msg.clone())?; pair.0.tx.send(msg.clone())?;
pair.1.tx.send(msg)?; pair.1.tx.send(msg)?;
// send msgs for browser notifications
pair.0.tx.send(RpcMessage::QueueFound(()))?;
pair.1.tx.send(RpcMessage::QueueFound(()))?;
Ok(()) Ok(())
} }
} }