diff --git a/VERSION b/VERSION index 867e5243..589268e6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.0 \ No newline at end of file +1.3.0 \ No newline at end of file diff --git a/WORKLOG.md b/WORKLOG.md index 999f1afa..5a757c5f 100644 --- a/WORKLOG.md +++ b/WORKLOG.md @@ -10,9 +10,9 @@ * treats * constructs jiggle when clicked * background colour changes depending on time of day - -* bug fixes - * pvp 1st round doesn't resolve until warden timer completes + * hit animation wobble + * combat text scale + translate + * susbcriber gold name in instance * bot game grind * stress test @@ -22,11 +22,8 @@ * make our own toasts / msg pane * send account_instances on players update -* convert PlusPlus to ++ or rename * add speed to descriptions -* add components to description e.g. Strike (red circle red circle attack) * clear skill (if currently targetted) -* increase power to speed up early rounds * only login / logout / register http diff --git a/acp/package.json b/acp/package.json index a13e342e..a0aeb290 100644 --- a/acp/package.json +++ b/acp/package.json @@ -1,6 +1,6 @@ { "name": "mnml-client", - "version": "1.2.0", + "version": "1.3.0", "description": "", "main": "index.js", "scripts": { diff --git a/bin/deploy.sh b/bin/deploy.sh index f84b7d97..dcf11a59 100755 --- a/bin/deploy.sh +++ b/bin/deploy.sh @@ -6,7 +6,6 @@ MNML_PATH=$(realpath "$DIR/../") VERSION=$(<"$MNML_PATH/VERSION") SERVER_BIN_DIR="/usr/local/mnml/bin" - CLIENT_DIST_DIR="/var/lib/mnml/client" CLIENT_PUBLIC_DIR="/var/lib/mnml/public/current" diff --git a/client/assets/styles/account.less b/client/assets/styles/account.less index d6f43a30..74ef9e3c 100644 --- a/client/assets/styles/account.less +++ b/client/assets/styles/account.less @@ -1,9 +1,6 @@ @import 'colours.less'; .account { - margin-top: 2em; - grid-area: bottom; - display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; grid-gap: 0 1em; diff --git a/client/assets/styles/controls.less b/client/assets/styles/controls.less index d036debc..4484d720 100644 --- a/client/assets/styles/controls.less +++ b/client/assets/styles/controls.less @@ -8,10 +8,17 @@ aside { "timer controls"; grid-template-columns: min-content 1fr; - grid-template-rows: 1fr 1fr 1fr; - grid-gap: 0.5em 0; + grid-template-rows: 1fr; - padding: 1em 1em 1em 0; + padding-left: 1em; + + .controls { + grid-area: controls; + + display: grid; + grid-auto-rows: 1fr; + grid-gap: 0.5em 0; + } // fix chrome being inconsistent table { diff --git a/client/assets/styles/instance.less b/client/assets/styles/instance.less index 44da08bf..ee7a1898 100644 --- a/client/assets/styles/instance.less +++ b/client/assets/styles/instance.less @@ -1,9 +1,9 @@ @import 'colours.less'; .instance { - overflow-x: hidden; + overflow: hidden; display: grid; - grid-template-columns: 2fr minmax(min-content, 1fr); + grid-template-columns: 1fr minmax(min-content, 1fr); grid-template-rows: min-content 1fr; grid-template-areas: @@ -63,10 +63,16 @@ .instance .info figure { display: inline; height: 0.5em; + + svg { + margin-right: 0.5em; + } } .instance .info figcaption { font-size: 1em; + display: inline-block; + vertical-align: middle; } diff --git a/client/assets/styles/menu.less b/client/assets/styles/menu.less index fb919124..041f0263 100644 --- a/client/assets/styles/menu.less +++ b/client/assets/styles/menu.less @@ -4,16 +4,16 @@ height: 100%; display: grid; - grid-template-rows: minmax(min-content, 2fr) 1fr; + grid-template-rows: min-content 1fr 2fr; grid-template-columns: 1fr; grid-template-areas: + "hdr" "top" "bottom"; .top { grid-area: top; - padding: 0 0 0.5em 2em; border-bottom: 0.1em solid #222; box-sizing: border-box; } @@ -24,9 +24,10 @@ .team { display: grid; - grid-area: top; + grid-area: bottom; grid-template-columns: repeat(auto-fill, minmax(min-content, 33%)); max-height: 100%; + margin-top: 1em; .team-select:not(:nth-child(3n)) { margin-right: 0.5em; @@ -61,9 +62,6 @@ } .inventory { - margin-top: 2em; - grid-area: bottom; - display: grid; grid-template-columns: 1fr 1fr; @@ -71,6 +69,10 @@ margin-bottom: 0.5em; } + .news { + padding-right: 1em; + } + .list { letter-spacing: 0.25em; text-transform: uppercase; @@ -87,4 +89,21 @@ flex-flow: column; } } + + .options { + grid-area: hdr; + + button { + width: 25%; + border-top: 0; + border: 1px solid #222; + &:not(:last-child) { + border-right: 0; + } + + &:last-child { + float: right; + } + } + } } diff --git a/client/assets/styles/styles.less b/client/assets/styles/styles.less index 30b6d8e1..f64f48fa 100644 --- a/client/assets/styles/styles.less +++ b/client/assets/styles/styles.less @@ -67,6 +67,7 @@ hr { color: #222; margin: 1.5em 0; width: 100%; + border-top: 1px solid #222; } figure { @@ -84,16 +85,17 @@ dl { #mnml { display: grid; - grid-template-columns: minmax(min-content, 1fr) 8fr 1fr; - grid-template-rows: min-content 1fr min-content; + grid-template-columns: 9fr 1fr; + grid-template-rows: min-content min-content 1fr; grid-template-areas: - "hd hd ctrl" - "nav main ctrl" - "nav main ctrl"; + "hdr ctrl" + "main ctrl" + "main ctrl"; + + padding: 0.5em 1em; } main { - padding: 1em; grid-area: main; } @@ -198,12 +200,6 @@ button[disabled] { */ .welcome { - .highlight { - color: black; - background: @white; - border: 1px solid @white; - } - .login { width: 50%; display: flex; @@ -212,12 +208,8 @@ button[disabled] { } .options { - width: 50%; display: flex; - flex-flow: row; - button { - flex: 1; - } + width: 50%; } h2 { @@ -262,4 +254,48 @@ figure.gray { .mobile-title { display: none; +} + +header { + .options { + font-size: 150%; + } + + button { + height: 2em; + } +} + +.options { + button { + &.highlight { + color: @white; + box-shadow: inset 0px 5px 0px 0px @white; + border: 0; + &:first-child { + border-left: 1px solid #444; + } + + &:last-child { + border-right: 1px solid #444; + } + + } + + border: 1px solid #444; + flex: 1; + } +} + +nav { + display: none; +} + +ul { + margin-bottom: 1em; + list-style: inside; +} + +li { + margin-bottom: 0.5em; } \ No newline at end of file diff --git a/client/package.json b/client/package.json index 14bc2149..21e2be2a 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "mnml-client", - "version": "1.2.0", + "version": "1.3.0", "description": "", "main": "index.js", "scripts": { diff --git a/client/src/animations.utils.jsx b/client/src/animations.utils.jsx index c563e597..feda087b 100644 --- a/client/src/animations.utils.jsx +++ b/client/src/animations.utils.jsx @@ -1,4 +1,3 @@ -const { removeTier } = require('./utils'); const { TIMES } = require('./constants'); function none() { @@ -60,10 +59,8 @@ function getObjects(resolution, stages, game, account) { ? null : createSourceAnim(); - const skill = removeTier(event.skill); - const animTarget = { - skill, + skill: event.skill, constructId: targetting(), player: playerTeamIds.includes(resolution.target.id), direction, diff --git a/client/src/components/account.page.jsx b/client/src/components/account.page.jsx deleted file mode 100644 index 021bb58b..00000000 --- a/client/src/components/account.page.jsx +++ /dev/null @@ -1,33 +0,0 @@ -const { connect } = require('preact-redux'); -const preact = require('preact'); - -const Team = require('./team'); -const AccountManagement = require('./account.management'); - -const addState = connect( - function receiveState(state) { - const { - ws, - account, - } = state; - - return { - account, - }; - }, -); - -function Account(args) { - const { - account, - } = args; - - return ( -
- - -
- ); -} - -module.exports = addState(Account); diff --git a/client/src/components/account.management.jsx b/client/src/components/account.top.jsx similarity index 98% rename from client/src/components/account.management.jsx rename to client/src/components/account.top.jsx index b2799138..813e6054 100644 --- a/client/src/components/account.management.jsx +++ b/client/src/components/account.top.jsx @@ -151,19 +151,9 @@ class AccountStatus extends Component { } return ( -
+
-

{account.name}

-
-
-
spawn new construct
- -
-
- - + {subInfo()}
@@ -216,7 +206,16 @@ class AccountStatus extends Component {
- {subInfo()} +
+
+
spawn new construct
+ +
+
+ +
); diff --git a/client/src/components/animations.jsx b/client/src/components/animations.jsx index 9e1a4e80..ff1c9eaf 100644 --- a/client/src/components/animations.jsx +++ b/client/src/components/animations.jsx @@ -40,6 +40,8 @@ const Triage = require('./anims/triage'); const TriageTick = require('./anims/triage.tick'); const actions = require('../actions'); +const { removeTier } = require('../utils'); + const addState = connect( function receiveState(state) { @@ -63,12 +65,12 @@ class ConstructAnimation extends Component { direction, constructId, } = animTarget; - + const animSkill = removeTier(skill); if (!constructId.includes(construct.id)) return false; // find target animation - const chooseAnim = (skill) => { - switch (skill) { + const chooseAnim = (animSkill) => { + switch (animSkill) { // Attack base case 'Attack': return ; case 'Blast': return ; @@ -123,7 +125,7 @@ class ConstructAnimation extends Component { }; }; - const anim = chooseAnim(skill); + const anim = chooseAnim(animSkill); if (!anim) return false; return ( diff --git a/client/src/components/collection.jsx b/client/src/components/collection.jsx new file mode 100644 index 00000000..c6441803 --- /dev/null +++ b/client/src/components/collection.jsx @@ -0,0 +1,89 @@ +const preact = require('preact'); +const { connect } = require('preact-redux'); + +const actions = require('./../actions'); +const { COLOURS } = require('./../utils'); +const { ConstructAvatar } = require('./construct'); + +const addState = connect( + function receiveState(state) { + const { ws, constructs, teamPage, teamSelect } = state; + + function sendConstructSpawn(name) { + return ws.sendMtxConstructSpawn(name); + } + + return { + constructs, + teamPage, + teamSelect, + }; + }, + function receiveDispatch(dispatch) { + function setTeam(constructIds) { + dispatch(actions.setTeamSelect(constructIds)); + } + + return { + setTeam, + }; + } +); + +function Collection(args) { + const { + constructs, + teamPage, + teamSelect, + setTeam, + } = args; + + if (!constructs) return
; + + // redux limitation + suggested workaround + // so much for dumb components + function selectConstruct(id) { + // remove + const i = teamSelect.findIndex(sid => sid === id); + if (i > -1) { + teamSelect[i] = null; + return setTeam(teamSelect); + } + + // window insert + const insert = teamSelect.findIndex(j => j === null); + if (insert === -1) return setTeam([id, null, null]); + teamSelect[insert] = id; + return setTeam(teamSelect); + } + console.log(constructs.length); + const dispConstructs = constructs.length >= ((teamPage + 1) * 6) + ? constructs.slice(teamPage * 6, (teamPage + 1) * 6) + : constructs.slice(teamPage * 6, constructs.length); + + const constructPanels = dispConstructs.map(construct => { + const colour = teamSelect.indexOf(construct.id); + const selected = colour > -1; + + const borderColour = selected ? COLOURS[colour] : '#000000'; + + return ( +
selectConstruct(construct.id)} > + +

{construct.name}

+
+ ); + }); + + return ( +
+ {constructPanels} +
+ ); +} + +module.exports = addState(Collection); diff --git a/client/src/components/game.ctrl.jsx b/client/src/components/game.ctrl.jsx index a27e4e06..4bf517d6 100644 --- a/client/src/components/game.ctrl.jsx +++ b/client/src/components/game.ctrl.jsx @@ -125,9 +125,11 @@ function Controls(args) { return ( ); } diff --git a/client/src/components/header.jsx b/client/src/components/header.jsx index 5562502b..7c776ff8 100644 --- a/client/src/components/header.jsx +++ b/client/src/components/header.jsx @@ -1,40 +1,96 @@ -// eslint-disable-next-line -const preact = require('preact'); const { connect } = require('preact-redux'); +const preact = require('preact'); -const { saw } = require('./shapes'); -const actions = require('./../actions'); - -function pingColour(ping) { - if (ping < 100) return 'forestgreen'; - if (ping < 200) return 'yellow'; - return 'red'; -} +const actions = require('../actions'); const addState = connect( - ({ account, ping, showNav }) => { - return { account, ping }; + function receiveState(state) { + const { + ws, + account, + nav, + } = state; + + function sendInstanceState(instance) { + return ws.sendInstanceState(instance.id); + } + + function sendAccountStates() { + ws.sendEmailState(); + ws.sendSubscriptionState(); + } + + return { + account, + nav, + + sendInstanceState, + sendAccountStates, + }; }, + function receiveDispatch(dispatch) { + function setNav(place) { + dispatch(actions.setGame(null)); + dispatch(actions.setInstance(null)); + dispatch(actions.setCombiner([])); + dispatch(actions.setReclaiming(false)); + dispatch(actions.setActiveSkill(null)); + dispatch(actions.setActiveConstruct(null)); + dispatch(actions.setInfo(null)); + dispatch(actions.setItemEquip(null)); + dispatch(actions.setItemUnequip([])); + dispatch(actions.setVboxHighlight([])); + + return dispatch(actions.setNav(place)); + } + + return { + setNav, + }; + } ); -function renderHeader(args) { - const { account, ping } = args; +function Header(args) { + const { + account, + nav, - const accountStatus = account - ? (
-

