Merge branch 'release/1.6.0'
This commit is contained in:
commit
0b55484c14
34
CHANGELOG.md
34
CHANGELOG.md
@ -2,6 +2,40 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [1.6.0] - 2019-10-18
|
||||
### Added
|
||||
- Subscriber chat!
|
||||
|
||||
### Changed
|
||||
- Made available skill / effect information during the combat phase.
|
||||
- Highlighting a skill replace the effect area with the skill description including speed multiplier.
|
||||
- Highlighting an effect will replace the targetting arrow / anim skill text with effect info.
|
||||
- You can now preview combinations before you create them
|
||||
- After selecting the three items for a combo hover over the combine button for info
|
||||
|
||||
- Damage formula for Slay and Siphon reworked
|
||||
- Slay now deals red damage based RedPower and GreenPower. Previously only based on RedPower.
|
||||
- Siphon now deals blue damage based BluePower and GreenPower. Previously only based on BluePower.
|
||||
|
||||
### Fixed
|
||||
- Matchmaking bug where server matches you with yourself
|
||||
|
||||
## [1.5.6] - 2019-10-17
|
||||
We've updated the UI during the vbox / buy phase to give a better indication of valid actions.
|
||||
|
||||
### Changed
|
||||
- Borders for skill combo's represent the base colours.
|
||||
- Heal (GG) has a green border, Siphon (BG) has an alternating blue / green border etc.
|
||||
- Borders are shown for items in inventory and as equipped skills during both phases.
|
||||
|
||||
- Improvements to making item combo's
|
||||
- If you select an item in your inventory it will now highlight other items that are valid for combining.
|
||||
- This includes items that can be bought and in your inventory.
|
||||
|
||||
- Improved the indicator for where to click for equipping and buying items where its valid.
|
||||
- Now slowly flashes between black and grey, previously changed the border once.
|
||||
|
||||
|
||||
## [1.5.5] - 2019-10-15
|
||||
### Changed
|
||||
* Purge
|
||||
|
||||
10
WORKLOG.md
10
WORKLOG.md
@ -3,10 +3,7 @@
|
||||
|
||||
*PRODUCTION*
|
||||
|
||||
* border colours for skills e.g. strike red border, slay half red half green
|
||||
* rename vbox to shop
|
||||
* combat phase info system
|
||||
* drag and drop buy / equip / unequip items
|
||||
|
||||
* mobile styles
|
||||
* mobile info page
|
||||
@ -16,6 +13,11 @@
|
||||
* can't reset password without knowing password =\
|
||||
* Invert recharge
|
||||
|
||||
* serde serialize privatise
|
||||
* chat
|
||||
|
||||
* Convert spec 'Plus' -> '+' when it appears as combo text in combiner and in info text
|
||||
|
||||
## SOON
|
||||
|
||||
* equip from shop (buy and equip without putting in your inventory) for bases
|
||||
@ -23,7 +25,6 @@
|
||||
* bot game grind
|
||||
* ACP
|
||||
* essential
|
||||
* serde serialize privatise
|
||||
* msg pane / chatwheel
|
||||
* audio
|
||||
* treats
|
||||
@ -71,6 +72,7 @@ $$$
|
||||
* Highlight (dota) colour
|
||||
* fx colours + styles
|
||||
|
||||
* ??? (PROBS NOT) drag and drop buy / equip / unequip items ???
|
||||
* modules
|
||||
* troll life -> dmg
|
||||
* prince of peace
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mnml-client",
|
||||
"version": "1.5.6",
|
||||
"version": "1.6.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -132,10 +132,16 @@ aside {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
.ready, .quit {
|
||||
.ready {
|
||||
flex: 1;
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
.quit {
|
||||
flex: 1;
|
||||
font-size: 200%;
|
||||
animation: co 0.75s cubic-bezier(0, 0, 1, 1) 0s infinite alternate;
|
||||
}
|
||||
}
|
||||
|
||||
.abandon:not([disabled]) {
|
||||
|
||||
@ -3,6 +3,7 @@ footer {
|
||||
flex-flow: row wrap;
|
||||
grid-area: footer;
|
||||
margin: 0;
|
||||
z-index: 10;
|
||||
|
||||
button {
|
||||
margin: 0;
|
||||
|
||||
@ -123,7 +123,6 @@
|
||||
button {
|
||||
width: 100%;
|
||||
height: 2em;
|
||||
height: 25%;
|
||||
margin-right: 1em;
|
||||
}
|
||||
button.active {
|
||||
@ -204,6 +203,7 @@
|
||||
|
||||
.resolving-skill {
|
||||
grid-area: target;
|
||||
text-align: center;
|
||||
align-self: center;
|
||||
height: auto;
|
||||
svg {
|
||||
@ -213,6 +213,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
.skill-description {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
text-align: center;
|
||||
svg {
|
||||
display: inline;
|
||||
height: 1em;
|
||||
margin-right: 0.1em
|
||||
}
|
||||
}
|
||||
|
||||
/* some stupid bug in chrome makes it fill the entire screen */
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
#targeting {
|
||||
@ -396,6 +407,10 @@
|
||||
.skills button {
|
||||
font-size: 50%;
|
||||
}
|
||||
|
||||
.skill-description {
|
||||
font-size: 65%;
|
||||
}
|
||||
}
|
||||
|
||||
.player {
|
||||
|
||||
@ -45,5 +45,9 @@
|
||||
grid-area: msg;
|
||||
color: @white;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.chat {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ html body {
|
||||
-ms-user-select: none;
|
||||
|
||||
overflow-x: hidden;
|
||||
// overflow-y: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#mnml {
|
||||
@ -26,7 +26,7 @@ html body {
|
||||
|
||||
/* stops inspector going skitz*/
|
||||
overflow-x: hidden;
|
||||
// overflow-y: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
// @media (min-width: 1921px) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mnml-client",
|
||||
"version": "1.5.6",
|
||||
"version": "1.6.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -11,14 +11,22 @@ export const setAnimText = value => ({ type: 'SET_ANIM_TEXT', value });
|
||||
|
||||
export const setDemo = value => ({ type: 'SET_DEMO', value });
|
||||
|
||||
export const setChatShow = value => ({ type: 'SET_CHAT_SHOW', value });
|
||||
export const setChatWheel = value => ({ type: 'SET_CHAT_WHEEL', value });
|
||||
export const setInstanceChat = value => ({ type: 'SET_INSTANCE_CHAT', value });
|
||||
|
||||
export const setActiveItem = value => ({ type: 'SET_ACTIVE_VAR', value });
|
||||
export const setActiveSkill = (constructId, skill) => ({ type: 'SET_ACTIVE_SKILL', value: constructId ? { constructId, skill } : null });
|
||||
export const setCombiner = value => ({ type: 'SET_COMBINER', value: Array.from(value) });
|
||||
export const setConstructEditId = value => ({ type: 'SET_CONSTRUCT_EDIT_ID', value });
|
||||
export const setConstructs = value => ({ type: 'SET_CONSTRUCTS', value });
|
||||
export const setConstructRename = value => ({ type: 'SET_CONSTRUCT_RENAME', value });
|
||||
|
||||
export const setGame = value => ({ type: 'SET_GAME', value });
|
||||
export const setGameSkillInfo = value => ({ type: 'SET_GAME_SKILL_INFO', value });
|
||||
export const setGameEffectInfo = value => ({ type: 'SET_GAME_EFFECT_INFO', value });
|
||||
export const setInfo = value => ({ type: 'SET_INFO', value });
|
||||
|
||||
export const setEmail = value => ({ type: 'SET_EMAIL', value });
|
||||
export const setInvite = value => ({ type: 'SET_INVITE', value });
|
||||
export const setInstance = value => ({ type: 'SET_INSTANCE', value });
|
||||
|
||||
65
client/src/components/chat.jsx
Normal file
65
client/src/components/chat.jsx
Normal file
@ -0,0 +1,65 @@
|
||||
const preact = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const actions = require('../actions');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const {
|
||||
ws,
|
||||
chatShow,
|
||||
chatWheel,
|
||||
instance,
|
||||
game,
|
||||
} = state;
|
||||
|
||||
function sendInstanceChat(instance, i) {
|
||||
return ws.sendInstanceChat(instance, i);
|
||||
}
|
||||
|
||||
return {
|
||||
instance,
|
||||
game,
|
||||
chatShow,
|
||||
chatWheel,
|
||||
|
||||
sendInstanceChat,
|
||||
};
|
||||
},
|
||||
|
||||
function receiveDispatch(dispatch) {
|
||||
function setChatShow(v) {
|
||||
dispatch(actions.setChatShow(v));
|
||||
}
|
||||
|
||||
return {
|
||||
setChatShow,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
function Chat(args) {
|
||||
const {
|
||||
instance,
|
||||
game,
|
||||
chatShow,
|
||||
chatWheel,
|
||||
|
||||
sendInstanceChat,
|
||||
setChatShow,
|
||||
} = args;
|
||||
|
||||
function onClick(i) {
|
||||
sendInstanceChat(instance ? instance.id : game && game.id, i);
|
||||
setChatShow(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={`instance-ctrl-btns chat`}>
|
||||
{chatWheel.map((c, i) => <button key={i} onClick={() => onClick(i)} >{c}</button>)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = addState(Chat);
|
||||
@ -2,10 +2,13 @@ const { connect } = require('preact-redux');
|
||||
const { Component } = require('preact');
|
||||
const preact = require('preact');
|
||||
const range = require('lodash/range');
|
||||
const reactStringReplace = require('react-string-replace');
|
||||
|
||||
const { STATS } = require('../utils');
|
||||
const { ConstructAvatar, ConstructText } = require('./construct');
|
||||
const shapes = require('./shapes');
|
||||
const { INFO } = require('./../constants');
|
||||
const actions = require('../actions');
|
||||
|
||||
const SkillBtn = require('./skill.btn');
|
||||
|
||||
@ -19,6 +22,8 @@ const addState = connect(
|
||||
animFocus,
|
||||
animating,
|
||||
animText,
|
||||
gameSkillInfo,
|
||||
itemInfo,
|
||||
} = state;
|
||||
|
||||
function selectSkillTarget(targetConstructId) {
|
||||
@ -40,8 +45,19 @@ const addState = connect(
|
||||
animText,
|
||||
activeSkill,
|
||||
selectSkillTarget,
|
||||
gameSkillInfo,
|
||||
itemInfo,
|
||||
};
|
||||
},
|
||||
|
||||
function receiveDispatch(dispatch) {
|
||||
function setGameEffectInfo(info) {
|
||||
dispatch(actions.setGameEffectInfo(info));
|
||||
}
|
||||
|
||||
return { setGameEffectInfo };
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
const eventClasses = (animating, animFocus, construct, postSkill) => {
|
||||
@ -77,6 +93,10 @@ class GameConstruct extends Component {
|
||||
selectSkillTarget,
|
||||
animFocus,
|
||||
animText,
|
||||
|
||||
setGameEffectInfo,
|
||||
gameSkillInfo,
|
||||
itemInfo,
|
||||
} = this.props;
|
||||
|
||||
const ko = construct.green_life.value === 0 ? 'ko' : '';
|
||||
@ -96,9 +116,33 @@ class GameConstruct extends Component {
|
||||
let crypSkills = <div></div>;
|
||||
if (player) crypSkills = (<div class="skills"> {skills} </div>);
|
||||
|
||||
const effects = construct.effects.length
|
||||
? construct.effects.map(c => <div key={c.effect}>{c.effect} - {c.duration}T</div>)
|
||||
: null;
|
||||
function hoverInfo(e, info) {
|
||||
e.stopPropagation();
|
||||
return setGameEffectInfo(info);
|
||||
}
|
||||
const effectBox = () => {
|
||||
if (gameSkillInfo && gameSkillInfo.constructId === construct.id) {
|
||||
const fullInfo = itemInfo.items.find(k => k.item === gameSkillInfo.skill) || INFO[gameSkillInfo.skill];
|
||||
const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat)/;
|
||||
const infoDescription = reactStringReplace(fullInfo.description, regEx, match => shapes[match]());
|
||||
const speed = <div> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </div>;
|
||||
return (
|
||||
<div class="skill-description">
|
||||
<h2> {gameSkillInfo.skill} </h2>
|
||||
<div> {infoDescription} </div>
|
||||
{speed}
|
||||
</div>);
|
||||
}
|
||||
const effects = construct.effects.length
|
||||
? construct.effects.map(c =>
|
||||
<div
|
||||
key={c.effect}
|
||||
onMouseOver={e => hoverInfo(e, c)}
|
||||
onMouseOut={e => hoverInfo(e, null)}
|
||||
> {c.effect} - {c.duration}T</div>)
|
||||
: null;
|
||||
return (<div class="effects"> {effects} </div>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -107,7 +151,7 @@ class GameConstruct extends Component {
|
||||
class={`game-construct ${ko} ${classes}`} >
|
||||
<div class="left">
|
||||
{crypSkills}
|
||||
<div class="effects"> {effects} </div>
|
||||
{effectBox()}
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="stats"> {stats} </div>
|
||||
|
||||
@ -8,6 +8,8 @@ const addState = connect(
|
||||
const {
|
||||
ws,
|
||||
game,
|
||||
account,
|
||||
chatShow,
|
||||
animating,
|
||||
} = state;
|
||||
|
||||
@ -30,6 +32,8 @@ const addState = connect(
|
||||
|
||||
return {
|
||||
game,
|
||||
account,
|
||||
chatShow,
|
||||
sendAbandon,
|
||||
sendGameSkillClear,
|
||||
sendReady,
|
||||
@ -45,7 +49,14 @@ const addState = connect(
|
||||
dispatch(actions.setInstance(null));
|
||||
}
|
||||
|
||||
return { quit };
|
||||
function setChatShow(v) {
|
||||
dispatch(actions.setChatShow(v));
|
||||
}
|
||||
|
||||
return {
|
||||
setChatShow,
|
||||
quit,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@ -53,10 +64,13 @@ function GameCtrlBtns(args) {
|
||||
const {
|
||||
game,
|
||||
animating,
|
||||
account,
|
||||
chatShow,
|
||||
|
||||
getInstanceState,
|
||||
sendGameSkillClear,
|
||||
sendReady,
|
||||
setChatShow,
|
||||
quit,
|
||||
} = args;
|
||||
|
||||
@ -73,7 +87,7 @@ function GameCtrlBtns(args) {
|
||||
|
||||
return (
|
||||
<div class="game-ctrl-btns">
|
||||
<button disabled={true} >Chat</button>
|
||||
<button disabled={!account.subscribed} onClick={() => setChatShow(!chatShow)}>Chat</button>
|
||||
<button disabled={animating} onClick={sendGameSkillClear}>Clear</button>
|
||||
{finished ? quitBtn : readyBtn}
|
||||
</div>
|
||||
|
||||
@ -4,6 +4,7 @@ const { connect } = require('preact-redux');
|
||||
const actions = require('../actions');
|
||||
|
||||
const PlayerBox = require('./player.box');
|
||||
const Chat = require('./chat');
|
||||
const GameCtrlButtons = require('./game.ctrl.btns');
|
||||
const GameCtrlTopButtons = require('./game.ctrl.btns.top');
|
||||
|
||||
@ -13,12 +14,16 @@ const addState = connect(
|
||||
animating,
|
||||
game,
|
||||
account,
|
||||
chatShow,
|
||||
instanceChat,
|
||||
} = state;
|
||||
|
||||
return {
|
||||
animating,
|
||||
game,
|
||||
account,
|
||||
chatShow,
|
||||
instanceChat,
|
||||
};
|
||||
},
|
||||
);
|
||||
@ -28,6 +33,8 @@ function Controls(args) {
|
||||
animating,
|
||||
account,
|
||||
game,
|
||||
chatShow,
|
||||
instanceChat,
|
||||
} = args;
|
||||
|
||||
if (!game) return false;
|
||||
@ -61,13 +68,17 @@ function Controls(args) {
|
||||
</div>
|
||||
);
|
||||
|
||||
const bottom = chatShow
|
||||
? <Chat />
|
||||
: <PlayerBox player={player} isPlayer={true} chat={instanceChat && instanceChat[player.id]} />;
|
||||
|
||||
return (
|
||||
<aside>
|
||||
{timer}
|
||||
<div class="controls instance-ctrl">
|
||||
<GameCtrlTopButtons />
|
||||
<PlayerBox player={opponent}/>
|
||||
<PlayerBox player={player} isPlayer={true} />
|
||||
<PlayerBox player={opponent} chat={instanceChat && instanceChat[opponent.id]}/>
|
||||
{bottom}
|
||||
<GameCtrlButtons />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@ -7,7 +7,9 @@ const addState = connect(
|
||||
function receiveState(state) {
|
||||
const {
|
||||
ws,
|
||||
chatShow,
|
||||
instance,
|
||||
account,
|
||||
} = state;
|
||||
|
||||
function sendReady() {
|
||||
@ -21,19 +23,34 @@ const addState = connect(
|
||||
|
||||
return {
|
||||
instance,
|
||||
chatShow,
|
||||
account,
|
||||
|
||||
sendAbandon,
|
||||
sendReady,
|
||||
};
|
||||
},
|
||||
|
||||
function receiveDispatch(dispatch) {
|
||||
function setChatShow(v) {
|
||||
dispatch(actions.setChatShow(v));
|
||||
}
|
||||
|
||||
return {
|
||||
setChatShow,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
function InstanceCtrlBtns(args) {
|
||||
const {
|
||||
instance,
|
||||
chatShow,
|
||||
account,
|
||||
|
||||
sendAbandon,
|
||||
sendReady,
|
||||
setChatShow,
|
||||
} = args;
|
||||
|
||||
const finished = instance && instance.phase === 'Finished';
|
||||
@ -49,7 +66,7 @@ function InstanceCtrlBtns(args) {
|
||||
|
||||
return (
|
||||
<div class="instance-ctrl-btns">
|
||||
<button disabled={true} >Chat</button>
|
||||
<button disabled={!account.subscribed} onClick={() => setChatShow(!chatShow)}>Chat</button>
|
||||
<button disabled={finished} class="ready" onClick={() => sendReady()}>Ready</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,21 +1,27 @@
|
||||
const preact = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const actions = require('../actions');
|
||||
const PlayerBox = require('./player.box');
|
||||
const Chat = require('./chat');
|
||||
const InstanceCtrlBtns = require('./instance.ctrl.btns');
|
||||
const InstanceCtrlTopBtns = require('./instance.ctrl.top.btns');
|
||||
|
||||
const actions = require('../actions');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const {
|
||||
ws,
|
||||
instance,
|
||||
instanceChat,
|
||||
account,
|
||||
chatShow,
|
||||
} = state;
|
||||
|
||||
return {
|
||||
chatShow,
|
||||
instance,
|
||||
instanceChat,
|
||||
account,
|
||||
};
|
||||
},
|
||||
@ -25,6 +31,8 @@ function Controls(args) {
|
||||
const {
|
||||
account,
|
||||
instance,
|
||||
instanceChat,
|
||||
chatShow,
|
||||
} = args;
|
||||
|
||||
if (!instance) return false;
|
||||
@ -58,13 +66,17 @@ function Controls(args) {
|
||||
</div>
|
||||
);
|
||||
|
||||
const bottom = chatShow
|
||||
? <Chat />
|
||||
: <PlayerBox player={player} isPlayer={true} chat={instanceChat && instanceChat[player.id]} />;
|
||||
|
||||
return (
|
||||
<aside>
|
||||
{timer}
|
||||
<div class="controls instance-ctrl">
|
||||
<InstanceCtrlTopBtns />
|
||||
<PlayerBox player={opponent} />
|
||||
<PlayerBox player={player} isPlayer={true} />
|
||||
<PlayerBox player={opponent} chat={instanceChat && instanceChat[opponent.id]}/>
|
||||
{bottom}
|
||||
<InstanceCtrlBtns />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@ -2,7 +2,7 @@ const preact = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const Main = require('./main');
|
||||
const Nav = require('./nav');
|
||||
// const Nav = require('./nav');
|
||||
const Controls = require('./controls');
|
||||
const Footer = require('./footer');
|
||||
|
||||
@ -12,7 +12,6 @@ const addState = connect(
|
||||
|
||||
const Mnml = ({ showNav }) =>
|
||||
<div id="mnml" class={showNav ? 'nav-visible' : ''}>
|
||||
<Nav />
|
||||
<Main />
|
||||
<Controls />
|
||||
<Footer />
|
||||
|
||||
@ -56,6 +56,7 @@ function Scoreboard(args) {
|
||||
const {
|
||||
isPlayer,
|
||||
player,
|
||||
chat,
|
||||
} = args;
|
||||
|
||||
const scoreText = () => {
|
||||
@ -73,14 +74,14 @@ function Scoreboard(args) {
|
||||
<div class="score">{scoreText()}</div>
|
||||
<div class="name">{player.name}</div>
|
||||
<Img img={player.img} id={player.id} />
|
||||
<div class="msg"> </div>
|
||||
<div class="msg">{chat || '\u00A0'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={`player-box bottom ${player.ready ? 'ready' : ''}`}>
|
||||
<div class="msg"> </div>
|
||||
<div class="msg">{chat || '\u00A0'}</div>
|
||||
<div class="score">{scoreText()}</div>
|
||||
<div class="name">{player.name}</div>
|
||||
<Img img={player.img} id={player.id} />
|
||||
|
||||
@ -23,18 +23,24 @@ const addState = connect(
|
||||
dispatch(actions.setActiveSkill(constructId, skill));
|
||||
}
|
||||
|
||||
return { setActiveSkill };
|
||||
function setGameSkillInfo(info) {
|
||||
dispatch(actions.setGameSkillInfo(info));
|
||||
}
|
||||
|
||||
return { setActiveSkill, setGameSkillInfo };
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
function Skill(props) {
|
||||
const {
|
||||
animating,
|
||||
construct,
|
||||
game,
|
||||
i,
|
||||
activeSkill,
|
||||
setActiveSkill,
|
||||
setGameSkillInfo,
|
||||
} = props;
|
||||
|
||||
if (!game) return false;
|
||||
@ -42,6 +48,11 @@ function Skill(props) {
|
||||
const s = construct.skills[i];
|
||||
const ko = construct.green_life.value === 0 ? 'ko' : '';
|
||||
|
||||
function hoverInfo(e, info) {
|
||||
e.stopPropagation();
|
||||
if (animating) return false;
|
||||
return setGameSkillInfo(info);
|
||||
}
|
||||
if (!s || !game) {
|
||||
return (
|
||||
<button
|
||||
@ -76,6 +87,8 @@ function Skill(props) {
|
||||
<button
|
||||
disabled={cdText || s.disabled || ko}
|
||||
class={`${(targeting || highlight) ? 'active' : ''} ${border}`}
|
||||
onMouseOver={e => hoverInfo(e, { skill: s.skill, constructId: construct.id })}
|
||||
onMouseOut={e => hoverInfo(e, null)}
|
||||
type="submit"
|
||||
onClick={onClick}>
|
||||
{s.skill} {cdText}
|
||||
|
||||
@ -6,11 +6,11 @@ const reactStringReplace = require('react-string-replace');
|
||||
const throttle = require('lodash/throttle');
|
||||
|
||||
const shapes = require('./shapes');
|
||||
const { removeTier } = require('../utils');
|
||||
const { effectInfo, removeTier } = require('../utils');
|
||||
|
||||
const addState = connect(
|
||||
({ game, account, animSkill, animating, itemInfo }) =>
|
||||
({ game, account, animSkill, animating, itemInfo })
|
||||
({ game, account, animSkill, animating, itemInfo, gameEffectInfo }) =>
|
||||
({ game, account, animSkill, animating, itemInfo, gameEffectInfo })
|
||||
);
|
||||
|
||||
class TargetSvg extends Component {
|
||||
@ -28,10 +28,24 @@ class TargetSvg extends Component {
|
||||
}
|
||||
|
||||
render(props, state) {
|
||||
const { game, account, animating, animSkill, itemInfo } = props;
|
||||
const { game, account, animating, animSkill, itemInfo, gameEffectInfo } = props;
|
||||
const { width, height } = state;
|
||||
if (!game) return false; // game will be null when battle ends
|
||||
|
||||
// Whenever someones looking at effects throw it up here
|
||||
if (gameEffectInfo) {
|
||||
const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat)/;
|
||||
const infoString = effectInfo(gameEffectInfo);
|
||||
const infoDescription = reactStringReplace(infoString, regEx, match => shapes[match]());
|
||||
|
||||
return (
|
||||
<div class="resolving-skill">
|
||||
<h1>{gameEffectInfo.effect}</h1>
|
||||
<div> {infoDescription} </div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// resolutions happening
|
||||
// just put skill name up
|
||||
if (animating) {
|
||||
|
||||
@ -20,6 +20,7 @@ const addState = connect(
|
||||
itemInfo,
|
||||
itemUnequip,
|
||||
navInstance,
|
||||
info,
|
||||
} = state;
|
||||
|
||||
function sendVboxDiscard() {
|
||||
@ -56,6 +57,7 @@ const addState = connect(
|
||||
itemUnequip,
|
||||
sendItemUnequip,
|
||||
navInstance,
|
||||
info,
|
||||
};
|
||||
},
|
||||
|
||||
@ -115,6 +117,7 @@ function Vbox(args) {
|
||||
sendItemUnequip,
|
||||
|
||||
setReclaiming,
|
||||
info,
|
||||
} = args;
|
||||
|
||||
if (!player) return false;
|
||||
@ -138,7 +141,7 @@ function Vbox(args) {
|
||||
//
|
||||
function vboxHover(e, v) {
|
||||
if (v) {
|
||||
setInfo(v);
|
||||
if (info !== v) setInfo(v);
|
||||
e.stopPropagation();
|
||||
}
|
||||
return true;
|
||||
@ -329,7 +332,7 @@ function Vbox(args) {
|
||||
|
||||
function combinerBtn() {
|
||||
let text = '';
|
||||
|
||||
let comboItem = '';
|
||||
if (combiner.length < 3) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (combiner.length > i) {
|
||||
@ -339,14 +342,18 @@ function Vbox(args) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
text = 'combine';
|
||||
// Since theres 3 items in combiner and you can't have invalid combos we can preview it
|
||||
const combinerItems = combiner.map(j => vbox.bound[j]);
|
||||
const comboItemObj = itemInfo.combos.find(combo => combinerItems.every(c => combo.components.includes(c)));
|
||||
comboItem = comboItemObj ? comboItemObj.item : 'refine';
|
||||
text = `Combine - ${comboItem}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
class='vbox-btn'
|
||||
disabled={combiner.length !== 3}
|
||||
onMouseOver={e => hoverInfo(e, 'refine')}
|
||||
onMouseOver={e => hoverInfo(e, comboItem)}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onMouseDown={() => sendVboxCombine()}>
|
||||
{text}
|
||||
@ -390,9 +397,10 @@ function Vbox(args) {
|
||||
//
|
||||
// EVERYTHING
|
||||
//
|
||||
function hoverInfo(e, info) {
|
||||
function hoverInfo(e, newInfo) {
|
||||
e.stopPropagation();
|
||||
return setInfo(info);
|
||||
if (info === newInfo) return true;
|
||||
return setInfo(newInfo);
|
||||
}
|
||||
|
||||
const classes = `vbox ${navInstance === 0 ? 'visible' : ''}`;
|
||||
|
||||
@ -109,7 +109,7 @@ function registerEvents(store) {
|
||||
store.dispatch(actions.setAnimTarget(null));
|
||||
store.dispatch(actions.setAnimText(null));
|
||||
store.dispatch(actions.setAnimating(false));
|
||||
|
||||
store.dispatch(actions.setGameEffectInfo(null));
|
||||
store.dispatch(actions.setSkip(false));
|
||||
|
||||
// set the game state so resolutions don't fire twice
|
||||
@ -212,6 +212,14 @@ function registerEvents(store) {
|
||||
return store.dispatch(actions.setInstance(v));
|
||||
}
|
||||
|
||||
function setInstanceChat(v) {
|
||||
return store.dispatch(actions.setInstanceChat(v));
|
||||
}
|
||||
|
||||
function setChatWheel(v) {
|
||||
return store.dispatch(actions.setChatWheel(v));
|
||||
}
|
||||
|
||||
function setItemInfo(v) {
|
||||
return store.dispatch(actions.setItemInfo(v));
|
||||
}
|
||||
@ -318,12 +326,14 @@ function registerEvents(store) {
|
||||
setAccountInstances,
|
||||
setActiveItem,
|
||||
setActiveSkill,
|
||||
setChatWheel,
|
||||
setDemo,
|
||||
setConstructList,
|
||||
setNewConstruct,
|
||||
setGame,
|
||||
setEmail,
|
||||
setInstance,
|
||||
setInstanceChat,
|
||||
setItemInfo,
|
||||
setInvite,
|
||||
setPing,
|
||||
|
||||
@ -24,15 +24,21 @@ module.exports = {
|
||||
|
||||
demo: createReducer(null, 'SET_DEMO'),
|
||||
|
||||
chatShow: createReducer(null, 'SET_CHAT_SHOW'),
|
||||
chatWheel: createReducer([], 'SET_CHAT_WHEEL'),
|
||||
|
||||
combiner: createReducer([], 'SET_COMBINER'),
|
||||
constructs: createReducer([], 'SET_CONSTRUCTS'),
|
||||
constructEditId: createReducer(null, 'SET_CONSTRUCT_EDIT_ID'),
|
||||
constructRename: createReducer(null, 'SET_CONSTRUCT_RENAME'),
|
||||
game: createReducer(null, 'SET_GAME'),
|
||||
gameSkillInfo: createReducer(null, 'SET_GAME_SKILL_INFO'),
|
||||
gameEffectInfo: createReducer(null, 'SET_GAME_EFFECT_INFO'),
|
||||
email: createReducer(null, 'SET_EMAIL'),
|
||||
invite: createReducer(null, 'SET_INVITE'),
|
||||
info: createReducer(null, 'SET_INFO'),
|
||||
instance: createReducer(null, 'SET_INSTANCE'),
|
||||
instanceChat: createReducer(null, 'SET_INSTANCE_CHAT'),
|
||||
instances: createReducer([], 'SET_INSTANCES'),
|
||||
itemEquip: createReducer(null, 'SET_ITEM_EQUIP'),
|
||||
itemInfo: createReducer({ combos: [], items: [] }, 'SET_ITEM_INFO'),
|
||||
|
||||
@ -67,6 +67,10 @@ function createSocket(events) {
|
||||
send(['InstanceState', { instance_id: instanceId }]);
|
||||
}
|
||||
|
||||
function sendInstanceChat(instanceId, index) {
|
||||
send(['InstanceChat', { instance_id: instanceId, index }]);
|
||||
}
|
||||
|
||||
function sendVboxAccept(instanceId, group, index) {
|
||||
send(['VboxAccept', { instance_id: instanceId, group, index }]);
|
||||
events.clearInstance();
|
||||
@ -253,8 +257,11 @@ function createSocket(events) {
|
||||
QueueJoined: () => events.notify('you have joined the pvp queue'),
|
||||
InviteRequested: () => events.notify('pvp queue request received'),
|
||||
Invite: code => events.setInvite(code),
|
||||
InstanceChat: chat => events.setInstanceChat(chat),
|
||||
ChatWheel: wheel => events.setChatWheel(wheel),
|
||||
Joining: () => events.notify('searching for instance...'),
|
||||
|
||||
Processing: () => true,
|
||||
Error: errHandler,
|
||||
};
|
||||
|
||||
@ -358,6 +365,7 @@ function createSocket(events) {
|
||||
sendInstanceState,
|
||||
sendInstanceInvite,
|
||||
sendInstanceJoin,
|
||||
sendInstanceChat,
|
||||
|
||||
sendVboxAccept,
|
||||
sendVboxApply,
|
||||
|
||||
@ -237,6 +237,64 @@ function convertItem(v) {
|
||||
// return;
|
||||
}
|
||||
|
||||
function effectInfo(i) {
|
||||
console.log(i);
|
||||
|
||||
function multiplier(s) { // Update later to use server info in future
|
||||
if (s === 'CounterAttack') return 70;
|
||||
if (s === 'CounterAttack+') return 95;
|
||||
if (s === 'CounterAttack++') return 120;
|
||||
|
||||
if (s === 'DecayTick') return 33;
|
||||
if (s === 'DecayTick+') return 45;
|
||||
if (s === 'DecayTick++') return 70;
|
||||
|
||||
if (s === 'SiphonTick') return 20;
|
||||
if (s === 'SiphonTick+') return 25;
|
||||
if (s === 'SiphonTick++') return 30;
|
||||
|
||||
if (s === 'TriageTick') return 75;
|
||||
if (s === 'TriageTick+') return 110;
|
||||
if (s === 'TriageTick++') return 140;
|
||||
|
||||
if (s === 'Electrocute' || s === 'ElectrocuteTick') return 80;
|
||||
if (s === 'Electrocute+' || s === 'ElectrocuteTick+') return 100;
|
||||
if (s === 'Electrocute++' || s === 'ElectrocuteTick++') return 130;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (i.effect) {
|
||||
case 'Amplify': return `Increases construct RedPower and BluePower by ${i.meta[1] - 100}%`;
|
||||
case 'Banish': return 'Banished construct cannot cast or take damage';
|
||||
case 'Block': return `Reduces construct red damage taken by ${100 - i.meta[1]}%`;
|
||||
case 'Buff': return `Increases construct RedPower and SpeedStat by ${i.meta[1] - 100}%`;
|
||||
case 'Sustain': return 'Construct cannot be KO while active. Additionally provides immunity to disables';
|
||||
case 'Curse': return `Construct will take ${i.meta[1] - 100}% increased red and blue damage`;
|
||||
case 'Haste': return `Construct has ${i.meta[1] - 100}% increased SpeedStat. Red attack skills will trigger a HasteStrike dealing 30% SpeedStat as red damage.`;
|
||||
case 'Hybrid': return `Construct has ${i.meta[1] - 100}% increased GreenPower. Blue attack skills will trigger a HybridBlast dealing 25% GreenPower as red damage.`;
|
||||
case 'Invert': return 'Reverses damage and healing. Healing will damage this construct and damage will heal.';
|
||||
case 'Counter': return `Red damage taken by this construct will trigger a CounterAttack. CounterAttack deals ${multiplier(i.meta[1])}% RedPower as red damage.`;
|
||||
case 'Purge': return 'Disable construct from casting any green skills';
|
||||
case 'Reflect': return 'Reflect blue skills back to caster';
|
||||
case 'Slow': return `Reduces construct SpeedStat by ${100 - i.meta[1]}%`;
|
||||
case 'Restrict': return 'Disable construct from casting any red skills';
|
||||
case 'Stun': return 'Stunned construct cannot use skills';
|
||||
case 'Intercept': return 'Redirect any skills on team to this target construct';
|
||||
case 'Vulnerable': return `Construct will take ${i.meta[1] - 100}% increased red damage`;
|
||||
case 'Silence': return 'Disable construct from casting any blue skills';
|
||||
case 'Wither': return `Construct will take ${100 - i.meta[1]}% reduced healing`; //
|
||||
case 'Decay': return `Construct will take ${multiplier(i.tick.skill)}% of caster's BluePower as blue damage each turn.`; //
|
||||
case 'Electric': return `Attacks against this construct will apply Electrocute dealing ${multiplier(i.meta[1])}% of construct BluePower as blue damage each turn.`;
|
||||
case 'Electrocute': return `Construct will take ${multiplier(i.tick.skill)}% of caster's BluePower as blue damage each turn.`;
|
||||
case 'Absorb': return 'If construct takes damage, Absorption will be applied increasing RedPower and BluePower based on damage taken.';
|
||||
case 'Absorption': return `Increasing construct RedPower and BluePower by ${i.meta[1]}`;
|
||||
case 'Triage': return `Construct will be healed for ${multiplier(i.tick.skill)}% of caster's GreenPower each turn.`;
|
||||
case 'Siphon': return `Construct will take ${multiplier(i.tick.skill)}% of caster's BluePower + GreenPower as blue damage each turn, healing the caster.`;
|
||||
|
||||
default: return 'Missing Effect Text';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
stringSort,
|
||||
numSort,
|
||||
@ -251,4 +309,5 @@ module.exports = {
|
||||
randomPoints,
|
||||
removeTier,
|
||||
match,
|
||||
effectInfo,
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mnml-ops",
|
||||
"version": "1.5.6",
|
||||
"version": "1.6.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mnml"
|
||||
version = "1.5.6"
|
||||
version = "1.6.0"
|
||||
authors = ["ntr <ntr@smokestack.io>"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -65,6 +65,19 @@ pub fn select(db: &Db, id: Uuid) -> Result<Account, Error> {
|
||||
Account::try_from(row)
|
||||
}
|
||||
|
||||
pub fn chat_wheel(_db: &Db, _id: Uuid) -> Result<Vec<String>, Error> {
|
||||
return Ok(vec![
|
||||
"gl".to_string(),
|
||||
"hf".to_string(),
|
||||
"gg".to_string(),
|
||||
"thx".to_string(),
|
||||
"nice".to_string(),
|
||||
"hmm".to_string(),
|
||||
"ok".to_string(),
|
||||
"...".to_string(),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn select_name(db: &Db, name: &String) -> Result<Account, Error> {
|
||||
let query = "
|
||||
SELECT id, name, balance, subscribed, img
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::thread::{spawn, sleep};
|
||||
use std::time;
|
||||
|
||||
// Db Commons
|
||||
use uuid::Uuid;
|
||||
@ -58,6 +60,9 @@ pub enum Event {
|
||||
Invite(Id),
|
||||
Join(Id, String),
|
||||
Joined(Id),
|
||||
|
||||
Chat(Id, Uuid, String),
|
||||
ChatClear(Id, Uuid),
|
||||
}
|
||||
|
||||
struct WsClient {
|
||||
@ -65,6 +70,7 @@ struct WsClient {
|
||||
account: Option<Uuid>,
|
||||
tx: Sender<RpcMessage>,
|
||||
subs: HashSet<Uuid>,
|
||||
chat: Option<(Uuid, String)>,
|
||||
pvp: bool,
|
||||
invite: Option<String>,
|
||||
}
|
||||
@ -120,7 +126,15 @@ impl Events {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let client = WsClient { id, tx, account: account_id, subs: HashSet::new(), pvp: false, invite: None };
|
||||
let client = WsClient { id,
|
||||
tx,
|
||||
account: account_id,
|
||||
subs: HashSet::new(),
|
||||
pvp: false,
|
||||
invite: None,
|
||||
chat: None,
|
||||
};
|
||||
|
||||
self.clients.insert(id, client);
|
||||
|
||||
info!("clients={:?}", self.clients.len());
|
||||
@ -171,7 +185,17 @@ impl Events {
|
||||
for (client_id, client) in self.clients.iter() {
|
||||
if client.subs.contains(&id) {
|
||||
subs += 1;
|
||||
match client.tx.send(msg.clone()) {
|
||||
|
||||
let redacted = match client.account {
|
||||
Some(a) => match msg {
|
||||
RpcMessage::InstanceState(ref i) => RpcMessage::InstanceState(i.clone().redact(a)),
|
||||
RpcMessage::GameState(ref i) => RpcMessage::GameState(i.clone().redact(a)),
|
||||
_ => msg.clone(),
|
||||
}
|
||||
None => msg.clone(),
|
||||
};
|
||||
|
||||
match client.tx.send(redacted) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
warn!("unable to send msg to client err={:?}", e);
|
||||
@ -205,7 +229,7 @@ impl Events {
|
||||
}
|
||||
|
||||
// create the req for the already queued opponent
|
||||
if let Some(opp_req) = match self.clients.iter_mut().find(|(_c_id, c)| c.pvp) {
|
||||
if let Some(opp_req) = match self.clients.iter_mut().find(|(c_id, c)| c.pvp && **c_id != id) {
|
||||
Some((q_id, q)) => {
|
||||
q.pvp = false;
|
||||
Some(PvpRequest { id: *q_id, account: q.account.unwrap(), tx: q.tx.clone() })
|
||||
@ -281,6 +305,58 @@ impl Events {
|
||||
return Ok(());
|
||||
},
|
||||
|
||||
Event::Chat(id, instance, msg) => {
|
||||
// set the chat state of this connection
|
||||
{
|
||||
let c = self.clients.get_mut(&id)
|
||||
.ok_or(format_err!("connection not found id={:?}", id))?;
|
||||
|
||||
if c.chat.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
c.chat = Some((instance, msg));
|
||||
|
||||
let events_tx = self.tx.clone();
|
||||
spawn(move || {
|
||||
sleep(time::Duration::from_secs(3));
|
||||
events_tx.send(Event::ChatClear(id, instance)).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
// now collect all listeners of this instance
|
||||
|
||||
let chat_state: HashMap<Uuid, String> = self.clients.iter()
|
||||
.filter(|(_id, c)| c.account.is_some())
|
||||
.filter(|(_id, c)| match c.chat {
|
||||
Some(ref chat) => chat.0 == instance,
|
||||
None => false,
|
||||
})
|
||||
.map(|(_id, c)| (c.account.unwrap(), c.chat.clone().unwrap().1))
|
||||
.collect();
|
||||
|
||||
return self.event(Event::Push(instance, RpcMessage::InstanceChat(chat_state)));
|
||||
},
|
||||
|
||||
Event::ChatClear(id, instance) => {
|
||||
{
|
||||
match self.clients.get_mut(&id) {
|
||||
Some(c) => c.chat = None,
|
||||
None => (),
|
||||
};
|
||||
}
|
||||
|
||||
let chat_state: HashMap<Uuid, String> = self.clients.iter()
|
||||
.filter(|(_id, c)| c.account.is_some())
|
||||
.filter(|(_id, c)| match c.chat {
|
||||
Some(ref chat) => chat.0 == instance,
|
||||
None => false,
|
||||
})
|
||||
.map(|(_id, c)| (c.account.unwrap(), c.chat.clone().unwrap().1))
|
||||
.collect();
|
||||
|
||||
return self.event(Event::Push(instance, RpcMessage::InstanceChat(chat_state)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,6 +62,17 @@ impl Game {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn redact(mut self, account: Uuid) -> Game {
|
||||
self.players = self.players.into_iter()
|
||||
.map(|p| p.redact(account))
|
||||
.collect();
|
||||
|
||||
self.stack
|
||||
.retain(|s| s.source_player_id == account);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_time_control(&mut self, tc: TimeControl) -> &mut Game {
|
||||
self.time_control = tc;
|
||||
self.phase_end = Some(tc.lobby_timeout());
|
||||
@ -631,8 +642,8 @@ pub fn game_write(tx: &mut Transaction, game: &Game) -> Result<(), Error> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn game_state(tx: &mut Transaction, _account: &Account, id: Uuid) -> Result<Game, Error> {
|
||||
return game_get(tx, id)
|
||||
pub fn game_state(tx: &mut Transaction, account: &Account, id: Uuid) -> Result<Game, Error> {
|
||||
Ok(game_get(tx, id)?.redact(account.id))
|
||||
}
|
||||
|
||||
pub fn game_get(tx: &mut Transaction, id: Uuid) -> Result<Game, Error> {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use std::fs::File;
|
||||
use std::collections::{HashMap};
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -31,6 +32,8 @@ enum InstancePhase {
|
||||
Finished,
|
||||
}
|
||||
|
||||
pub type ChatState = HashMap<Uuid, String>;
|
||||
|
||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||
struct Round {
|
||||
game_id: Option<Uuid>,
|
||||
@ -126,6 +129,14 @@ impl Instance {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redact(mut self, account: Uuid) -> Instance {
|
||||
self.players = self.players.into_iter()
|
||||
.map(|p| p.redact(account))
|
||||
.collect();
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn phase_timed_out(&self) -> bool {
|
||||
match self.phase_end {
|
||||
Some(t) => Utc::now().signed_duration_since(t).num_milliseconds() > 0,
|
||||
|
||||
@ -850,7 +850,8 @@ impl Item {
|
||||
Item::Slay|
|
||||
Item::SlayPlus |
|
||||
Item::SlayPlusPlus => format!(
|
||||
"Deals {:?}% RedPower as red damage and provides self healing based on damage dealt.",
|
||||
"Deals {:?}% RedPower + {:?}% GreenPower as red damage and provides self healing based on damage dealt.",
|
||||
self.into_skill().unwrap().multiplier(),
|
||||
self.into_skill().unwrap().multiplier()),
|
||||
|
||||
Item::Sleep|
|
||||
@ -884,7 +885,8 @@ impl Item {
|
||||
Item::Siphon|
|
||||
Item::SiphonPlus |
|
||||
Item::SiphonPlusPlus => format!(
|
||||
"Deals {:?}% BluePower as blue damage each turn and heals caster based on damage dealt. Lasts {:?}T.",
|
||||
"Deals {:?}% BluePower + {:?}% GreenPower as blue damage each turn and heals caster based on damage dealt. Lasts {:?}T.",
|
||||
self.into_skill().unwrap().effect()[0].get_skill().unwrap().multiplier(),
|
||||
self.into_skill().unwrap().effect()[0].get_skill().unwrap().multiplier(),
|
||||
self.into_skill().unwrap().effect()[0].get_duration()),
|
||||
|
||||
|
||||
@ -103,6 +103,24 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redact(mut self, account: Uuid) -> Player {
|
||||
// all g
|
||||
if account == self.id {
|
||||
return self;
|
||||
}
|
||||
|
||||
// remove vbox
|
||||
self.vbox = Vbox::new();
|
||||
|
||||
// hide skills
|
||||
for construct in self.constructs.iter_mut() {
|
||||
construct.skills = vec![];
|
||||
construct.specs = vec![];
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_bot(mut self, bot: bool) -> Player {
|
||||
self.bot = bot;
|
||||
self
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use std::time::{Instant};
|
||||
use std::thread::spawn;
|
||||
use std::thread::{spawn, sleep};
|
||||
use std::time;
|
||||
|
||||
use std::str;
|
||||
|
||||
use uuid::Uuid;
|
||||
@ -21,7 +23,7 @@ use account;
|
||||
use construct::{Construct};
|
||||
use events::{Event};
|
||||
use game::{Game, game_state, game_skill, game_skill_clear, game_ready};
|
||||
use instance::{Instance, instance_state, instance_practice, instance_ready, instance_abandon, demo};
|
||||
use instance::{Instance, ChatState, instance_state, instance_practice, instance_ready, instance_abandon, demo};
|
||||
use item::{Item, ItemInfoCtr, item_info};
|
||||
use mtx;
|
||||
use mail;
|
||||
@ -50,6 +52,8 @@ pub enum RpcMessage {
|
||||
ItemInfo(ItemInfoCtr),
|
||||
|
||||
InstanceState(Instance),
|
||||
InstanceChat(ChatState),
|
||||
ChatWheel(Vec<String>),
|
||||
|
||||
EmailState(Option<Email>),
|
||||
SubscriptionState(Option<Subscription>),
|
||||
@ -66,6 +70,8 @@ pub enum RpcMessage {
|
||||
Invite(String),
|
||||
Joining(()),
|
||||
|
||||
Processing(()),
|
||||
|
||||
Error(String),
|
||||
}
|
||||
|
||||
@ -102,6 +108,7 @@ pub enum RpcRequest {
|
||||
InstanceAbandon { instance_id: Uuid },
|
||||
InstanceReady { instance_id: Uuid },
|
||||
InstanceState { instance_id: Uuid },
|
||||
InstanceChat { instance_id: Uuid, index: usize },
|
||||
|
||||
VboxAccept { instance_id: Uuid, group: usize, index: usize },
|
||||
VboxDiscard { instance_id: Uuid },
|
||||
@ -158,6 +165,22 @@ impl Connection {
|
||||
self.events.send(Event::Join(self.id, code))?;
|
||||
Ok(RpcMessage::Joining(()))
|
||||
},
|
||||
|
||||
RpcRequest::InstanceChat { instance_id, index } => {
|
||||
if !account.subscribed {
|
||||
return Err(err_msg("subscribe to unlock chat"))
|
||||
}
|
||||
|
||||
let wheel = account::chat_wheel(&db, account.id)?;
|
||||
|
||||
if let Some(c) = wheel.get(index) {
|
||||
self.events.send(Event::Chat(self.id, instance_id, c.to_string()))?;
|
||||
} else {
|
||||
return Err(err_msg("invalid chat index"));
|
||||
}
|
||||
|
||||
Ok(RpcMessage::Processing(()))
|
||||
},
|
||||
_ => {
|
||||
// all good, let's make a tx and process
|
||||
let mut tx = db.transaction()?;
|
||||
@ -257,6 +280,25 @@ impl Connection {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// this is where last minute processing happens
|
||||
// use it to modify outgoing messages, update subs, serialize in some way...
|
||||
fn send(&self, msg: RpcMessage) -> Result<(), Error> {
|
||||
let msg = match self.account {
|
||||
Some(ref a) => match msg {
|
||||
RpcMessage::InstanceState(v) => RpcMessage::InstanceState(v.redact(a.id)),
|
||||
RpcMessage::AccountInstances(v) =>
|
||||
RpcMessage::AccountInstances(v.into_iter().map(|i| i.redact(a.id)).collect()),
|
||||
RpcMessage::GameState(v) => RpcMessage::GameState(v.redact(a.id)),
|
||||
_ => msg,
|
||||
},
|
||||
None => msg,
|
||||
};
|
||||
|
||||
self.ws.send(msg).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// we unwrap everything in here cause really
|
||||
@ -272,7 +314,7 @@ impl Handler for Connection {
|
||||
|
||||
// if user logged in do some prep work
|
||||
if let Some(ref a) = self.account {
|
||||
self.ws.send(RpcMessage::AccountState(a.clone())).unwrap();
|
||||
self.send(RpcMessage::AccountState(a.clone())).unwrap();
|
||||
self.events.send(Event::Subscribe(self.id, a.id)).unwrap();
|
||||
|
||||
// check if they have an image that needs to be generated
|
||||
@ -283,23 +325,26 @@ impl Handler for Connection {
|
||||
|
||||
// send account constructs
|
||||
let account_constructs = account::constructs(&mut tx, a).unwrap();
|
||||
self.ws.send(RpcMessage::AccountConstructs(account_constructs)).unwrap();
|
||||
self.send(RpcMessage::AccountConstructs(account_constructs)).unwrap();
|
||||
|
||||
// get account instances
|
||||
// and send them to the client
|
||||
let account_instances = account::account_instances(&mut tx, a).unwrap();
|
||||
self.ws.send(RpcMessage::AccountInstances(account_instances)).unwrap();
|
||||
self.send(RpcMessage::AccountInstances(account_instances)).unwrap();
|
||||
|
||||
let shop = mtx::account_shop(&mut tx, &a).unwrap();
|
||||
self.ws.send(RpcMessage::AccountShop(shop)).unwrap();
|
||||
self.send(RpcMessage::AccountShop(shop)).unwrap();
|
||||
|
||||
let team = account::team(&mut tx, &a).unwrap();
|
||||
self.ws.send(RpcMessage::AccountTeam(team)).unwrap();
|
||||
self.send(RpcMessage::AccountTeam(team)).unwrap();
|
||||
|
||||
let wheel = account::chat_wheel(&db, a.id).unwrap();
|
||||
self.send(RpcMessage::ChatWheel(wheel)).unwrap();
|
||||
|
||||
// tx should do nothing
|
||||
tx.commit().unwrap();
|
||||
} else {
|
||||
self.ws.send(RpcMessage::Demo(demo().unwrap())).unwrap();
|
||||
self.send(RpcMessage::Demo(demo().unwrap())).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -327,11 +372,11 @@ impl Handler for Connection {
|
||||
_ => (),
|
||||
};
|
||||
|
||||
self.ws.send(reply).unwrap();
|
||||
self.send(reply).unwrap();
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("{:?}", e);
|
||||
self.ws.send(RpcMessage::Error(e.to_string())).unwrap();
|
||||
self.send(RpcMessage::Error(e.to_string())).unwrap();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@ -757,13 +757,13 @@ impl Skill {
|
||||
Skill::HealPlus => 185, //GG
|
||||
Skill::HealPlusPlus => 270, //GG
|
||||
|
||||
Skill::SiphonTick=> 40, // GB
|
||||
Skill::SiphonTickPlus => 50,
|
||||
Skill::SiphonTickPlusPlus => 60,
|
||||
Skill::SiphonTick=> 20, // GB
|
||||
Skill::SiphonTickPlus => 25,
|
||||
Skill::SiphonTickPlusPlus => 30,
|
||||
|
||||
Skill::Slay=> 70, // RG
|
||||
Skill::SlayPlus => 115,
|
||||
Skill::SlayPlusPlus => 180,
|
||||
Skill::Slay=> 40, // RG
|
||||
Skill::SlayPlus => 60,
|
||||
Skill::SlayPlusPlus => 90,
|
||||
|
||||
Skill::Strike=> 90, //RR
|
||||
Skill::StrikePlus => 140,
|
||||
@ -1420,9 +1420,7 @@ fn break_(source: &mut Construct, target: &mut Construct, mut results: Resolutio
|
||||
}
|
||||
|
||||
fn block(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]))
|
||||
.stages(EventStages::StartEnd));
|
||||
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
|
||||
return results;
|
||||
}
|
||||
|
||||
@ -1473,7 +1471,7 @@ fn restrict(source: &mut Construct, target: &mut Construct, mut results: Resolut
|
||||
}
|
||||
|
||||
fn slay(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
||||
let amount = source.red_power().pct(skill.multiplier());
|
||||
let amount = source.red_power().pct(skill.multiplier()) + source.green_power().pct(skill.multiplier());
|
||||
let slay_events = target.deal_red_damage(skill, amount);
|
||||
|
||||
for e in slay_events {
|
||||
@ -1734,7 +1732,7 @@ fn siphon(source: &mut Construct, target: &mut Construct, mut results: Resolutio
|
||||
}
|
||||
|
||||
fn siphon_tick(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
|
||||
let amount = source.blue_power().pct(skill.multiplier());
|
||||
let amount = source.blue_power().pct(skill.multiplier()) + source.green_power().pct(skill.multiplier());
|
||||
let siphon_events = target.deal_blue_damage(skill, amount);
|
||||
|
||||
for e in siphon_events {
|
||||
@ -2039,13 +2037,14 @@ mod tests {
|
||||
.named(&"camel".to_string());
|
||||
|
||||
x.blue_power.force(256);
|
||||
x.green_power.force(220);
|
||||
x.green_life.force(1024);
|
||||
x.green_life.reduce(512);
|
||||
|
||||
let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]);
|
||||
|
||||
assert!(y.affected(Effect::Siphon));
|
||||
assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTick.multiplier())));
|
||||
assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.multiplier())));
|
||||
|
||||
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
||||
match event {
|
||||
@ -2055,14 +2054,15 @@ mod tests {
|
||||
|
||||
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
|
||||
match event {
|
||||
Event::Damage { amount, skill: _, mitigation: _, colour: _} => assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier())),
|
||||
Event::Damage { amount, skill: _, mitigation: _, colour: _} => assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier())
|
||||
+ 220.pct(Skill::SiphonTick.multiplier())),
|
||||
_ => panic!("not damage siphon"),
|
||||
};
|
||||
|
||||
let Resolution { source: _, target, event, stages: _ } = results.remove(0);
|
||||
match event {
|
||||
Event::Healing { amount, skill: _, overhealing: _ } => {
|
||||
assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier()));
|
||||
assert_eq!(amount, 256.pct(Skill::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.multiplier()));
|
||||
assert_eq!(target.id, x.id);
|
||||
},
|
||||
_ => panic!("not healing"),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user