diff --git a/COMBOS.md b/COMBOS.md index dbc20358..f0e082b0 100644 --- a/COMBOS.md +++ b/COMBOS.md @@ -1,3 +1,8 @@ +# vbox_info -> + +combos [strike, [R R Attack]] +specs [spec [bonus amount, [r g b]] + # Playthrough cryps join game @@ -118,7 +123,7 @@ All current specs / items can be further combo'd into T2 and T3 versions Upgraded skills will have a combination of higher damage / longer duration / reduced cooldown Upgraded skills use the same speed formula as previously -### Spec / Skill hybrid specs ### +### Spec / Skill hybrid specs ### # Strike # 2 x Red Damage + Strike => Strike damage bonus (crit?) diff --git a/client/cryps.css b/client/cryps.css index 6490fa65..4f867c8b 100644 --- a/client/cryps.css +++ b/client/cryps.css @@ -116,6 +116,21 @@ button.left:hover, button.left:focus { box-shadow: inset 0.5em 0 0 0 whitesmoke; } +button.action { + animation: action 1s infinite ease-in-out alternate; +} + +@keyframes action { + 0% { + box-shadow: inset 0 0 0 0 whitesmoke; + } + 100% { + box-shadow: inset 0.5em 0 0 0 whitesmoke; + } +} + + + svg { flex: 1; fill: none; @@ -427,6 +442,12 @@ header { margin: 0; } +table .highlight { + background: whitesmoke; + color: black; + font-weight: bold; +} + button[disabled] { color: #333; border-color: #333; @@ -489,7 +510,6 @@ table td { padding: 0.2em; text-align: center; height: 40px; - cursor: pointer; text-transform: uppercase; } @@ -498,6 +518,7 @@ table td { transition-duration: 0.5s; transition-delay: 0; transition-timing-function: ease; + cursor: pointer; } .vbox-table table td:active { diff --git a/client/package.json b/client/package.json index 659493d8..6ef0299f 100644 --- a/client/package.json +++ b/client/package.json @@ -28,7 +28,8 @@ "phaser": "^3.15.1", "preact": "^8.3.1", "preact-redux": "^2.0.3", - "redux": "^4.0.0" + "redux": "^4.0.0", + "redux-diff-logger": "0.0.9" }, "devDependencies": { "babel-core": "^6.26.3", diff --git a/client/src/actions.jsx b/client/src/actions.jsx index 5bd8654f..0ed25f3e 100644 --- a/client/src/actions.jsx +++ b/client/src/actions.jsx @@ -4,6 +4,9 @@ export const setAccount = value => ({ type: SET_ACCOUNT, value }); export const SET_CRYPS = 'SET_CRYPS'; export const setCryps = value => ({ type: SET_CRYPS, value }); +export const SET_VBOX_INFO = 'SET_VBOX_INFO'; +export const setVboxInfo = value => ({ type: SET_VBOX_INFO, value }); + export const SET_INSTANCES = 'SET_INSTANCES'; export const setInstances = value => ({ type: SET_INSTANCES, value }); diff --git a/client/src/components/info.component.jsx b/client/src/components/info.component.jsx index 52faaaa1..b2f3259c 100644 --- a/client/src/components/info.component.jsx +++ b/client/src/components/info.component.jsx @@ -2,7 +2,7 @@ const preact = require('preact'); const range = require('lodash/range'); const { ITEMS: { SKILLS, COLOURS, SPECS: SPEC_CONSTANT } } = require('./../constants'); -const { COLOUR_ICONS, STATS, SPECS } = require('../utils'); +const { COLOUR_ICONS, STATS, SPECS, convertVar, crypAvatar } = require('../utils'); function Info(args) { const { @@ -12,10 +12,16 @@ function Info(args) { instance, player, setInfo, + vboxInfo, } = args; - function infoVar([type, value]) { - let red = 0; let blue = 0; let green = 0; + function CrypVar() { + const [type, value] = info; + if (!type) return false; + + let red = 0; + let blue = 0; + let green = 0; player.cryps.forEach(cryp => { red += cryp.colours.red; blue += cryp.colours.blue; @@ -100,7 +106,12 @@ function Info(args) { } } - function infoCrypElement(cryp) { + function CrypInfo() { + if (!activeCryp) return false; + const cryp = player.cryps.find(c => c.id === activeCryp.id); + + if (!cryp) return false; + // onClick={() => setInfo('skill', { skill: s, cryp })} const skills = range(0, 3).map(i => { const skill = cryp.skills[i]; @@ -139,7 +150,6 @@ function Info(args) { return (
-

