diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a23a1a..ee730dfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,53 @@ All notable changes to this project will be documented in this file. 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 # Added - Step by step tutorial diff --git a/VERSION b/VERSION index 081af9a1..afa2b351 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.7.1 \ No newline at end of file +1.8.0 \ No newline at end of file diff --git a/WORKLOG.md b/WORKLOG.md index 174a7073..7ecaec13 100644 --- a/WORKLOG.md +++ b/WORKLOG.md @@ -3,22 +3,33 @@ *PRODUCTION* -* vbox phase skill list navigator (overlay maybe?) * can't reset password without knowing password =\ +* ws gzip encoding -* mobile styles * mobile info page -* fix info page for tablet layout * Invert recharge ## SOON -* equip from shop (buy and equip without putting in your inventory) for bases -* move item from one construct to another +* change cooldowns to delay & recharge + - delay is cooldown before skill can first be used + - recharge is cooldown after using skill + - every x speed reduces delay of skills + +* combo rework + - reduce number of items for creating t2/t3 items from 3 -> 2 + - add lost complexity by adding skill spec items + - 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 * essential + * audio * treats * susbcriber gold name in instance @@ -66,7 +77,6 @@ $$$ * Highlight (dota) colour * fx colours + styles -* ??? (PROBS NOT) drag and drop buy / equip / unequip items ??? * modules * troll life -> dmg * prince of peace diff --git a/acp/package.json b/acp/package.json index 48ed247e..69013a0e 100644 --- a/acp/package.json +++ b/acp/package.json @@ -1,6 +1,6 @@ { "name": "mnml-client", - "version": "1.7.1", + "version": "1.8.0", "description": "", "main": "index.js", "scripts": { diff --git a/client/assets/icons/mnml.png b/client/assets/icons/mnml.png index 068e30c1..d6e38299 100644 Binary files a/client/assets/icons/mnml.png and b/client/assets/icons/mnml.png differ diff --git a/client/assets/rotate.svg b/client/assets/rotate.svg new file mode 100644 index 00000000..b48ee399 --- /dev/null +++ b/client/assets/rotate.svg @@ -0,0 +1,2593 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/client/assets/styles/account.less b/client/assets/styles/account.less index adb879fe..ff102b69 100644 --- a/client/assets/styles/account.less +++ b/client/assets/styles/account.less @@ -4,7 +4,7 @@ grid-template-columns: 1fr 1fr 1fr 1fr; div { - padding-right: 2em; + padding-right: 1em; } button { diff --git a/client/assets/styles/colours.less b/client/assets/styles/colours.less index 13da2d0e..f3ea9020 100644 --- a/client/assets/styles/colours.less +++ b/client/assets/styles/colours.less @@ -169,3 +169,15 @@ button { color: @green; } } + +@keyframes rb-text { + 0% { + color: @red; + } + 50% { + color: @white; + } + 100% { + color: @blue; + } +} diff --git a/client/assets/styles/controls.less b/client/assets/styles/controls.less index 54bcb375..69e89c1b 100644 --- a/client/assets/styles/controls.less +++ b/client/assets/styles/controls.less @@ -96,6 +96,20 @@ aside { border-color: forestgreen; } } + + // all green + // &:enabled { + // background: forestgreen; + // color: black; + // border-color: forestgreen; + + // &:hover { + // color: forestgreen; + // border-color: forestgreen; + // background: 0; + // } + // } + } .team-page-ctrl { diff --git a/client/assets/styles/game.less b/client/assets/styles/game.less index 65ad6b25..d6bd7d30 100644 --- a/client/assets/styles/game.less +++ b/client/assets/styles/game.less @@ -10,15 +10,22 @@ // "opponent" // "target " // "player "; + + .skill-description { + font-size: 75%; + } } -.game .team { +.game .team, .faceoff .team { display: grid; grid-template-columns: repeat(3, 1fr); /* stops overflow */ min-height: 0; min-width: 0; + + // timer is 0.25 w/ 1em margin + width: calc(90% - 1.25em); } .player { @@ -28,7 +35,6 @@ position: absolute; bottom: 0; height: 50%; - width: 90%; } .opponent { @@ -37,13 +43,15 @@ position: absolute; top: 0; height: 35%; - width: 90%; margin-top: 0.5em; .game-construct { align-items: flex-start; - grid-template-columns: 1fr 2fr; + grid-template-rows: 2fr min-content; grid-template-rows: 1fr; + grid-template-areas: + "right" + "left"; .right { height: 100%; @@ -78,12 +86,16 @@ justify-items: center; grid-template-columns: 1fr; - grid-template-rows: min-content 1fr; + grid-template-rows: 1fr 2fr; + grid-template-areas: + "left" + "right"; .left { width: 100%; display: grid; + grid-area: left; grid-template-columns: 1fr 2fr; grid-template-areas: "skills effects"; @@ -91,6 +103,7 @@ .right { display: grid; + grid-area: right; grid-template-rows: minmax(min-content, 1fr) min-content min-content; grid-template-areas: "avatar" @@ -120,6 +133,7 @@ } .skills { + z-index: 2; button { width: 100%; height: 2em; @@ -131,6 +145,7 @@ } .effects { + z-index: 2; grid-area: effects; white-space: nowrap; width: 100%; @@ -198,7 +213,7 @@ position: absolute; top: 35%; height: 15%; - width: 90%; + width: calc(90% - 1.25em); } .resolving-skill { @@ -238,6 +253,9 @@ overflow: hidden; max-height: 100%; max-width: 100%; + + display: flex; + flex-flow: column; } .combat-anim svg { @@ -257,14 +275,6 @@ color: #a52a2a; } -.red-damage text { - fill: #a52a2a; -} - -.red-damage .stats { - /*border-top: 1px solid #a52a2a;*/ -} - .game-construct.blue-damage { color: #3050f8; opacity: 1; @@ -274,13 +284,6 @@ color: #3050f8; } -.blue-damage text { - fill: #3050f8; -} - -.blue-damage .stats { -} - .game-construct.green-damage { color: #1FF01F; opacity: 1; @@ -290,31 +293,8 @@ color: #1FF01F; } -.green-damage text { - fill: #1FF01F; -} - -.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-construct.rb-damage { + animation: rb-text 1s cubic-bezier(0.5, 0, 0.5, 1) 0s infinite; } .game .img, .faceoff .img { @@ -345,104 +325,4 @@ overflow: hidden; max-height: 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); - } - } - } diff --git a/client/assets/styles/instance.less b/client/assets/styles/instance.less index 0f05fc8b..fdf9f4cd 100644 --- a/client/assets/styles/instance.less +++ b/client/assets/styles/instance.less @@ -9,10 +9,6 @@ grid-template-areas: "vbox info" "constructs constructs"; - - hr { - grid-area: rule; - } } @media (max-width: 1920px) { @@ -88,65 +84,6 @@ 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 { 0% { color: palegoldenrod; @@ -195,6 +132,7 @@ grid-area: skills; padding: 0 0.5em; margin-bottom: 0.75em; + z-index: 2; display: grid; grid-template-columns: repeat(3, 1fr); @@ -203,12 +141,16 @@ button { height: 3em; } + label { + display: flex; + } } .specs { grid-area: specs; padding: 0 0.5em; margin-bottom: 0.75em; + z-index: 2; display: grid; grid-template-columns: repeat(3, 1fr); @@ -225,9 +167,13 @@ } figcaption { - font-size: 75%; + // font-size: 75%; line-height: initial; } + + label { + display: flex; + } } .stats { @@ -239,7 +185,7 @@ text-align: center; figcaption { - font-size: 75%; + // font-size: 75%; } // give speed some space diff --git a/client/assets/styles/instance.mobile.less b/client/assets/styles/instance.mobile.less index 9fec777b..925b487f 100644 --- a/client/assets/styles/instance.mobile.less +++ b/client/assets/styles/instance.mobile.less @@ -17,9 +17,7 @@ @media (max-width: 800px) { .instance { - overflow-y: scroll; - font-size: 8pt; - display: grid; + font-size: 6pt; grid-template-columns: 1fr; grid-template-rows: min-content 1fr; grid-template-areas: @@ -30,141 +28,4 @@ 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; - } } \ No newline at end of file diff --git a/client/assets/styles/menu.less b/client/assets/styles/menu.less index 672e928e..7282cc94 100644 --- a/client/assets/styles/menu.less +++ b/client/assets/styles/menu.less @@ -142,6 +142,19 @@ section { 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; - } -} \ No newline at end of file diff --git a/client/assets/styles/styles.less b/client/assets/styles/styles.less index 86ea5d38..99329d24 100644 --- a/client/assets/styles/styles.less +++ b/client/assets/styles/styles.less @@ -309,6 +309,7 @@ li { background-size: contain; background-repeat: no-repeat; background-position: center; + z-index: 0; // pointer-events: none; } @@ -316,4 +317,29 @@ li { width: 1px; height: 1px; padding: 0px; -} \ No newline at end of file +} + +#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; + } +} diff --git a/client/assets/styles/styles.mobile.less b/client/assets/styles/styles.mobile.less index be259075..c0e8798b 100644 --- a/client/assets/styles/styles.mobile.less +++ b/client/assets/styles/styles.mobile.less @@ -1,115 +1,187 @@ -@media (max-width: 800px) { +@media (max-width: 1000px) { body { overflow-y: initial; } #mnml { - font-size: 12pt; - padding: 0; + font-size: 8pt; + padding: 0.25em; + + .instance { + grid-template-columns: 1fr; + grid-template-rows: min-content 1fr; + + grid-template-areas: + "vbox" + "constructs"; + + svg { + stroke-width: 1.25em; + } + } + + .game { + .team, #targeting, .resolving-skill { + width: calc(90% - 3em); + } + + .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; + } + } + + .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 { + button { + margin-bottom: 0.5em; + } + } + } +} + + +// portrait menu +@media (max-width: 600px) { + #mnml { grid-template-columns: 1fr; - grid-template-rows: 10fr 1fr; + grid-template-rows: 1fr; grid-template-areas: "main" - "footer"; - - height: 100vh; - max-height: initial; - min-height: initial; } - table td { - height: 2.5em; + section { + grid-template-columns: 1fr; + + .list { + grid-template-columns: 1fr 1fr; + + &.play { + grid-template-columns: 1fr; + } + } + } - nav { - display: none; - } + .account { + grid-template-columns: 1fr; - aside { - display: none; - } - - footer { - display: flex; - position: fixed; - bottom: 0; - width: 100%; - - button { - font-size: 90%; + div { + padding: 0; } } - #nav-btn, #instance-nav { - display: unset; - } - - #mnml.nav-visible nav { - padding: 0.5em; - margin: 0; - display: block; - grid-area: main; - } - - #mnml.nav-visible main { + .play-ctrl { 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; - - grid-template-columns: 1fr; - grid-template-areas: - "constructs" - "inventory" - "games"; - } - .menu { + height: auto; + display: block; + .options { display: grid; - grid-template-columns: 1fr; + grid-template-columns: repeat(2, 1fr); button:not(:last-child) { border: 2px solid #222; } button.logo { + grid-column-end: span 2; border: none; margin-right: 0; margin-top: 0.5em; background-position: center; } } + + .team { + grid-template-columns: 1fr; + + .construct { + height: 10em; + } + } + + .news { + padding: 0; + } } section { @@ -117,14 +189,4 @@ grid-template-columns: 1fr; } } - - .account { - div { - padding: 0; - } - } - - .play-p { - display: none; - } -} +} \ No newline at end of file diff --git a/client/assets/styles/vbox.less b/client/assets/styles/vbox.less index fe62f0a2..6b868716 100644 --- a/client/assets/styles/vbox.less +++ b/client/assets/styles/vbox.less @@ -22,10 +22,6 @@ grid-gap: 0.5em 1em; align-items: center; margin-bottom: 0.5em; - - button { - width: 100%; - } } .vbox-btn { @@ -73,6 +69,7 @@ button { height: 4em; margin: 0; + width: 100%; // text-transform: none; @@ -83,7 +80,7 @@ &.highlight { color: black; background: @white; - border: 1px solid @white; + // border: 1px solid @white; (this bangs around the vbox) // overwrite the classes on white svg elements svg { @@ -100,7 +97,7 @@ figure { svg { height: 2em; - stroke-width: 8px; + stroke-width: 0.5em; } 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; +} diff --git a/client/index.html b/client/index.html index 760d2f1e..f9a2f86c 100644 --- a/client/index.html +++ b/client/index.html @@ -15,6 +15,8 @@ + + diff --git a/client/manifest.webmanifest b/client/manifest.webmanifest index 3d2582a5..4703d584 100644 --- a/client/manifest.webmanifest +++ b/client/manifest.webmanifest @@ -1,7 +1,7 @@ { - "name": "mnml", - "description": "mnml pvp atbs", - "short_name": "mnml", + "name": "MNML", + "description": "abstract strategy", + "short_name": "MNML", "icons": [ { "src": "./assets/icons/mnml.png", @@ -15,7 +15,8 @@ } ], "start_url": "/index.html", - "display": "fullscreen", + "display": "standalone", + "orientation": "landscape", "theme_color": "#000000", "background_color": "#000000" } \ No newline at end of file diff --git a/client/package.json b/client/package.json index f446e92c..fbdfadd2 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "mnml-client", - "version": "1.7.1", + "version": "1.8.0", "description": "", "main": "index.js", "scripts": { diff --git a/client/src/actions.jsx b/client/src/actions.jsx index 4b9d11ac..e42d349c 100644 --- a/client/src/actions.jsx +++ b/client/src/actions.jsx @@ -36,12 +36,10 @@ export const setItemInfo = value => ({ type: 'SET_ITEM_INFO', value }); export const setItemUnequip = value => ({ type: 'SET_ITEM_UNEQUIP', value }); export const setMtxActive = value => ({ type: 'SET_MTX_ACTIVE', 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 setPlayer = value => ({ type: 'SET_PLAYER', value }); export const setReclaiming = value => ({ type: 'SET_RECLAIMING', value }); export const setShowLog = value => ({ type: 'SET_SHOW_LOG', value }); -export const setShowNav = value => ({ type: 'SET_SHOW_NAV', value }); export const setSkip = value => ({ type: 'SET_SKIP', value }); export const setShop = value => ({ type: 'SET_SHOP', value }); export const setSubscription = value => ({ type: 'SET_SUBSCRIPTION', value }); diff --git a/client/src/animations.utils.jsx b/client/src/animations.utils.jsx index 8e2818c7..f749e43f 100644 --- a/client/src/animations.utils.jsx +++ b/client/src/animations.utils.jsx @@ -160,7 +160,7 @@ function getText(resolution) { if (type === 'Recharge') { 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 (blue > 0) return { text: [`+${blue}B`, ''], css: 'blue-damage' }; return nullText; diff --git a/client/src/components/anims/siphon.tick.jsx b/client/src/components/anims/siphon.tick.jsx index 43155c07..1f7f4d47 100644 --- a/client/src/components/anims/siphon.tick.jsx +++ b/client/src/components/anims/siphon.tick.jsx @@ -38,7 +38,7 @@ class SiphonTick extends Component { render() { return ( 'red-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', Siphon: () => 'blue-green-border', // Stun - Link: () => 'blue-greenborder', + Link: () => 'blue-green-border', Bash: () => 'red-border', Sleep: () => 'green-border', Ruin: () => 'blue-border', @@ -31,9 +32,9 @@ module.exports = { // This will need to be edited if we change server recipes Restrict: () => 'red-border', Purge: () => 'green-border', Silence: () => 'blue-border', - Curse: () => 'red-green-border', + Invert: () => 'red-green-border', Decay: () => 'blue-green-border', - Invert: () => 'red-blue-border', + Curse: () => 'red-blue-border', // // Lifes Upgrades // LifeGG: () => 'green-border', diff --git a/client/src/components/demo.jsx b/client/src/components/demo.jsx index 45dcf71d..494015ef 100644 --- a/client/src/components/demo.jsx +++ b/client/src/components/demo.jsx @@ -84,7 +84,7 @@ function Demo(args) { function inventoryElement() { return ( -
+