{account.name}

- {saw(pingColour(ping))} -
{ping}ms
-
) - : ''; + sendAccountStates, + setNav, + } = args; + + if (!account) return false; + + function navTo(p) { + return setNav(p); + } + + function accountClick() { + sendAccountStates(); + navTo('account'); + } return (
-

mnml.gg

- {accountStatus} +
+ + + +
); } - -module.exports = addState(renderHeader); +module.exports = addState(Header); \ No newline at end of file diff --git a/client/src/components/info.component.jsx b/client/src/components/info.component.jsx index 2696cadf..29edb8cb 100644 --- a/client/src/components/info.component.jsx +++ b/client/src/components/info.component.jsx @@ -9,7 +9,6 @@ const shapes = require('./shapes'); function InfoComponent(args) { const { itemInfo, - combiner, player, info, } = args; @@ -18,7 +17,21 @@ function InfoComponent(args) { // const { info } = args; function Info() { - if (!info) return false; + if (!info) { + return ( +
+

VBOX phase

+

double clicking items in the VBOX will purchase and move them to your INVENTORY.

+

+ hover over an item to see its effects and combinations.
+ combine a SKILL or SPEC with 2 COLOURS to create an item.
+ combine 3 of the same item to upgrade it.
+ click an item and then click a construct to equip that item to it.
+

+

click the READY button on the right to progress to the GAME PHASE.

+
+ ); + } const fullInfo = itemInfo.items.find(i => i.item === info) || INFO[info]; if (!fullInfo) return false; const isSkill = fullInfo.skill; @@ -28,17 +41,11 @@ function InfoComponent(args) { const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat)/; const infoDescription = reactStringReplace(fullInfo.description, regEx, match => shapes[match]()); - const itemSource = itemInfo.combos.filter(c => c.item === info); - const itemSourceInfo = itemSource.length - ? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}` - : false; - const itemRegEx = /(Red|Blue|Green)/; - const itemSourceDescription = reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]()); return (

{fullInfo.item}

-

{itemSourceDescription}

-
{infoDescription}
+

SKILL

+
{infoDescription}
); } @@ -132,6 +139,7 @@ function InfoComponent(args) { return (

{info}

+

SPEC

{infoDescription}
{thresholds} @@ -150,30 +158,11 @@ function InfoComponent(args) { function Combos() { if (!player) return false; - - // show recipe for what's in combiner - if (combiner.some(u => u !== null)) { - const filteredCombos = itemInfo.combos - .filter(combo => combiner.every(u => u === null - || combo.components.includes(player.vbox.bound[u]))); - if (filteredCombos.length > 6) return false; - return ( - - - {filteredCombos.map((c, i) => - - - {c.components.map((u, j) => )} - - )} - -
{convertItem(c.item)}{convertItem(u)}
- ); - } - if (!info) return false; + const vboxCombos = itemInfo.combos.filter(c => c.components.includes(info)); if (vboxCombos.length > 6) return false; + return ( diff --git a/client/src/components/instance.constructs.jsx b/client/src/components/instance.constructs.jsx index 1761b656..1eecfd6e 100644 --- a/client/src/components/instance.constructs.jsx +++ b/client/src/components/instance.constructs.jsx @@ -98,7 +98,7 @@ function Construct(props) { const skill = construct.skills[i]; const s = skill ? skill.skill - : (+); + : (SKILL); function skillClick(e) { if (!skill) return false; diff --git a/client/src/components/instance.ctrl.jsx b/client/src/components/instance.ctrl.jsx index 129bb80c..a431f1d4 100644 --- a/client/src/components/instance.ctrl.jsx +++ b/client/src/components/instance.ctrl.jsx @@ -93,11 +93,13 @@ function Controls(args) { ); return ( - - // ); - // } - - // const highlighted = value && vboxHighlight.includes(value); - - // return ( - // - // ); - // }); - - function reclaimClick(e) { e.stopPropagation(); return setReclaiming(!reclaiming); @@ -292,6 +243,7 @@ function Vbox(args) { } function onClick(e) { + if (vboxSelecting) clearVboxSelected(); if (reclaiming) return sendVboxReclaim(i); const combinerIndex = combiner.indexOf(i); @@ -310,7 +262,8 @@ function Vbox(args) { ); @@ -319,7 +272,8 @@ function Vbox(args) { return ( @@ -346,7 +300,8 @@ function Vbox(args) { class='vbox-btn' disabled={combiner.length !== 3} onMouseOver={e => hoverInfo(e, 'refine')} - onClick={() => sendVboxCombine()}> + onClick={e => e.stopPropagation()} + onMouseDown={() => sendVboxCombine()}> {text} ); @@ -363,7 +318,8 @@ function Vbox(args) { return (
e.stopPropagation()} style={vboxSelecting || itemUnequip.length ? { cursor: 'pointer' } : null} onMouseOver={e => hoverInfo(e, 'inventory')}>
@@ -371,7 +327,8 @@ function Vbox(args) {
diff --git a/client/src/events.jsx b/client/src/events.jsx index 876d184b..f378ba24 100644 --- a/client/src/events.jsx +++ b/client/src/events.jsx @@ -3,7 +3,7 @@ const eachSeries = require('async/eachSeries'); const actions = require('./actions'); const { TIMES } = require('./constants'); const animations = require('./animations.utils'); -const { infoToast, errorToast } = require('./utils'); +const { infoToast, errorToast, removeTier } = require('./utils'); function registerEvents(store) { function notify(msg) { @@ -69,13 +69,12 @@ function registerEvents(store) { const timeout = animations.getTime(sequence); const anims = animations.getObjects(r, sequence, game, account); const text = animations.getText(r, sequence); - store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game))); if (sequence.includes('START_SKILL')) store.dispatch(actions.setAnimSource(anims.animSource)); if (sequence.includes('END_SKILL')) { store.dispatch(actions.setAnimTarget(anims.animTarget)); - if (!['Banish', 'Invert'].includes(anims.animTarget.skill)) store.dispatch(actions.setAnimCb(cb)); + if (!['Banish', 'Invert'].includes(removeTier(anims.animTarget.skill))) store.dispatch(actions.setAnimCb(cb)); } if (sequence.includes('POST_SKILL')) { // timeout to prevent text classes from being added too soon @@ -90,7 +89,7 @@ function registerEvents(store) { store.dispatch(actions.setAnimText(null)); store.dispatch(actions.setAnimFocus([])); if (!sequence.includes('END_SKILL') - || ['Banish', 'Invert'].includes(anims.animTarget.skill)) return cb(); + || ['Banish', 'Invert'].includes(removeTier(anims.animTarget.skill))) return cb(); return true; }, timeout); }, err => { diff --git a/client/src/reducers.jsx b/client/src/reducers.jsx index 35c45a64..496a5acc 100644 --- a/client/src/reducers.jsx +++ b/client/src/reducers.jsx @@ -50,7 +50,6 @@ module.exports = { teamPage: createReducer(0, 'SET_TEAM_PAGE'), teamSelect: createReducer([null, null, null], 'SET_TEAM_SELECT'), - vboxHighlight: createReducer([], 'SET_VBOX_HIGHLIGHT'), vboxSelected: createReducer([], 'SET_VBOX_SELECTED'), ws: createReducer(null, 'SET_WS'), diff --git a/client/src/socket.jsx b/client/src/socket.jsx index d4a0c421..d3f91ff4 100644 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -200,7 +200,7 @@ function createSocket(events) { let pongTimeout; function onPong() { events.setPing(Date.now() - ping); - pongTimeout = setTimeout(sendPing, 1000); + // pongTimeout = setTimeout(sendPing, 1000); } // ------------- diff --git a/etc/telegraf/telegraf.conf b/etc/telegraf/telegraf.conf index 9d994fd3..97a108c7 100644 --- a/etc/telegraf/telegraf.conf +++ b/etc/telegraf/telegraf.conf @@ -101,7 +101,7 @@ ## urls will be written to each interval. # urls = ["unix:///var/run/influxdb.sock"] # urls = ["udp://127.0.0.1:8089"] - urls = ["http://mnml-prod-elk:8086"] + urls = ["http://mnml-prod-mn:8086"] ## The target database for metrics; will be created as needed. ## For UDP url endpoint database needs to be configured on server side. diff --git a/ops/package.json b/ops/package.json index 75a489af..69c69d72 100755 --- a/ops/package.json +++ b/ops/package.json @@ -1,6 +1,6 @@ { "name": "mnml-ops", - "version": "1.2.0", + "version": "1.3.0", "description": "", "main": "index.js", "scripts": { diff --git a/server/Cargo.toml b/server/Cargo.toml index 06eb0264..26856718 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mnml" -version = "1.2.0" +version = "1.3.0" authors = ["ntr "] [dependencies] diff --git a/server/src/account.rs b/server/src/account.rs index 716c9b10..db90e6a6 100644 --- a/server/src/account.rs +++ b/server/src/account.rs @@ -10,7 +10,7 @@ use postgres::transaction::Transaction; use http::MnmlHttpError; use names::{name as generate_name}; -use construct::{Construct, construct_recover, construct_spawn}; +use construct::{Construct, ConstructSkeleton, construct_spawn}; use instance::{Instance, instance_delete}; use mtx::{Mtx, FREE_MTX}; use pg::Db; @@ -350,23 +350,20 @@ pub fn constructs(tx: &mut Transaction, account: &Account) -> Result, _> = result.iter() - .map(|row| { + let mut constructs = result.iter() + .filter_map(|row| { let construct_bytes: Vec = row.get(0); - match from_slice::(&construct_bytes) { - Ok(c) => Ok(c), - Err(_e) => construct_recover(construct_bytes, tx), + match from_slice::(&construct_bytes) { + Ok(s) => Some(s), + Err(e) => { + warn!("{:?}", e); + None + }, } }) - .collect(); + .map(|sk| Construct::from_skeleton(&sk)) + .collect::>(); - // catch any errors - if constructs.is_err() { - warn!("{:?}", constructs); - return Err(err_msg("could not deserialise a construct")); - } - - let mut constructs = constructs.unwrap(); constructs.sort_by_key(|c| c.id); return Ok(constructs); } @@ -382,23 +379,19 @@ pub fn team(tx: &mut Transaction, account: &Account) -> Result, E let result = tx .query(query, &[&account.id])?; - let constructs: Result, _> = result.iter() - .map(|row| { + let mut constructs = result.iter() + .filter_map(|row| { let construct_bytes: Vec = row.get(0); - match from_slice::(&construct_bytes) { - Ok(c) => Ok(c), - Err(_e) => construct_recover(construct_bytes, tx), + match from_slice::(&construct_bytes) { + Ok(s) => Some(s), + Err(e) => { + warn!("{:?}", e); + None + }, } }) - .collect(); - - // catch any errors - if constructs.is_err() { - warn!("{:?}", constructs); - return Err(err_msg("could not deserialise a construct")); - } - - let mut constructs = constructs.unwrap(); + .map(|sk| Construct::from_skeleton(&sk)) + .collect::>(); if constructs.len() != 3 { return Err(format_err!("team not size 3 account={:?}", account)); diff --git a/server/src/construct.rs b/server/src/construct.rs index 652acb1b..c7a4f35c 100644 --- a/server/src/construct.rs +++ b/server/src/construct.rs @@ -187,9 +187,10 @@ impl ConstructStat { } #[derive(Debug,Clone,Serialize,Deserialize)] -pub struct ConstructRecover { +pub struct ConstructSkeleton { pub id: Uuid, pub account: Uuid, + pub img: Uuid, pub name: String, } @@ -220,13 +221,13 @@ impl Construct { id, account: id, img: Uuid::new_v4(), - red_power: ConstructStat { base: 256, value: 256, max: 256, 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 }, - blue_power: ConstructStat { base: 256, value: 256, max: 256, 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 }, - green_power: ConstructStat { base: 256, value: 256, max: 256, stat: Stat::GreenPower }, - green_life: ConstructStat { base: 1024, value: 1024, max: 1024, stat: Stat::GreenLife }, - speed: ConstructStat { base: 128, value: 128, max: 128, stat: Stat::Speed }, + green_power: ConstructStat { base: 320, value: 300, max: 300, stat: Stat::GreenPower }, + green_life: ConstructStat { base: 950, value: 950, max: 950, 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![], effects: vec![], @@ -236,6 +237,27 @@ impl Construct { }; } + pub fn from_skeleton(skeleton: &ConstructSkeleton) -> Construct { + return Construct { + id: skeleton.id, + account: skeleton.id, + img: skeleton.img, + name: skeleton.name.clone(), + + .. Construct::new() + }; + } + + pub fn to_skeleton(&self) -> ConstructSkeleton { + ConstructSkeleton { + id: self.id, + account: self.id, + img: self.img, + name: self.name.clone(), + } + } + + pub fn named(mut self, name: &String) -> Construct { self.name = name.clone(); self @@ -827,10 +849,9 @@ pub fn construct_get(tx: &mut Transaction, id: Uuid, account_id: Uuid) -> Result let result = result.iter().next().ok_or(format_err!("construct {:} not found", id))?; let construct_bytes: Vec = result.get(0); - let construct = from_slice::(&construct_bytes) - .or_else(|_| construct_recover(construct_bytes, tx))?; + let skeleton = from_slice::(&construct_bytes)?; - return Ok(construct); + return Ok(Construct::from_skeleton(&skeleton)); } pub fn construct_select(tx: &mut Transaction, id: Uuid, account_id: Uuid) -> Result { @@ -847,10 +868,9 @@ pub fn construct_select(tx: &mut Transaction, id: Uuid, account_id: Uuid) -> Res let result = result.iter().next().ok_or(format_err!("construct {:} not found", id))?; let construct_bytes: Vec = result.get(0); - let construct = from_slice::(&construct_bytes) - .or_else(|_| construct_recover(construct_bytes, tx))?; + let skeleton = from_slice::(&construct_bytes)?; - return Ok(construct); + return Ok(Construct::from_skeleton(&skeleton)); } pub fn construct_spawn(tx: &mut Transaction, account: Uuid, name: String, team: bool) -> Result { @@ -878,7 +898,7 @@ pub fn construct_spawn(tx: &mut Transaction, account: Uuid, name: String, team: } pub fn construct_write(tx: &mut Transaction, construct: Construct) -> Result { - let construct_bytes = to_vec(&construct)?; + let construct_bytes = to_vec(&construct.to_skeleton())?; let query = " UPDATE constructs @@ -897,20 +917,6 @@ pub fn construct_write(tx: &mut Transaction, construct: Construct) -> Result, tx: &mut Transaction) -> Result { - let c = from_slice::(&construct_bytes)?; - - let mut construct = Construct::new() - .named(&c.name) - .set_account(c.account); - - construct.id = c.id; - - info!("recovered construct {:?}", c.name); - - return construct_write(tx, construct); -} - #[cfg(test)] mod tests { use construct::*; diff --git a/server/src/events.rs b/server/src/events.rs index ca1ce5af..da9fe692 100644 --- a/server/src/events.rs +++ b/server/src/events.rs @@ -132,12 +132,12 @@ impl Events { }, Event::Subscribe(id, obj) => { - info!("subscribe id={:?} object={:?}", id, obj); + trace!("subscribe id={:?} object={:?}", id, obj); match self.clients.get_mut(&id) { Some(client) => { client.subs.insert(obj); - info!("client={:?} subscriptions={:?}", id, client.subs.len()); + trace!("client={:?} subscriptions={:?}", id, client.subs.len()); Ok(()) }, None => return Err(format_err!("unknown client {:?}", id)) @@ -145,12 +145,12 @@ impl Events { }, Event::Unsubscribe(id, obj) => { - info!("unsubscribe id={:?} object={:?}", id, obj); + trace!("unsubscribe id={:?} object={:?}", id, obj); match self.clients.get_mut(&id) { Some(mut client) => { client.subs.remove(&obj); - info!("unsubscribe subscriptions removed={:?}", client.subs.len()); + trace!("unsubscribe subscriptions removed={:?}", client.subs.len()); Ok(()) }, None => return Err(format_err!("unknown client {:?}", id)) @@ -158,7 +158,7 @@ impl Events { }, Event::Push(id, msg) => { - info!("push id={:?}", id); + trace!("push id={:?}", id); let mut subs = 0; let mut dead = vec![]; @@ -177,11 +177,11 @@ impl Events { } if !dead.is_empty() { - info!("dead connections={:?}", dead.len()); + trace!("dead connections={:?}", dead.len()); dead.iter().for_each(|id| self.remove_client(*id)); } - info!("push subscribers={:?}", subs); + trace!("push subscribers={:?}", subs); Ok(()) }, diff --git a/server/src/game.rs b/server/src/game.rs index 2c64d05f..5b2191c4 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1152,7 +1152,8 @@ mod tests { // should not be stunned because of counter 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(), (1024 - x_construct.red_power().pct(Skill::CounterAttack.multiplier()))); + 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()))); } #[test] @@ -1235,7 +1236,7 @@ mod tests { assert_eq!(target.id, y_construct.id); match event { Event::Damage { amount, skill: _, mitigation: _, colour: _} => - assert_eq!(amount, 256.pct(Skill::Attack.multiplier()) >> 1), + assert_eq!(amount, x_construct.red_power().pct(Skill::Attack.multiplier()) >> 1), _ => panic!("not damage link"), }; } diff --git a/server/src/img.rs b/server/src/img.rs index a1b15953..75b0d95f 100644 --- a/server/src/img.rs +++ b/server/src/img.rs @@ -53,16 +53,15 @@ pub fn invader_write(id: Uuid) -> Result { colours.push("none".to_string()); // add up to 100 for % - // [3] is the transparent colour let weights = [ 5, 5, 65, - 25, + 25, // the transparent colour ]; let colour_dist = WeightedIndex::new(&weights)?; - write!(&mut svg, "")?; + write!(&mut svg, "")?; for i in 0..50 { let x = (i % 5) * 50; let y = (i / 5) * 50; diff --git a/server/src/instance.rs b/server/src/instance.rs index 34f72bd1..6feeb8f1 100644 --- a/server/src/instance.rs +++ b/server/src/instance.rs @@ -76,7 +76,7 @@ impl TimeControl { pub fn lobby_timeout(&self) -> DateTime { Utc::now() - .checked_add_signed(Duration::minutes(5)) + .checked_add_signed(Duration::seconds(30)) .expect("could not set phase end") } @@ -366,7 +366,7 @@ impl Instance { } fn current_game_id(&self) -> Option { - if self.phase == InstancePhase::Lobby { + if self.phase != InstancePhase::InProgress { return None; } diff --git a/server/src/item.rs b/server/src/item.rs index 67efb76e..a2d4b5cf 100644 --- a/server/src/item.rs +++ b/server/src/item.rs @@ -88,181 +88,181 @@ pub enum Item { AmplifyPlus, #[serde(rename = "Amplify++")] AmplifyPlusPlus, - + Absorb, #[serde(rename = "Absorb+")] AbsorbPlus, #[serde(rename = "Absorb++")] AbsorbPlusPlus, - + Banish, #[serde(rename = "Banish+")] BanishPlus, #[serde(rename = "Banish++")] BanishPlusPlus, - + Bash, #[serde(rename = "Bash+")] BashPlus, #[serde(rename = "Bash++")] BashPlusPlus, - + Blast, #[serde(rename = "Blast+")] BlastPlus, #[serde(rename = "Blast++")] BlastPlusPlus, - + Chaos, #[serde(rename = "Chaos+")] ChaosPlus, #[serde(rename = "Chaos++")] ChaosPlusPlus, - + Sustain, #[serde(rename = "Sustain+")] SustainPlus, #[serde(rename = "Sustain++")] SustainPlusPlus, - + Electrify, #[serde(rename = "Electrify+")] ElectrifyPlus, #[serde(rename = "Electrify++")] ElectrifyPlusPlus, - + Curse, #[serde(rename = "Curse+")] CursePlus, #[serde(rename = "Curse++")] CursePlusPlus, - + Decay, #[serde(rename = "Decay+")] DecayPlus, #[serde(rename = "Decay++")] DecayPlusPlus, - + Hex, #[serde(rename = "Hex+")] HexPlus, #[serde(rename = "Hex++")] HexPlusPlus, - + Haste, #[serde(rename = "Haste+")] HastePlus, #[serde(rename = "Haste++")] HastePlusPlus, - + Heal, #[serde(rename = "Heal+")] HealPlus, #[serde(rename = "Heal++")] HealPlusPlus, - + Hybrid, #[serde(rename = "Hybrid+")] HybridPlus, #[serde(rename = "Hybrid++")] HybridPlusPlus, - + Invert, #[serde(rename = "Invert+")] InvertPlus, #[serde(rename = "Invert++")] InvertPlusPlus, - + Counter, #[serde(rename = "Counter+")] CounterPlus, #[serde(rename = "Counter++")] CounterPlusPlus, - + Purge, #[serde(rename = "Purge+")] PurgePlus, #[serde(rename = "Purge++")] PurgePlusPlus, - + Purify, #[serde(rename = "Purify+")] PurifyPlus, #[serde(rename = "Purify++")] PurifyPlusPlus, - + Reflect, #[serde(rename = "Reflect+")] ReflectPlus, #[serde(rename = "Reflect++")] ReflectPlusPlus, - + Recharge, #[serde(rename = "Recharge+")] RechargePlus, #[serde(rename = "Recharge++")] RechargePlusPlus, - + Ruin, #[serde(rename = "Ruin+")] RuinPlus, #[serde(rename = "Ruin++")] RuinPlusPlus, - + Link, #[serde(rename = "Link+")] LinkPlus, #[serde(rename = "Link++")] LinkPlusPlus, - + Silence, #[serde(rename = "Silence+")] SilencePlus, #[serde(rename = "Silence++")] SilencePlusPlus, - + Slay, #[serde(rename = "Slay+")] SlayPlus, #[serde(rename = "Slay++")] SlayPlusPlus, - + Sleep, #[serde(rename = "Sleep+")] SleepPlus, #[serde(rename = "Sleep++")] SleepPlusPlus, - + Restrict, #[serde(rename = "Restrict+")] RestrictPlus, #[serde(rename = "Restrict++")] RestrictPlusPlus, - + Strike, #[serde(rename = "Strike+")] StrikePlus, #[serde(rename = "Strike++")] StrikePlusPlus, - + Siphon, #[serde(rename = "Siphon+")] SiphonPlus, #[serde(rename = "Siphon++")] SiphonPlusPlus, - + Intercept, #[serde(rename = "Intercept+")] InterceptPlus, #[serde(rename = "Intercept++")] InterceptPlusPlus, - + Break, #[serde(rename = "Break+")] BreakPlus, #[serde(rename = "Break++")] BreakPlusPlus, - + Triage, #[serde(rename = "Triage+")] TriagePlus, @@ -748,7 +748,7 @@ impl Item { Item::Counter| Item::CounterPlus | - Item::CounterPlusPlus => format!("Self targetting skill. Recharges RedLife for {:?}% RedPower and blocks red skills for {:?}T. + Item::CounterPlusPlus => format!("Self targetting skill. Recharges RedLife for {:?}% RedPower and blocks red skills for {:?}T. If a red skill is parried the construct will riposte the source dealing {:?}% RedPower as red damage.", self.into_skill().unwrap().multiplier(), self.into_skill().unwrap().effect()[0].get_duration(), diff --git a/server/src/skill.rs b/server/src/skill.rs index e00d86e9..c587a465 100644 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -497,152 +497,235 @@ pub enum Skill { // Evade, // actively evade // Nightmare, // Sleep, + Amplify, + #[serde(rename = "Amplify+")] AmplifyPlus, + #[serde(rename = "Amplify++")] AmplifyPlusPlus, + Absorb, + #[serde(rename = "Absorb+")] + AbsorbPlus, + #[serde(rename = "Absorb++")] + AbsorbPlusPlus, + Banish, + #[serde(rename = "Banish+")] BanishPlus, + #[serde(rename = "Banish++")] BanishPlusPlus, Bash, + #[serde(rename = "Bash+")] BashPlus, + #[serde(rename = "Bash++")] BashPlusPlus, Blast, + #[serde(rename = "Blast+")] BlastPlus, + #[serde(rename = "Blast++")] BlastPlusPlus, Chaos, + #[serde(rename = "Chaos+")] ChaosPlus, + #[serde(rename = "Chaos++")] ChaosPlusPlus, Sustain, + #[serde(rename = "Sustain+")] SustainPlus, + #[serde(rename = "Sustain++")] SustainPlusPlus, Electrify, + #[serde(rename = "Electrify+")] ElectrifyPlus, + #[serde(rename = "Electrify++")] ElectrifyPlusPlus, - Electrocute, - ElectrocutePlus, - ElectrocutePlusPlus, - ElectrocuteTick, - ElectrocuteTickPlus, - ElectrocuteTickPlusPlus, Curse, + #[serde(rename = "Curse+")] CursePlus, + #[serde(rename = "Curse++")] CursePlusPlus, - Decay, // dot + Decay, + #[serde(rename = "Decay+")] DecayPlus, + #[serde(rename = "Decay++")] DecayPlusPlus, - DecayTick, // dot - DecayTickPlus, - DecayTickPlusPlus, - - Haste, - HastePlus, - HastePlusPlus, - HasteStrike, - - Heal, - HealPlus, - HealPlusPlus, Hex, + #[serde(rename = "Hex+")] HexPlus, + #[serde(rename = "Hex++")] HexPlusPlus, - Absorption, - AbsorptionPlus, - AbsorptionPlusPlus, - Absorb, - AbsorbPlus, - AbsorbPlusPlus, - HybridBlast, + Haste, + #[serde(rename = "Haste+")] + HastePlus, + #[serde(rename = "Haste++")] + HastePlusPlus, + + Heal, + #[serde(rename = "Heal+")] + HealPlus, + #[serde(rename = "Heal++")] + HealPlusPlus, Hybrid, + #[serde(rename = "Hybrid+")] HybridPlus, + #[serde(rename = "Hybrid++")] HybridPlusPlus, Invert, + #[serde(rename = "Invert+")] InvertPlus, + #[serde(rename = "Invert++")] InvertPlusPlus, - Counter, // avoid all damage + Counter, + #[serde(rename = "Counter+")] CounterPlus, + #[serde(rename = "Counter++")] CounterPlusPlus, + Purge, + #[serde(rename = "Purge+")] PurgePlus, + #[serde(rename = "Purge++")] PurgePlusPlus, Purify, + #[serde(rename = "Purify+")] PurifyPlus, + #[serde(rename = "Purify++")] PurifyPlusPlus, - Recharge, - RechargePlus, - RechargePlusPlus, - Reflect, + #[serde(rename = "Reflect+")] ReflectPlus, + #[serde(rename = "Reflect++")] ReflectPlusPlus, - CounterAttack, - CounterAttackPlus, - CounterAttackPlusPlus, + Recharge, + #[serde(rename = "Recharge+")] + RechargePlus, + #[serde(rename = "Recharge++")] + RechargePlusPlus, Ruin, + #[serde(rename = "Ruin+")] RuinPlus, + #[serde(rename = "Ruin++")] RuinPlusPlus, Link, + #[serde(rename = "Link+")] LinkPlus, + #[serde(rename = "Link++")] LinkPlusPlus, Silence, + #[serde(rename = "Silence+")] SilencePlus, + #[serde(rename = "Silence++")] SilencePlusPlus, - Siphon, - SiphonPlus, - SiphonPlusPlus, - SiphonTick, - SiphonTickPlus, - SiphonTickPlusPlus, - Slay, + #[serde(rename = "Slay+")] SlayPlus, + #[serde(rename = "Slay++")] SlayPlusPlus, Sleep, + #[serde(rename = "Sleep+")] SleepPlus, + #[serde(rename = "Sleep++")] SleepPlusPlus, Restrict, + #[serde(rename = "Restrict+")] RestrictPlus, + #[serde(rename = "Restrict++")] RestrictPlusPlus, Strike, + #[serde(rename = "Strike+")] StrikePlus, + #[serde(rename = "Strike++")] StrikePlusPlus, + Siphon, + #[serde(rename = "Siphon+")] + SiphonPlus, + #[serde(rename = "Siphon++")] + SiphonPlusPlus, + Intercept, + #[serde(rename = "Intercept+")] InterceptPlus, + #[serde(rename = "Intercept++")] InterceptPlusPlus, - Break, // no damage stun, adds vulnerable + Break, + #[serde(rename = "Break+")] BreakPlus, + #[serde(rename = "Break++")] BreakPlusPlus, - Triage, // hot + Triage, + #[serde(rename = "Triage+")] TriagePlus, + #[serde(rename = "Triage++")] TriagePlusPlus, + Absorption, + #[serde(rename = "Absorption+")] + AbsorptionPlus, + #[serde(rename = "Absorption++")] + AbsorptionPlusPlus, + + CounterAttack, + #[serde(rename = "CounterAttack+")] + CounterAttackPlus, + #[serde(rename = "CounterAttack++")] + CounterAttackPlusPlus, + + Electrocute, + #[serde(rename = "Electrocute+")] + ElectrocutePlus, + #[serde(rename = "Electrocute++")] + ElectrocutePlusPlus, + ElectrocuteTick, + #[serde(rename = "ElectrocuteTick+")] + ElectrocuteTickPlus, + #[serde(rename = "ElectrocuteTick++")] + ElectrocuteTickPlusPlus, + + DecayTick, // dot + #[serde(rename = "DecayTick+")] + DecayTickPlus, + #[serde(rename = "DecayTick++")] + DecayTickPlusPlus, + + HasteStrike, + HybridBlast, + + SiphonTick, + #[serde(rename = "SiphonTick+")] + SiphonTickPlus, + #[serde(rename = "SiphonTick++")] + SiphonTickPlusPlus, + TriageTick, + #[serde(rename = "TriageTick+")] TriageTickPlus, + #[serde(rename = "TriageTick++")] TriageTickPlusPlus, } @@ -1675,13 +1758,14 @@ fn purge(source: &mut Construct, target: &mut Construct, mut results: Resolution .event(Event::Removal { effect: ce.effect, construct_effects: target.effects.clone() })); } - let mut turns = 0; + let mut turns = 1; for cs in target.skills.iter_mut() { if Effect::Purge.disables_skill(cs.skill) { turns += 1; } } - if turns > 0 { + + if turns > 1 { let mut effect = skill.effect()[0]; effect.duration = effect.duration * turns; results.push(Resolution::new(source, target).event(target.add_effect(skill, effect)).stages(EventStages::PostOnly)); @@ -1789,6 +1873,7 @@ mod tests { .named(&"camel".to_string()); x.red_power.force(10000000000000); // multiplication of int max will cause overflow + y.green_life.force(1024); // make tests more flexible if we change stats sustain(&mut y.clone(), &mut y, vec![], Skill::Sustain); assert!(y.affected(Effect::Sustain)); @@ -1885,6 +1970,8 @@ mod tests { let mut y = Construct::new() .named(&"camel".to_string()); + x.blue_power.force(256); + x.green_life.force(1024); x.green_life.reduce(512); let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]); diff --git a/server/src/spec.rs b/server/src/spec.rs index 0f6b5125..443bdf18 100644 --- a/server/src/spec.rs +++ b/server/src/spec.rs @@ -1,6 +1,5 @@ use construct::{Stat, Colours}; use util::{IntPct}; -use std::cmp; #[derive(Debug,Clone,Serialize,Deserialize)] pub struct SpecBonus { @@ -29,9 +28,11 @@ impl SpecValues { self.bonuses.iter().fold(self.base, |acc, s| acc + s.get_bonus(c)) } +/* pub fn calc_multi (&self, c: &Colours) -> u64 { self.multi * (c.red + c.green + c.blue) as u64 } +*/ } @@ -711,10 +712,8 @@ impl Spec { Spec::SpeedBBPlusPlus | Spec::SpeedRGPlusPlus | Spec::SpeedGBPlusPlus | - Spec::SpeedRBPlusPlus => modified + { - base.pct(cmp::min(self.values().calc_multi(construct_colours), - self.values().max_value(player_colours))) - }, + Spec::SpeedRBPlusPlus => modified + base.pct(self.values().max_value(player_colours)), + // Flat bonus Spec::Life => modified + self.values().base, Spec::LifeRR| @@ -734,10 +733,7 @@ impl Spec { Spec::LifeBBPlusPlus | Spec::LifeRGPlusPlus | Spec::LifeGBPlusPlus | - Spec::LifeRBPlusPlus => modified + { - cmp::min(self.values().calc_multi(construct_colours), - self.values().max_value(player_colours)) - }, + Spec::LifeRBPlusPlus => modified + self.values().max_value(player_colours), } } }
- //   - // vboxHover(e, value)} - // onClick={e => boundClick(e, i, highlighted) }> - // {convertItem(value)} - //