{cryp.name}

{stats}
@@ -158,10 +168,10 @@ function Info(args) { return instance.rounds[instance.rounds.length - 1].find(r => r.player_ids.includes(id)); } - function playerText(player) { - const round = playerRound(player.id); + function playerText(p) { + const round = playerRound(p.id); if (!round) { - return player.ready + return p.ready ? 'ready' : ''; } @@ -169,12 +179,14 @@ function Info(args) { if (round.finished) return 'finished'; if (round.game_id) return 'in game'; - return player.ready + return p.ready ? 'ready' : ''; } - function scoreBoard() { + function ScoreBoard() { + if (activeCryp || info[0]) return null; + const players = instance.players.map((p, i) => { const pText = playerText(p); return ( @@ -196,29 +208,33 @@ function Info(args) { ); } - const scoreBoardEl = activeCryp || info[0] - ? null - : scoreBoard(); - - const infoCryp = activeCryp - ? infoCrypElement(player.cryps.find(c => c.id === activeCryp.id)) - : null; - - const otherInfo = info[0] - ? infoVar(info) - : null; + function Combos() { + if (!info[0]) return false; + if (activeCryp) return false; + return ( + + + {vboxInfo.combos.filter(c => c.units.includes(info[1])).map((c, i) => + + + {c.units.map(u => )} + + )} + +
{convertVar(c.var)}{convertVar(u)}
+ ); + } // const beginningHdr = instance.phase === 'Lobby' // ?

game beginning...

// : null; - const instanceInfoClass = `instance-info ${!info[0] ? '' : 'hidden'}`; - return ( -
- {scoreBoardEl} - {infoCryp} - {otherInfo} +
+ + + +
); } diff --git a/client/src/components/info.container.jsx b/client/src/components/info.container.jsx index 7507c31f..e30110ef 100644 --- a/client/src/components/info.container.jsx +++ b/client/src/components/info.container.jsx @@ -10,6 +10,7 @@ const addState = connect( info, ws, instance, + vboxInfo, player, } = state; @@ -23,6 +24,7 @@ const addState = connect( sendUnequip, instance, player, + vboxInfo, }; }, diff --git a/client/src/components/instance.cryps.jsx b/client/src/components/instance.cryps.jsx index eda591bb..35c9cbf3 100644 --- a/client/src/components/instance.cryps.jsx +++ b/client/src/components/instance.cryps.jsx @@ -69,7 +69,8 @@ function Cryp(props) { return setActiveCryp(cryp); } - return ; + const classes = `right ${skill ? '' : 'action'}`; + return ; }); // needed for ondrop to fire diff --git a/client/src/components/vbox.component.jsx b/client/src/components/vbox.component.jsx index 6462bdb5..9ff01840 100644 --- a/client/src/components/vbox.component.jsx +++ b/client/src/components/vbox.component.jsx @@ -3,19 +3,7 @@ const range = require('lodash/range'); const shapes = require('./shapes'); -function convertVar(v) { - if (['Red', 'Green', 'Blue'].includes(v)) { - return ( - shapes.vboxColour(v.toLowerCase()) - ); - } - return v ||  ; - // uncomment for double borders in vbox; - // if (v) { - // return
{v}
; - // } - // return; -} +const { convertVar } = require('./../utils'); function Vbox(args) { const { @@ -131,10 +119,7 @@ function Vbox(args) { function boundClick(e, i) { if (reclaiming && vbox.bound[i]) sendVboxReclaim(i); else if (vbox.bound[i]) { - const insert = ['Red', 'Green', 'Blue'].includes(vbox.bound[i]) - ? combiner.findIndex(j => j === null) - : 2; - + const insert = combiner.findIndex(j => j === null); if (insert === -1) return setCombiner([i, null, null]); combiner[insert] = i; boundTimer = null; diff --git a/client/src/events.jsx b/client/src/events.jsx index bfbd0ae4..79ab20cb 100644 --- a/client/src/events.jsx +++ b/client/src/events.jsx @@ -18,24 +18,24 @@ function registerEvents(store) { }); - // cryp animations - function crypAnimations() { - const cryps = document.querySelectorAll('img'); - if (!cryps.length) return window.requestAnimationFrame(crypAnimations); - return anime({ - targets: 'img', - translateX: () => anime.random(-20, 20), - translateY: () => anime.random(0, -40), - rotate: () => anime.random(-15, 15), - duration: () => anime.random(5000, 6000), - delay: () => anime.random(0, 1000), - direction: 'alternate', - easing: 'linear', - loop: true, - }); - } - setInterval(crypAnimations, 5000); - crypAnimations(); + // // cryp animations + // function crypAnimations() { + // const cryps = document.querySelectorAll('img'); + // if (!cryps.length) return window.requestAnimationFrame(crypAnimations); + // return anime({ + // targets: 'img', + // translateX: () => anime.random(-20, 20), + // translateY: () => anime.random(0, -40), + // rotate: () => anime.random(-15, 15), + // duration: () => anime.random(5000, 6000), + // delay: () => anime.random(0, 1000), + // direction: 'alternate', + // easing: 'linear', + // loop: true, + // }); + // } + // setInterval(crypAnimations, 5000); + // crypAnimations(); function setPing(ping) { store.dispatch(actions.setPing(ping)); @@ -134,7 +134,6 @@ function registerEvents(store) { const player = v.players.find(p => p.id === account.id); if (player) store.dispatch(actions.setPlayer(player)); if (v) ws.startInstanceStateTimeout(v.id); - return store.dispatch(actions.setInstance(v)); } @@ -150,6 +149,10 @@ function registerEvents(store) { console.log('EVENT ->', 'crypStatusUpdate', { id, skill, target }); } + function setVboxInfo(v) { + return store.dispatch(actions.setVboxInfo(v)); + } + // events.on('SET_PLAYER', setInstance); // events.on('SEND_SKILL', function skillActive(gameId, crypId, targetCrypId, skill) { @@ -189,94 +192,8 @@ function registerEvents(store) { }); } - // function loginPrompt() { - // const USER_INPUT = ''; - // const PASSWORD_INPUT = ''; - // const LOGIN_BUTTON = ''; - // const REGISTER_BUTTON = ''; - // const DEMO_BUTTON = ''; - - // const ws = registry.get('ws'); - - // function submitLogin(instance, thisToast, button, e, inputs) { - // const USERNAME = inputs[0].value; - // const PASSWORD = inputs[1].value; - // ws.sendAccountLogin(USERNAME, PASSWORD); - // } - - // function submitRegister(instance, thisToast, button, e, inputs) { - // const USERNAME = inputs[0].value; - // const PASSWORD = inputs[1].value; - // ws.sendAccountCreate(USERNAME, PASSWORD); - // } - - // function submitDemo() { - // ws.sendAccountDemo(); - // } - - // const existing = document.querySelector('#login'); // Selector of your toast - // if (existing) toast.hide({}, existing, 'reconnect'); - - // toast.question({ - // id: 'login', - // theme: 'dark', - // color: 'black', - // timeout: false, - // // overlay: true, - // drag: false, - // close: false, - // title: 'LOGIN', - // position: 'center', - // inputs: [ - // [USER_INPUT, 'change', () => true, true], // true to focus - // [PASSWORD_INPUT, 'change', () => true], - // ], - // buttons: [ - // [LOGIN_BUTTON, submitLogin], // true to focus - // [REGISTER_BUTTON, submitRegister], // true to focus - // [DEMO_BUTTON, submitDemo], // true to focus - // ], - // }); - - // console.log('ACCOUNT', function closeLoginCb() { - // const prompt = document.querySelector('#login'); // Selector of your toast - // if (prompt) toast.hide({ transitionOut: 'fadeOut' }, prompt, 'EVENT ->'); - // }); - // } - - // events.on('CRYP_SPAWN', function spawnPrompt() { - // const NAME_INPUT = ''; - // const SPAWN_BUTTON = ''; - - // const ws = registry.get('ws'); - - // function submitSpawn(instance, thisToast, button, e, inputs) { - // const NAME = inputs[0].value; - // ws.sendCrypSpawn(NAME); - // instance.hide({ transitionOut: 'fadeOut' }, thisToast, 'button'); - // } - - // toast.question({ - // theme: 'dark', - // color: 'black', - // timeout: false, - // // overlay: true, - // drag: false, - // close: true, - // title: 'SPAWN CRYP', - // position: 'center', - // inputs: [ - // [NAME_INPUT, 'change', null, true], // true to focus - // ], - // buttons: [ - // [SPAWN_BUTTON, submitSpawn], // true to focus - // ], - // }); - // }); - return { errorPrompt, - // loginPrompt, clearCombiner, setAccount, setActiveSkill, @@ -294,6 +211,7 @@ function registerEvents(store) { setZone, setPing, setScores, + setVboxInfo, }; } diff --git a/client/src/main.jsx b/client/src/main.jsx index 8018fe1f..22cf3550 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -1,8 +1,9 @@ const preact = require('preact'); const jdenticon = require('jdenticon'); +const logger = require('redux-diff-logger'); const { Provider } = require('preact-redux'); -const { createStore, combineReducers } = require('redux'); +const { createStore, combineReducers, applyMiddleware } = require('redux'); const reducers = require('./reducers'); const actions = require('./actions'); @@ -15,7 +16,8 @@ const Header = require('./components/header.container'); const Body = require('./components/body.component'); // Redux Store -const store = createStore( +const createStoreWithMiddleware = applyMiddleware(logger)(createStore); +const store = createStoreWithMiddleware( combineReducers({ account: reducers.accountReducer, activeSkill: reducers.activeSkillReducer, @@ -27,6 +29,7 @@ const store = createStore( resolution: reducers.resolutionReducer, showLog: reducers.showLogReducer, info: reducers.infoReducer, + vboxInfo: reducers.vboxInfoReducer, instance: reducers.instanceReducer, player: reducers.playerReducer, ping: reducers.pingReducer, @@ -37,9 +40,10 @@ const store = createStore( }) ); + document.fonts.load('16pt "Jura"').then(() => { const events = registerEvents(store); - store.subscribe(() => console.log(store.getState())); + // store.subscribe(() => console.log(store.getState())); setupKeys(store); const ws = createSocket(events); diff --git a/client/src/reducers.jsx b/client/src/reducers.jsx index cb4197cb..c9c69a70 100644 --- a/client/src/reducers.jsx +++ b/client/src/reducers.jsx @@ -20,6 +20,16 @@ function pingReducer(state = defaultPing, action) { } } +const defaultVboxInfo = { combos: [], vars: [] }; +function vboxInfoReducer(state = defaultVboxInfo, action) { + switch (action.type) { + case actions.SET_VBOX_INFO: + return action.value; + default: + return state; + } +} + const defaultActiveSkill = null; function activeSkillReducer(state = defaultActiveSkill, action) { switch (action.type) { @@ -188,4 +198,5 @@ module.exports = { wsReducer, infoReducer, pingReducer, + vboxInfoReducer, }; diff --git a/client/src/socket.jsx b/client/src/socket.jsx index d6c818fc..4663daed 100644 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -64,14 +64,6 @@ function createSocket(events) { send({ method: 'cryp_spawn', params: { name } }); } - function sendCrypLearn(id, skill) { - send({ method: 'cryp_learn', params: { id, skill } }); - } - - function sendCrypForget(id, skill) { - send({ method: 'cryp_forget', params: { id, skill } }); - } - function sendGameState(id) { send({ method: 'game_state', params: { id } }); } @@ -119,6 +111,10 @@ function createSocket(events) { send({ method: 'player_vbox_reclaim', params: { instance_id: instanceId, index } }); } + function sendVboxInfo() { + send({ method: 'vbox_info', params: {} }); + } + function sendGameSkill(gameId, crypId, targetCrypId, skill) { send({ method: 'game_skill', @@ -178,6 +174,7 @@ function createSocket(events) { function accountInstanceList(res) { const [struct, playerList] = res; + sendVboxInfo(); events.setInstanceList(playerList); } @@ -228,9 +225,9 @@ function createSocket(events) { clearTimeout(instanceStateTimeout); } - function instanceScores(response) { - const [structName, scores] = response; - events.setScores(scores); + function vboxInfo(response) { + const [structName, info] = response; + events.setVboxInfo(info); } // ------------- @@ -241,18 +238,16 @@ function createSocket(events) { // this object wraps the reply types to a function const handlers = { cryp_spawn: crypSpawn, - cryp_forget: () => true, - cryp_learn: () => true, game_state: gameState, account_login: accountLogin, account_create: accountLogin, account_cryps: accountCryps, account_instances: accountInstanceList, - instance_scores: instanceScores, zone_create: res => console.log(res), zone_state: zoneState, zone_close: res => console.log(res), instance_state: instanceState, + vbox_info: vboxInfo, }; function logout() { @@ -346,8 +341,6 @@ function createSocket(events) { sendGameSkill, sendGameTarget, sendCrypSpawn, - sendCrypLearn, - sendCrypForget, sendSpecForget, sendZoneCreate, sendZoneJoin, @@ -364,6 +357,7 @@ function createSocket(events) { sendVboxCombine, sendVboxDiscard, sendVboxUnequip, + sendVboxInfo, startInstanceStateTimeout, startGameStateTimeout, connect, diff --git a/client/src/utils.jsx b/client/src/utils.jsx index 44cb24e3..1ce650e3 100644 --- a/client/src/utils.jsx +++ b/client/src/utils.jsx @@ -344,8 +344,23 @@ function getCombatText(cryp, resolution) { return ''; } +function convertVar(v) { + if (['Red', 'Green', 'Blue'].includes(v)) { + return ( + shapes.vboxColour(v.toLowerCase()) + ); + } + return v ||  ; + // uncomment for double borders in vbox; + // if (v) { + // return
{v}
; + // } + // return; +} + module.exports = { stringSort, + convertVar, numSort, genAvatar, crypAvatar, diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 47a8b989..a92669de 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -21,9 +21,9 @@ use account::{Account, account_create, account_login, account_from_token, accoun use skill::{Skill}; // use zone::{Zone, zone_create, zone_join, zone_close}; use spec::{Spec}; -use player::{Score, player_mm_cryps_set, Player}; +use player::{Score, player_mm_cryps_set}; use instance::{Instance, instance_state, instance_new, instance_ready, instance_join}; -use vbox::{Var, vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip}; +use vbox::{Var, VboxInfo, vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip, vbox_info}; pub struct Rpc; @@ -86,6 +86,8 @@ impl Rpc { "player_vbox_reclaim" => Rpc::player_vbox_reclaim(data, &mut tx, account.unwrap(), client), "player_vbox_unequip" => Rpc::player_vbox_unequip(data, &mut tx, account.unwrap(), client), + "vbox_info" => Ok(RpcResponse { method: "vbox_info".to_string(), params: RpcResult::VboxInfo(vbox_info()) }), + _ => Err(format_err!("unknown method - {:?}", v.method)), }; @@ -392,6 +394,7 @@ pub enum RpcResult { Account(Account), CrypList(Vec), GameState(Game), + VboxInfo(VboxInfo), InstanceScores(Vec<(String, Score)>), // ZoneState(Zone), // ZoneClose(()), diff --git a/server/src/vbox.rs b/server/src/vbox.rs index b0f4d93b..eff3a332 100644 --- a/server/src/vbox.rs +++ b/server/src/vbox.rs @@ -344,7 +344,8 @@ impl From for Var { } -struct Combo { +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct Combo { var: Var, units: Vec, } @@ -417,6 +418,50 @@ fn get_combos() -> Vec { return combinations; } +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct VarInfo { + pub v: Var, + pub spec: bool, + pub skill: bool, +} + + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct VboxInfo { + pub combos: Vec, + pub vars: Vec, +} + +pub fn vbox_info() -> VboxInfo { + let combos = get_combos(); + let mut vars = combos + .into_iter() + .flat_map(|mut c| { + c.units.push(c.var); + c.units + }) + .collect::>(); + + vars.sort_unstable(); + vars.dedup(); + + let vars = vars + .into_iter() + .map(|v| VarInfo { + v, + spec: v.into_spec().is_some(), + skill: v.into_skill().is_some(), + }) + .collect::>(); + + let combos = get_combos(); + + return VboxInfo { + combos, + vars, + }; +} + #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Vbox { pub bits: u16, @@ -632,4 +677,8 @@ mod tests { assert_eq!(count.red, 2); } + #[test] + fn vbox_info_test() { + println!("{:#?}", vbox_info()); + } } \ No newline at end of file