VBOX PHASE {shapes.Red()} {shapes.Green()} {shapes.Blue()} @@ -117,7 +117,7 @@ function Demo(args) { : 'empty gray'; const constructEl = c => ( -
+

{c.name}

diff --git a/client/src/components/game.construct.jsx b/client/src/components/game.construct.jsx index 75a1677d..b448ecec 100644 --- a/client/src/components/game.construct.jsx +++ b/client/src/components/game.construct.jsx @@ -153,7 +153,7 @@ class GameConstruct extends Component { } const effects = construct.effects.length ? construct.effects.map(c => -
hoverInfo(e, c)} onMouseOut={e => hoverInfo(e, null)} diff --git a/client/src/components/instance.component.jsx b/client/src/components/instance.component.jsx index b5e98674..23b0148a 100644 --- a/client/src/components/instance.component.jsx +++ b/client/src/components/instance.component.jsx @@ -1,9 +1,6 @@ -const { Component } = require('preact'); const preact = require('preact'); const { connect } = require('preact-redux'); -const Hammer = require('hammerjs'); - const Vbox = require('./vbox.component'); const InfoContainer = require('./info.container'); const InstanceConstructsContainer = require('./instance.constructs'); @@ -16,12 +13,10 @@ const addState = connect( const { instance, nav, - navInstance, } = state; return { instance, nav, - navInstance, }; }, @@ -30,10 +25,6 @@ const addState = connect( return dispatch(actions.setInfo(c)); } - function setNavInstance(c) { - return dispatch(actions.setNavInstance(c)); - } - function clearItems() { dispatch(actions.setCombiner([])); @@ -48,80 +39,38 @@ const addState = connect( return { setInfo, clearItems, - setNavInstance, }; } ); -class Instance extends Component { - shouldComponentUpdate(newProps) { - if (newProps.instance !== this.props.instance) return true; - return false; +function Instance(args) { + const { + instance, + clearItems, + } = args; + + if (!instance) return false; + + if (instance.phase !== 'InProgress') { + return ; } - render(args) { - const { - instance, - clearItems, - } = args; - - if (!instance) return false; - - if (instance.phase !== 'InProgress') { - return ; - } - - function instanceClick(e) { - e.stopPropagation(); - clearItems(); - } - - function onTouchMove(e) { - e.preventDefault(); - } - - return ( -
- - - -
- ); + function instanceClick(e) { + e.stopPropagation(); + clearItems(); } - componentDidMount() { - if (!this.h) this.bindSwipes(); + function onTouchMove(e) { + e.preventDefault(); } - 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; - } + return ( +
+ + + +
+ ); } module.exports = addState(Instance); diff --git a/client/src/components/instance.constructs.jsx b/client/src/components/instance.constructs.jsx index 1e010061..da843f8f 100644 --- a/client/src/components/instance.constructs.jsx +++ b/client/src/components/instance.constructs.jsx @@ -1,5 +1,6 @@ const { connect } = require('preact-redux'); const preact = require('preact'); + const range = require('lodash/range'); const buttons = require('./buttons'); @@ -19,10 +20,15 @@ const addState = connect( account, itemInfo, itemEquip, - navInstance, + itemUnequip, + vboxSelected, tutorial, } = state; + function sendVboxAcceptEquip(constructId) { + return ws.sendVboxAcceptEquip(instance.id, vboxSelected[0], vboxSelected[1], constructId); + } + function sendVboxApply(constructId, i) { return ws.sendVboxApply(instance.id, constructId, i); } @@ -31,15 +37,22 @@ const addState = connect( return ws.sendVboxUnequip(instance.id, constructId, item); } + function sendVboxUnequipApply(targetConstructId) { + return ws.sendVboxUnequipApply(instance.id, itemUnequip[0], itemUnequip[1], targetConstructId); + } + return { instance, player, account, + sendVboxAcceptEquip, + sendVboxUnequipApply, sendVboxApply, itemInfo, itemEquip, - navInstance, + itemUnequip, sendUnequip, + vboxSelected, tutorial, }; }, @@ -76,21 +89,23 @@ function Construct(props) { construct, iter, itemEquip, + itemUnequip, instance, - mobileVisible, player, + vboxSelected, tutorial, // Static Info itemInfo, // Function Calls sendVboxApply, + sendVboxAcceptEquip, + sendVboxUnequipApply, sendUnequip, setActiveConstruct, setItemUnequip, setItemEquip, setInfo, } = props; - const { vbox } = player; const duplicateSkill = construct.skills.length !== 0 && construct.skills.every(sk => { @@ -99,14 +114,16 @@ function Construct(props) { return sk.skill === vbox.bound[itemEquip]; }); const tutorialDisableEquip = tutorialShouldDisableEquip(tutorial, iter, instance, construct); - function onClick(e) { e.stopPropagation(); e.preventDefault(); 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); - return setActiveConstruct(construct); + setItemUnequip([]); + return true; } function hoverInfo(e, info) { @@ -153,15 +170,20 @@ function Construct(props) { const classes = `${equipping ? 'equipping' : ''} ${!skill ? 'empty' : ''} ${border()}`; return ( - + ); }); @@ -195,13 +217,18 @@ function Construct(props) { return ( - + ); }); @@ -220,10 +247,10 @@ function Construct(props) {
; }); - const classes = `instance-construct ${mobileVisible ? 'visible' : ''}`; + const classes = `instance-construct`; const avatarMouseOver = e => hoverInfo(e, `constructAvatar ${construct.name}`); return ( -
+
ev.preventDefault()} onDrop={onClick}>

hoverInfo(e, `constructName ${construct.name}`)}>{construct.name}

hoverInfo(e, 'constructSkills')} > @@ -242,11 +269,12 @@ function Construct(props) { class InstanceConstructs extends preact.Component { shouldComponentUpdate(newProps) { 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.navInstance !== this.props.navInstance) return true; // JSON or Array objects if (newProps.player !== this.props.player) return true; if (newProps.instance !== this.props.instance) return true; + if (newProps.vboxSelected !== this.props.vboxSelected) return true; return false; } @@ -254,16 +282,19 @@ class InstanceConstructs extends preact.Component { const { // Changing state variables itemEquip, + itemUnequip, instance, - navInstance, player, tutorial, + vboxSelected, // Static data itemInfo, // Function calls setInfo, setActiveConstruct, sendVboxApply, + sendVboxAcceptEquip, + sendVboxUnequipApply, setVboxHighlight, setItemUnequip, setItemEquip, @@ -274,25 +305,28 @@ class InstanceConstructs extends preact.Component { if (instance.phase === 'Lobby') return false; 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); return Construct({ iter: i, construct: player.constructs[i], itemEquip, + itemUnequip, instance, setItemUnequip, setItemEquip, player, sendVboxApply, + sendVboxAcceptEquip, + sendVboxUnequipApply, setInfo, setActiveConstruct, itemInfo, setVboxHighlight, sendUnequip, + vboxSelected, tutorial, - mobileVisible: navInstance === i + 1, }); }); diff --git a/client/src/components/mnml.jsx b/client/src/components/mnml.jsx index 60dad587..6b168c68 100644 --- a/client/src/components/mnml.jsx +++ b/client/src/components/mnml.jsx @@ -7,24 +7,28 @@ const Controls = require('./controls'); const Footer = require('./footer'); const addState = connect( - state => ({ showNav: state.showNav }) + ({ game, instance }) => ({ game, instance }) ); -class Mnml extends preact.Component { - shouldComponentUpdate(newProps) { - if (newProps.showNav !== this.props.showNav) return true; - return false; - } +function Mnml(args) { + const { + game, + instance, + } = args; - render(args) { - return ( -
-
- -
+ const rotateClass = (game || instance) && window.innerWidth < window.innerHeight + ? 'show' + : ''; + + return ( +
+
+ +
+
- ); - } +
+ ); } module.exports = addState(Mnml); diff --git a/client/src/components/player.box.jsx b/client/src/components/player.box.jsx index 6cace8a4..f4f0908f 100644 --- a/client/src/components/player.box.jsx +++ b/client/src/components/player.box.jsx @@ -60,10 +60,10 @@ function Scoreboard(args) { } = args; const scoreText = () => { - if (player.score === 'Zero' || player.score === 'Lose') return [â–«, â–«, â–«]; - if (player.score === 'One') return [â– , â–«, â–«]; - if (player.score === 'Two') return [â– , â– , â–«]; - if (player.score === 'Win') return [â– , â– , â– ]; + if (player.score === 'Zero' || player.score === 'Lose') return [, , ]; + if (player.score === 'One') return [, , ]; + if (player.score === 'Two') return [, , ]; + if (player.score === 'Win') return [, , ]; return ''; }; diff --git a/client/src/components/reshape.jsx b/client/src/components/reshape.jsx index d45bd4d4..68ca09b0 100644 --- a/client/src/components/reshape.jsx +++ b/client/src/components/reshape.jsx @@ -117,8 +117,6 @@ function Reshape(args) {
{shop.owned.map(useMtx)} -
-
{shop.available.map(availableMtx)}
diff --git a/client/src/components/targeting.arrows.jsx b/client/src/components/targeting.arrows.jsx index b615517b..d893e3bc 100644 --- a/client/src/components/targeting.arrows.jsx +++ b/client/src/components/targeting.arrows.jsx @@ -59,7 +59,7 @@ class TargetSvg extends Component { if (tutorialGame) { return (
-

Select your skills, click on targets and then hit ready.

+

Select your skills, click on targets and then hit READY.

); } diff --git a/client/src/components/vbox.component.jsx b/client/src/components/vbox.component.jsx index c2c1a51a..1f70236c 100644 --- a/client/src/components/vbox.component.jsx +++ b/client/src/components/vbox.component.jsx @@ -20,7 +20,6 @@ const addState = connect( vboxSelected, itemInfo, itemUnequip, - navInstance, tutorial, } = state; @@ -29,6 +28,7 @@ const addState = connect( } function sendVboxAccept(group, index) { + document.activeElement.blur(); return ws.sendVboxAccept(instance.id, group, index); } @@ -57,7 +57,6 @@ const addState = connect( itemInfo, itemUnequip, sendItemUnequip, - navInstance, tutorial, }; }, @@ -100,7 +99,6 @@ class Vbox extends preact.Component { if (newProps.combiner !== this.props.combiner) return true; if (newProps.itemUnequip !== this.props.itemUnequip) 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.vboxSelected !== this.props.vboxSelected) return true; if (newProps.player !== this.props.player) return true; @@ -116,7 +114,6 @@ class Vbox extends preact.Component { player, reclaiming, tutorial, - navInstance, vboxSelected, instance, @@ -180,7 +177,7 @@ class Vbox extends preact.Component { } function availableBtn(v, group, index) { - if (!v) return ; + if (!v) return ; const selected = vboxSelected[0] === group && vboxSelected[1] === index; // state not yet set in double click handler @@ -194,8 +191,6 @@ class Vbox extends preact.Component { e.stopPropagation(); setItemEquip(null); setCombiner([]); - - if (selected) return clearVboxSelected(); setInfo(vbox.free[group][index]); return setVboxSelected([group, index]); } @@ -217,28 +212,24 @@ class Vbox extends preact.Component { const classes = `${v.toLowerCase()} ${selected ? 'highlight' : ''} ${comboHighlight}`; - if (shapes[v]) { - return ( + const vboxObject = shapes[v] ? shapes[v]() : v; + return ( + ); } @@ -288,7 +279,7 @@ class Vbox extends preact.Component { const inventoryHighlight = vboxSelecting || itemUnequip.length; if (!v && v !== 0) { - return ; + return ; } const combinerItems = combiner.map(j => vbox.bound[j]); @@ -306,54 +297,55 @@ class Vbox extends preact.Component { } return false; }) ? 'combo-border' : ''; - function onClick(e) { + function onClick(type) { if (vboxSelecting) clearVboxSelected(); if (reclaiming) return sendVboxReclaim(i); + const combinerContainsIndex = combiner.indexOf(i) > -1; // 4 things selected - if (combiner.length > 2) { + if (combiner.length > 2 && !combinerContainsIndex) { setInfo(vbox.bound[i]); return combinerChange([i]); } // removing - const combinerIndex = combiner.indexOf(i); - if (combinerIndex > -1) { - return combinerChange(without(combiner, i)); + if (combinerContainsIndex) { + if (type === 'click') { + return combinerChange(without(combiner, i)); + } + return true; } - combiner.push(i); - - if (!comboHighlight) { + if (!comboHighlight && !combinerContainsIndex) { setInfo(vbox.bound[i]); return combinerChange([i]); } + combiner.push(i); return combinerChange(combiner); } const highlighted = combiner.indexOf(i) > -1; const border = buttons[removeTier(v)] ? buttons[removeTier(v)]() : ''; const classes = `${highlighted ? 'highlight' : border} ${comboHighlight}`; - if (shapes[v]) { - return ( + + const invObject = shapes[v] ? shapes[v]() : v; + + return ( + ); } @@ -408,7 +400,10 @@ class Vbox extends preact.Component {
e.stopPropagation()} - style={vboxSelecting || (itemUnequip.length) ? { cursor: 'pointer' } : null}> + style={vboxSelecting || (itemUnequip.length) ? { cursor: 'pointer' } : null} + onDragOver={ev => ev.preventDefault()} + onDrop={inventoryClick} + >

e.target.scrollIntoView(true)} @@ -439,7 +434,7 @@ class Vbox extends preact.Component { return setInfo(newInfo); } - const classes = `vbox ${navInstance === 0 ? 'visible' : ''}`; + const classes = `vbox`; return (
{vboxElement()} diff --git a/client/src/events.jsx b/client/src/events.jsx index 0cc1e4a0..0d9c88fa 100644 --- a/client/src/events.jsx +++ b/client/src/events.jsx @@ -12,6 +12,13 @@ const { tutorialVbox } = require('./tutorial.utils'); function registerEvents(store) { function notify(msg) { + if (window.Notification && window.Notification.permission === 'granted') { + const n = new Notification('MNML', { + body: msg, + tag: 'MNML', + }); + } + return infoToast(msg); } @@ -127,9 +134,13 @@ function registerEvents(store) { } function setAccount(account) { - if (account) { + if (account && process.env.NODE_ENV !== 'development') { LogRocket.init('yh0dy3/mnml'); LogRocket.identify(account.id, account); + + if (window.Notification) { + window.Notification.requestPermission(); + } } store.dispatch(actions.setAccount(account)); @@ -179,6 +190,7 @@ function registerEvents(store) { store.dispatch(actions.setItemEquip(null)); store.dispatch(actions.setItemUnequip([])); store.dispatch(actions.setVboxHighlight([])); + store.dispatch(actions.setVboxSelected([])); } function setAccountInstances(v) { @@ -212,7 +224,9 @@ function registerEvents(store) { if (v.phase === 'Finished') { 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)); } else if (v.time_control === 'Practice' && v.rounds.length === 1 && tutorial) { tutorialVbox(player, store, tutorial); diff --git a/client/src/reducers.jsx b/client/src/reducers.jsx index d2d06f43..96d8001c 100644 --- a/client/src/reducers.jsx +++ b/client/src/reducers.jsx @@ -45,8 +45,6 @@ module.exports = { itemUnequip: createReducer([], 'SET_ITEM_UNEQUIP'), mtxActive: createReducer(null, 'SET_MTX_ACTIVE'), nav: createReducer(null, 'SET_NAV'), - navInstance: createReducer(0, 'SET_NAV_INSTANCE'), - showNav: createReducer(null, 'SET_SHOW_NAV'), ping: createReducer(null, 'SET_PING'), player: createReducer(null, 'SET_PLAYER'), reclaiming: createReducer(false, 'SET_RECLAIMING'), diff --git a/client/src/socket.jsx b/client/src/socket.jsx index 85d81154..fbc6b296 100644 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -82,6 +82,11 @@ function createSocket(events) { 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) { send(['VboxApply', { instance_id: instanceId, construct_id: constructId, index }]); events.clearInstance(); @@ -92,6 +97,11 @@ function createSocket(events) { 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) { send(['VboxDiscard', { instance_id: instanceId }]); events.clearInstance(); @@ -99,7 +109,7 @@ function createSocket(events) { function sendVboxCombine(instanceId, indices) { send(['VboxCombine', { instance_id: instanceId, indices }]); - events.clearCombiner(); + events.clearInstance(); } function sendVboxReclaim(instanceId, index) { @@ -264,13 +274,14 @@ function createSocket(events) { Pong: onPong, Demo: onDemo, - QueueRequested: () => events.notify('pvp queue request received'), - QueueJoined: () => events.notify('you have joined the pvp queue'), - InviteRequested: () => events.notify('pvp invite request received'), + QueueRequested: () => events.notify('PVP queue request received.'), + QueueJoined: () => events.notify('You have joined the PVP queue.'), + QueueFound: () => events.notify('Your PVP game has started.'), + InviteRequested: () => events.notify('PVP invite request received.'), Invite: code => events.setInvite(code), InstanceChat: chat => events.setInstanceChat(chat), ChatWheel: wheel => events.setChatWheel(wheel), - Joining: () => events.notify('searching for instance...'), + // Joining: () => events.notify('Searching for instance...'), Processing: () => true, Error: errHandler, @@ -307,8 +318,11 @@ function createSocket(events) { return handlers[msgType](params); } + let attempts = 1; + // Connection opened function onOpen() { + attempts = 0; toast.info({ message: 'connected', position: 'topRight', @@ -327,12 +341,21 @@ function createSocket(events) { } 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); toast.warning({ message: 'disconnected', position: 'topRight', }); - return setTimeout(connect, 5000); + return setTimeout(connect, attempts * 1000); } function connect() { @@ -381,11 +404,13 @@ function createSocket(events) { sendInstanceChat, sendVboxAccept, + sendVboxAcceptEquip, sendVboxApply, sendVboxReclaim, sendVboxCombine, sendVboxDiscard, sendVboxUnequip, + sendVboxUnequipApply, sendItemInfo, diff --git a/client/src/tutorial.utils.jsx b/client/src/tutorial.utils.jsx index 1cdf0dfb..53268161 100644 --- a/client/src/tutorial.utils.jsx +++ b/client/src/tutorial.utils.jsx @@ -1,11 +1,10 @@ const preact = require('preact'); 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 (tutorial <= 2 || (tutorial > 2 && i > 0)) { - const mobileVisible = navInstance === i + 1; - const classes = `instance-construct ${mobileVisible ? 'visible' : ''}`; + const classes = `instance-construct`; return (
); } } @@ -200,13 +199,16 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) { } if (tutorial === 8) { + if (window.innerWidth < 1000) { + return exit(); + } + return (

Tutorial

You've completed the tutorial! Try to create more skill and spec combinations.

You can unequip skills and specs back into the inventory by double clicking.
Reclaim can be used to refund the cost of items in your inventory.

-

Click the EXIT TUTORIAL button to replace this section with more information.

); } @@ -214,10 +216,11 @@ function tutorialStage(tutorial, ws, clearTutorial, instance) { }; const classes = tutorial === 8 ? 'focus' : ''; + const text = tutorial === 8 ? 'Continue' : 'Close Tutorial' const exitTutorial = ; + onMouseDown={exit}> {text} ; return (
diff --git a/client/src/utils.jsx b/client/src/utils.jsx index ec49e659..8cbbc11a 100644 --- a/client/src/utils.jsx +++ b/client/src/utils.jsx @@ -264,7 +264,7 @@ function effectInfo(i) { } 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 '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}%`; diff --git a/etc/nginx/sites-available/mnml.gg.PRODUCTION.nginx.conf b/etc/nginx/sites-available/mnml.gg.PRODUCTION.nginx.conf index 82567688..0fca6361 100644 --- a/etc/nginx/sites-available/mnml.gg.PRODUCTION.nginx.conf +++ b/etc/nginx/sites-available/mnml.gg.PRODUCTION.nginx.conf @@ -15,6 +15,12 @@ map $http_upgrade $connection_upgrade { server { 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 / { root /var/lib/mnml/public/current; index index.html; diff --git a/etc/nginx/sites-available/mnml.gg.STAGING.SAMPLE.nginx.conf b/etc/nginx/sites-available/mnml.gg.STAGING.SAMPLE.nginx.conf index bed6a99e..2e75cf1e 100644 --- a/etc/nginx/sites-available/mnml.gg.STAGING.SAMPLE.nginx.conf +++ b/etc/nginx/sites-available/mnml.gg.STAGING.SAMPLE.nginx.conf @@ -15,8 +15,14 @@ map $http_upgrade $connection_upgrade { server { server_name sixtysix.pro; - auth_basic "who dis"; - auth_basic_user_file /etc/mnml/htpasswd.users; + 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; + + # auth_basic "who dis"; + # auth_basic_user_file /etc/mnml/htpasswd.users; location / { root /var/lib/mnml/public/current; diff --git a/ops/fetch.molecules.js b/ops/fetch.molecules.js deleted file mode 100644 index 5b9b7ba2..00000000 --- a/ops/fetch.molecules.js +++ /dev/null @@ -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); -// } -// } -// } - diff --git a/ops/fix.molecule.holes.js b/ops/fix.molecule.holes.js deleted file mode 100644 index f3c07006..00000000 --- a/ops/fix.molecule.holes.js +++ /dev/null @@ -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; - }) -} - diff --git a/ops/migrations/20191031203812_remove-molecular.js b/ops/migrations/20191031203812_remove-molecular.js new file mode 100644 index 00000000..194db918 --- /dev/null +++ b/ops/migrations/20191031203812_remove-molecular.js @@ -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 () => {}; \ No newline at end of file diff --git a/ops/molecules.js b/ops/molecules.js deleted file mode 100644 index 39c7d1b5..00000000 --- a/ops/molecules.js +++ /dev/null @@ -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); - }); - }); - }); -} - diff --git a/ops/package.json b/ops/package.json index 876bd4cf..3c4ae105 100755 --- a/ops/package.json +++ b/ops/package.json @@ -1,6 +1,6 @@ { "name": "mnml-ops", - "version": "1.7.1", + "version": "1.8.0", "description": "", "main": "index.js", "scripts": { diff --git a/server/Cargo.toml b/server/Cargo.toml index 40e26480..a29783c7 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mnml" -version = "1.7.1" +version = "1.8.0" authors = ["ntr "] [dependencies] diff --git a/server/src/account.rs b/server/src/account.rs index 22224568..8e8a1b6f 100644 --- a/server/src/account.rs +++ b/server/src/account.rs @@ -67,13 +67,13 @@ pub fn select(db: &Db, id: Uuid) -> Result { pub fn chat_wheel(_db: &Db, _id: Uuid) -> Result, Error> { return Ok(vec![ - "gl".to_string(), - "hf".to_string(), "gg".to_string(), - "thx".to_string(), - "nice".to_string(), + "glhf".to_string(), "hmm".to_string(), "ok".to_string(), + "rekt".to_string(), + "thx".to_string(), + "nice".to_string(), "...".to_string(), ]) } diff --git a/server/src/construct.rs b/server/src/construct.rs index 325e6d5e..55a971f1 100644 --- a/server/src/construct.rs +++ b/server/src/construct.rs @@ -220,11 +220,11 @@ impl Construct { account: id, img: Uuid::new_v4(), 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_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_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 }, // evasion: ConstructStat { base: 0, value: 0, max: 0, stat: Stat::Evasion }, skills: vec![], diff --git a/server/src/effect.rs b/server/src/effect.rs index c2c77392..9e3076bc 100644 --- a/server/src/effect.rs +++ b/server/src/effect.rs @@ -105,7 +105,7 @@ impl Effect { 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::Hybrid => vec![Stat::GreenPower], diff --git a/server/src/game.rs b/server/src/game.rs index 12a175b4..e484c286 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1229,7 +1229,7 @@ mod tests { assert!(game.player_by_id(x_player.id).unwrap().constructs[0].is_stunned() == false); // riposte 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] diff --git a/server/src/img.rs b/server/src/img.rs index c10d8e57..705d86cd 100644 --- a/server/src/img.rs +++ b/server/src/img.rs @@ -1,5 +1,4 @@ use std::f64; -use std::fs::copy; use std::fs::File; use std::io::prelude::*; use std::char::from_u32; @@ -9,25 +8,7 @@ use rand::prelude::*; use rand::distributions::{Normal, WeightedIndex}; use failure::Error; -use failure::err_msg; - -pub fn molecular_write(id: Uuid) -> Result { - 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?")) -} +// use failure::err_msg; pub fn invader_write(id: Uuid) -> Result { let mut rng = thread_rng(); @@ -257,6 +238,118 @@ fn _hieroglyph() -> String { return s; } + +pub fn smile(id: Uuid) -> Result { + 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, "")?; + + let left_eye_path = eyes_left[eye_left_dist.sample(&mut rng)].0; + + // left eye + write!(&mut svg, + "", + 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, + "", + eye_colour, stroke_width, right_eye_path)?; + + let mouth_path = mouths[mouth_dist.sample(&mut rng)].0; + // mouth + write!(&mut svg, + "", + mouth_colour, stroke_width, mouth_path)?; + + write!(&mut 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 { 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)] mod tests { use super::*; - // #[test] // fn invader_img_test() { // for i in 0..100 { @@ -283,107 +375,9 @@ mod tests { // 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 { -// +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 \ No newline at end of file diff --git a/server/src/instance.rs b/server/src/instance.rs index 2e65b9d6..5dc79652 100644 --- a/server/src/instance.rs +++ b/server/src/instance.rs @@ -473,10 +473,10 @@ impl Instance { Ok(self) } - pub fn vbox_accept(mut self, account: Uuid, group: usize, index: usize) -> Result { + pub fn vbox_accept(mut self, account: Uuid, group: usize, index: usize, construct_id: Option) -> Result { self.vbox_action_allowed(account)?; self.account_player(account)? - .vbox_accept(group, index)?; + .vbox_accept(group, index, construct_id)?; Ok(self) } @@ -501,10 +501,10 @@ impl Instance { Ok(self) } - pub fn vbox_unequip(mut self, account: Uuid, target: Item, construct_id: Uuid) -> Result { + pub fn vbox_unequip(mut self, account: Uuid, target: Item, construct_id: Uuid, target_construct_id: Option) -> Result { self.vbox_action_allowed(account)?; self.account_player(account)? - .vbox_unequip(target, construct_id)?; + .vbox_unequip(target, construct_id, target_construct_id)?; Ok(self) } } diff --git a/server/src/item.rs b/server/src/item.rs index 52b3bb8b..1f1405a2 100644 --- a/server/src/item.rs +++ b/server/src/item.rs @@ -620,70 +620,70 @@ impl Item { // Lifes Upgrades Item::LifeGG | Item::LifeGGPlus | - Item::LifeGGPlusPlus => format!("Increases construct GreenLife by {:?}. + Item::LifeGGPlusPlus => format!("Increases construct GreenLife by {:?}. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::LifeRR | Item::LifeRRPlus | - Item::LifeRRPlusPlus => format!("Increases construct RedLife by {:?}. + Item::LifeRRPlusPlus => format!("Increases construct RedLife by {:?}. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), - Item::LifeBB | - Item::LifeBBPlus | - Item::LifeBBPlusPlus => format!("Increases construct BlueLife by {:?}. + Item::LifeBB | + Item::LifeBBPlus | + Item::LifeBBPlusPlus => format!("Increases construct BlueLife by {:?}. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::LifeRG | Item::LifeRGPlus | - Item::LifeRGPlusPlus => format!("Increases construct RedLife and GreenLife by {:?}. + Item::LifeRGPlusPlus => format!("Increases construct RedLife and GreenLife by {:?}. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::LifeGB | Item::LifeGBPlus | - Item::LifeGBPlusPlus => format!("Increases construct GreenLife and BlueLife by {:?}. + Item::LifeGBPlusPlus => format!("Increases construct GreenLife and BlueLife by {:?}. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::LifeRB | Item::LifeRBPlus | - Item::LifeRBPlusPlus => format!("Increases construct RedLife and BlueLife by {:?}. + Item::LifeRBPlusPlus => format!("Increases construct RedLife and BlueLife by {:?}. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), // Power Upgrades - Item::PowerRR | - Item::PowerRRPlus | - Item::PowerRRPlusPlus => format!("Increases construct RedPower by {:?}%. + Item::PowerRR | + Item::PowerRRPlus | + Item::PowerRRPlusPlus => format!("Increases construct RedPower by {:?}%. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::PowerBB | Item::PowerBBPlus | - Item::PowerBBPlusPlus => format!("Increases construct BluePower by {:?}%. + Item::PowerBBPlusPlus => format!("Increases construct BluePower by {:?}%. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::PowerGG | Item::PowerGGPlus | - Item::PowerGGPlusPlus => format!("Increases construct GreenPower by {:?}%. + Item::PowerGGPlusPlus => format!("Increases construct GreenPower by {:?}%. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::PowerRG | Item::PowerRGPlus | - Item::PowerRGPlusPlus => format!("Increases construct GreenPower and RedPower by {:?}%. + Item::PowerRGPlusPlus => format!("Increases construct GreenPower and RedPower by {:?}%. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::PowerGB | Item::PowerGBPlus | - Item::PowerGBPlusPlus => format!("Increases construct GreenPower and BluePower by {:?}%. + Item::PowerGBPlusPlus => format!("Increases construct GreenPower and BluePower by {:?}%. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), Item::PowerRB | Item::PowerRBPlus | - Item::PowerRBPlusPlus => format!("Increases construct RedPower and BluePower by {:?}%. + Item::PowerRBPlusPlus => format!("Increases construct RedPower and BluePower by {:?}%. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), @@ -705,21 +705,21 @@ impl Item { Item::SpeedGGPlusPlus | Item::SpeedRGPlusPlus | Item::SpeedGBPlusPlus | - Item::SpeedRBPlusPlus => format!("Increases construct SpeedStat by {:?}%. + Item::SpeedRBPlusPlus => format!("Increases construct SpeedStat by {:?}%. If your team meets total colour thresholds the spec provides additional bonuses.", self.into_spec().unwrap().values().base()), // Skills <- need to move effect mulltipliers into skills Item::Amplify| 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_duration()), Item::Banish| Item::BanishPlus | 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.", self.into_skill().unwrap().effect()[0].get_duration(), self.into_skill().unwrap().multiplier()), @@ -773,11 +773,12 @@ impl Item { Item::Absorb| Item::AbsorbPlus | Item::AbsorbPlusPlus => format!( - "Gain Absorb for {:?}T. When attacked with Absorb you gain Absorption. - Absorption increases RedPower and BluePower based on Damage taken. - Absorption lasts {:?}T.", + "Gain Absorb for {:?}T. Taking damage replaces Absorb with Absorption. + Absorption increases RedPower and BluePower based on damage taken. + Absorption lasts {:?}T. Recharges BlueLife based on {:?}% BluePower.", 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::HastePlus | @@ -796,7 +797,7 @@ impl Item { Item::Hybrid| Item::HybridPlus | Item::HybridPlusPlus => format!( - "Hybrid increases GreenPower by {:?}%. + "Hybrid increases GreenPower by {:?}%. Blue based Attack skills will blast again dealing {:?}% GreenPower as blue damage. Lasts {:?}T.", self.into_skill().unwrap().effect()[0].get_multiplier() - 100, @@ -823,7 +824,7 @@ impl Item { Item::Purge| Item::PurgePlus | Item::PurgePlusPlus => format!( - "Remove buffs from target construct. + "Remove buffs from target construct. Applies purge disabling target green skills for {:?}T.", self.into_skill().unwrap().effect()[0].get_duration()), @@ -836,7 +837,7 @@ impl Item { Item::Reflect| Item::ReflectPlus | Item::ReflectPlusPlus => format!( - "Reflect incoming blue skills to source. Lasts {:?}T. + "Reflect incoming blue skills to source. Lasts {:?}T. Recharges target BlueLife based on {:?}% BluePower.", self.into_skill().unwrap().effect()[0].get_duration(), self.into_skill().unwrap().multiplier()), @@ -943,6 +944,11 @@ impl Item { } } + // !!!!!! + // IF YOU CHANGE A COMBO + // BE SURE TO EDIT BUTTONS.JSX TOO + // !!!!!! + fn combo(&self) -> Vec { match self { Item::Intercept => vec![Item::Buff, Item::Red, Item::Red], diff --git a/server/src/mtx.rs b/server/src/mtx.rs index 239e1fe7..b47daa5f 100644 --- a/server/src/mtx.rs +++ b/server/src/mtx.rs @@ -20,8 +20,8 @@ pub const FREE_MTX: [MtxVariant; 2] = [ ]; pub const SHOP_LISTINGS: [Listing; 2] = [ - Listing { variant: MtxVariant::Molecular, credits: 10 }, Listing { variant: MtxVariant::Invader, credits: 10 }, + Listing { variant: MtxVariant::Smile, credits: 10 }, ]; const NEW_IMAGE_COST: i64 = 1; @@ -43,9 +43,9 @@ pub struct Listing { #[derive(Debug,Copy,Clone,PartialEq,Serialize,Deserialize)] pub enum MtxVariant { Rename, - Molecular, Invader, Shapes, + Smile, } impl MtxVariant { @@ -61,8 +61,8 @@ impl TryFrom for MtxVariant { fn try_from(v: String) -> Result { match v.as_ref() { "Rename" => Ok(MtxVariant::Rename), - "Molecular" => Ok(MtxVariant::Molecular), "Invader" => Ok(MtxVariant::Invader), + "Smile" => Ok(MtxVariant::Smile), "Shapes" => Ok(MtxVariant::Shapes), _ => Err(format_err!("mtx variant not found variant={:?}", v)), } @@ -81,7 +81,6 @@ impl Mtx { match variant { _ => Mtx { id: Uuid::new_v4(), account, variant }, // 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 { MtxVariant::Invader => img::invader_write(construct.img)?, - MtxVariant::Molecular => img::molecular_write(construct.img)?, MtxVariant::Shapes => img::shapes_write(construct.img)?, + MtxVariant::Smile => img::smile(construct.img)?, _ => construct.img, }; @@ -179,8 +178,8 @@ pub fn account_apply(tx: &mut Transaction, account: &Account, variant: MtxVarian match mtx.variant { MtxVariant::Invader => img::invader_write(account.img)?, - MtxVariant::Molecular => img::molecular_write(account.img)?, MtxVariant::Shapes => img::shapes_write(account.img)?, + MtxVariant::Smile => img::smile(account.img)?, _ => account.img, }; diff --git a/server/src/player.rs b/server/src/player.rs index 994d7f4c..0e0baf56 100644 --- a/server/src/player.rs +++ b/server/src/player.rs @@ -256,8 +256,12 @@ impl Player { Ok(self) } - pub fn vbox_accept(&mut self, group: usize, index: usize) -> Result<&mut Player, Error> { - self.vbox.accept(group, index)?; + pub fn vbox_accept(&mut self, group: usize, index: usize, construct_id: Option) -> Result<&mut Player, Error> { + 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) } @@ -320,8 +324,8 @@ impl Player { Ok(self) } - pub fn vbox_unequip(&mut self, target: Item, construct_id: Uuid) -> Result<&mut Player, Error> { - if self.vbox.bound.len() >= 9 { + pub fn vbox_unequip(&mut self, target: Item, construct_id: Uuid, target_construct_id: Option) -> Result<&mut Player, Error> { + if self.vbox.bound.len() >= 9 && !target_construct_id.is_some() { return Err(err_msg("too many items bound")); } @@ -354,7 +358,12 @@ impl Player { } 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) } diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 54602215..e0db439a 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -1,6 +1,5 @@ use std::time::{Instant}; -use std::thread::{spawn, sleep}; -use std::time; +use std::thread::{spawn}; use std::str; @@ -65,6 +64,7 @@ pub enum RpcMessage { QueueRequested(()), QueueJoined(()), QueueCancelled(()), + QueueFound(()), InviteRequested(()), Invite(String), @@ -112,10 +112,12 @@ pub enum RpcRequest { InstanceChat { instance_id: Uuid, 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 }, VboxCombine { instance_id: Uuid, indices: Vec }, VboxApply { instance_id: Uuid, construct_id: Uuid, index: usize }, 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 }, } @@ -234,7 +236,10 @@ impl Connection { Ok(instance_abandon(&mut tx, account, instance_id)?), 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 } => 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)?)), 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 {} => Ok(RpcMessage::ConstructSpawn(mtx::new_construct(&mut tx, account)?)), diff --git a/server/src/skill.rs b/server/src/skill.rs index 5884198a..855f8954 100644 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -89,10 +89,15 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut if source.affected(Effect::Haste) { match skill { - Skill::Attack | - Skill::Slay| - Skill::Chaos| - Skill::Strike=> { + Skill::Slay | + Skill::SlayPlus | + Skill::SlayPlusPlus | + Skill::Chaos | + Skill::ChaosPlus | + Skill::ChaosPlusPlus | + Skill::Strike | + Skill::StrikePlus | + Skill::StrikePlusPlus => { let amount = source.speed().pct(Skill::HasteStrike.multiplier()); target.deal_red_damage(Skill::HasteStrike, amount) .into_iter() @@ -105,8 +110,14 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut if source.affected(Effect::Hybrid) { match skill { Skill::Blast| - Skill::Chaos| - Skill::Siphon=> { + Skill::BlastPlus | + Skill::BlastPlusPlus | + Skill::Chaos | + Skill::ChaosPlus | + Skill::ChaosPlusPlus | + Skill::Siphon | + Skill::SiphonPlus | + Skill::SiphonPlusPlus => { let amount = source.green_power().pct(Skill::HybridBlast.multiplier()); target.deal_blue_damage(Skill::HybridBlast, amount) .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(); match event { - Event::Damage { amount, skill, mitigation: _, colour: c } => { + Event::Damage { amount, skill, mitigation, colour: c } => { if target.affected(Effect::Electric) && !skill.is_tick() { let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() .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(); match meta { 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"), }; @@ -833,6 +844,10 @@ impl Skill { Skill::HybridBlast => 50, Skill::HasteStrike => 60, + + Skill::Absorb=> 95, + Skill::AbsorbPlus => 120, + Skill::AbsorbPlusPlus => 155, Skill::Intercept=> 80, Skill::InterceptPlus => 110, @@ -911,11 +926,11 @@ impl Skill { Skill::HastePlusPlus => vec![ConstructEffect {effect: Effect::Haste, duration: 5, 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}], - 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}], - 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}], 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}], 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::InterceptPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 3, meta: None, tick: None}], - Skill::InterceptPlusPlus => vec![ConstructEffect {effect: Effect::Intercept, duration: 4, 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: 1, 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, meta: Some(EffectMeta::Skill(Skill::TriageTick)), tick: None}], @@ -1062,9 +1077,11 @@ impl Skill { Skill::Invert=> Some(2), Skill::InvertPlus => Some(2), Skill::InvertPlusPlus => Some(2), - Skill::Decay=> Some(1), // dot - Skill::DecayPlus => Some(1), - Skill::DecayPlusPlus => Some(1), + + Skill::Decay=> None, // dot + Skill::DecayPlus => None, + Skill::DecayPlusPlus => None, + Skill::Siphon| Skill::SiphonPlus | Skill::SiphonPlusPlus => None, @@ -1091,7 +1108,7 @@ impl Skill { Skill::Banish | Skill::BanishPlus | - Skill::BanishPlusPlus => Some(2), + Skill::BanishPlusPlus => Some(1), Skill::Haste=> Some(1), Skill::HastePlus => Some(1), @@ -1121,9 +1138,9 @@ impl Skill { Skill::SustainPlus | Skill::SustainPlusPlus => Some(1), - Skill::Intercept=> Some(2), - Skill::InterceptPlus => Some(2), - Skill::InterceptPlusPlus => Some(2), + Skill::Intercept=> Some(1), + Skill::InterceptPlus => Some(1), + Skill::InterceptPlusPlus => Some(1), Skill::Electrify | Skill::ElectrifyPlus | @@ -1394,7 +1411,7 @@ fn sustain(source: &mut Construct, target: &mut Construct, mut results: Resoluti } _ => { 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); - return results; + EventStages::NoStages } }; 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 { 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;; } @@ -1721,7 +1751,7 @@ fn reflect(source: &mut Construct, target: &mut Construct, mut results: Resoluti } _ => { warn!("no recharge event found {:?}", e); - return results; + EventStages::NoStages } }; 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); - return results; + EventStages::NoStages } }; 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 }; - target.deal_blue_damage(skill, swap) - .into_iter() - .for_each(|e| results.push(Resolution::new(source, target).event(e))); - - source.deal_green_damage(skill, swap) - .into_iter() - .for_each(|e| results.push(Resolution::new(source, source).event(e).stages(EventStages::PostOnly))); + let link_events = target.deal_blue_damage(skill, swap); + for e in link_events { + match e { + Event::Damage { amount, mitigation: _, colour: _, skill: _ } => { + results.push(Resolution::new(source, target).event(e)); + let heal = source.deal_green_damage(skill, amount); + 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) .event(source.add_effect(skill, skill.effect()[0])).stages(EventStages::PostOnly)); @@ -2076,6 +2112,7 @@ mod tests { x.blue_power.force(256); x.green_power.force(220); x.green_life.force(1024); + y.blue_life.force(0); x.green_life.reduce(512); let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]); diff --git a/server/src/vbox.rs b/server/src/vbox.rs index 162f48de..3488b14d 100644 --- a/server/src/vbox.rs +++ b/server/src/vbox.rs @@ -99,8 +99,8 @@ impl Vbox { self } - pub fn accept(&mut self, i: usize, j: usize) -> Result<&mut Vbox, Error> { - if self.bound.len() >= 9 { + pub fn accept(&mut self, i: usize, j: usize, construct_id: Option) -> Result<&mut Vbox, Error> { + if self.bound.len() >= 9 && !construct_id.is_some() { 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> { 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> { @@ -181,9 +181,9 @@ pub fn vbox_discard(tx: &mut Transaction, account: &Account, instance_id: Uuid) return instance_update(tx, instance); } -pub fn vbox_accept(tx: &mut Transaction, account: &Account, instance_id: Uuid, group: usize, index: usize) -> Result { +pub fn vbox_accept(tx: &mut Transaction, account: &Account, instance_id: Uuid, group: usize, index: usize, construct_id: Option) -> Result { 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); } @@ -205,9 +205,9 @@ pub fn vbox_apply(tx: &mut Transaction, account: &Account, instance_id: Uuid, co return instance_update(tx, instance); } -pub fn vbox_unequip(tx: &mut Transaction, account: &Account, instance_id: Uuid, construct_id: Uuid, target: Item) -> Result { +pub fn vbox_unequip(tx: &mut Transaction, account: &Account, instance_id: Uuid, construct_id: Uuid, target: Item, target_construct_id: Option) -> Result { 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); } diff --git a/server/src/warden.rs b/server/src/warden.rs index 38779346..ff4b2760 100644 --- a/server/src/warden.rs +++ b/server/src/warden.rs @@ -112,6 +112,10 @@ impl Warden { pair.0.tx.send(msg.clone())?; pair.1.tx.send(msg)?; + // send msgs for browser notifications + pair.0.tx.send(RpcMessage::QueueFound(()))?; + pair.1.tx.send(RpcMessage::QueueFound(()))?; + Ok(()) } }