Merge branch 'release/1.4.6'

This commit is contained in:
ntr 2019-09-17 15:40:35 +10:00
commit 3d301479ca
34 changed files with 985 additions and 1081 deletions

View File

@ -7,7 +7,39 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed ### Fixed
### Changed ### Changed
## [In Progress] ## [0.1.4 2019-09-17]
### Changed
Removed self targetting, all skills can be used on any target
`Reflect` No cooldown, 1T duration
`Purify` No cooldown
`Recharge` No cooldown
`Link` reworked ->
Stuns caster for 3/2/1T
If target has higher green life than caster:
Deal blue damage to target equal to difference between green life
Heal with green damage to source equal to difference between green life
`Counter` effect no longer applies immunities
Counter no cooldown
Counter applies for 1T
Counter skill now applies block at 40% / 60% / 80% reduction for 1T
Counter no longer recharges red life
`Electrify`
No Cooldown
Duration -> 1T
Electrocute duration now 2/3/4T
`Sustain`
Now has 1T cooldown at all levels
Has 1T duration at all levels
Now recharges red life to target (120 / 150 / 230)%
## [0.1.3 2019-??-??]
### Added ### Added
@ -20,20 +52,6 @@ Added `Buff` as a skill
`Sustain` now grants immunity to disables. `Sustain` now grants immunity to disables.
*BALANCE*
- purify
- 1 effect from all constructs at level 2
- removes all effects from all constructs at l3
- invert
- fx for buffs when applied to enemies
- invert + haste -> doubles all cooldowns
var / skill info rpc
thresholds / bonuses
sell cost
etc
## [0.1.2] - 2019-05-07 ## [0.1.2] - 2019-05-07
### Added ### Added

View File

@ -1 +1 @@
1.4.5 1.4.6

View File

@ -31,6 +31,7 @@
* fuck magic * fuck magic
* empower on ko * empower on ko
var / skill info rpc -> sell cost / cooldown
* rework vecs into sets * rework vecs into sets
* remove names so games/instances are copy * remove names so games/instances are copy

View File

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

View File

@ -1,8 +1,14 @@
require('./assets/styles/styles.less'); require('./assets/styles/styles.less');
require('./assets/styles/styles.mobile.css'); require('./assets/styles/menu.less');
require('./assets/styles/nav.less');
require('./assets/styles/footer.less');
require('./assets/styles/account.less');
require('./assets/styles/controls.less');
require('./assets/styles/instance.less'); require('./assets/styles/instance.less');
require('./assets/styles/vbox.less');
require('./assets/styles/game.less');
require('./assets/styles/player.less');
require('./assets/styles/styles.mobile.css');
require('./assets/styles/instance.mobile.css'); require('./assets/styles/instance.mobile.css');
require('./assets/styles/game.css');
// kick it off
require('./src/animations.test.jsx'); require('./src/animations.test.jsx');

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -28,30 +28,31 @@
.opponent { .opponent {
grid-area: opponent; grid-area: opponent;
}
.opponent .combat-text { .game-construct {
top: 75%;
}
.opponent .combat-anim {
top: 25%;
}
.opponent .game-construct {
align-items: flex-start; align-items: flex-start;
grid-template-rows: min-content min-content min-content minmax(min-content, 2fr); grid-template-columns: 1fr 2fr;
grid-template-columns: 1fr; grid-template-rows: 1fr;
grid-template-areas:
"stats "
"name "
"effects "
"avatar ";
}
.opponent .game-construct .name { .right {
height: 100%;
display: grid;
grid-template-rows: min-content min-content 1fr;
grid-template-areas:
"stats"
"name"
"avatar";
}
.effects {
align-self: flex-start;
}
}
.game-construct .name {
margin-bottom: 0; margin-bottom: 0;
margin-top: 0.25em; margin-top: 0.25em;
}
} }
.game-construct { .game-construct {
@ -65,19 +66,112 @@
justify-items: center; justify-items: center;
grid-template-rows: min-content minmax(min-content, 1fr) min-content min-content min-content; grid-template-columns: 1fr 3fr;
grid-template-columns: 1fr;
.left {
display: grid;
grid-template-rows: 1fr 1fr;
grid-template-areas: grid-template-areas:
"skills " "skills "
"avatar " "effects";
"effects " }
"name "
"stats "; .right {
display: grid;
grid-template-rows: minmax(min-content, 1fr) min-content min-content;
grid-template-areas:
"avatar"
"name"
"stats";
width: 100%;
}
transition-property: translate, opacity; transition-property: translate, opacity;
transition-duration: 0.25s; transition-duration: 0.25s;
transition-delay: 0; transition-delay: 0;
transition-timing-function: ease; transition-timing-function: ease;
.effects {
align-self: flex-end;
text-align: right;
}
.stats {
align-self: flex-end;
}
.name {
width: 100%;
margin-bottom: 0.25em;
text-align: center;
grid-area: name;
}
.skills {
grid-area: skills;
width: 100%;
button {
width: 100%;
height: 2em;
height: 25%;
}
}
.effects {
grid-area: effects;
white-space: nowrap;
width: 100%;
text-align: center;
font-size: 1.5em;
}
.stats {
grid-area: stats;
display: flex;
justify-content: center;
text-align: center;
}
.stats div {
padding: 0 0.5em;
display: flex;
flex-flow: column;
white-space: nowrap;
text-align: center;
}
.stats .value {
display: none;
}
figcaption {
white-space: nowrap;
font-size: 100%;
}
&.ko {
animation: none;
opacity: 0.20;
}
&.ko button:hover {
color: #333;
}
&.unfocus {
opacity: 0.35;
}
&.unfocus.ko {
opacity: 0.20;
}
}
@media (max-width: 1500px) {
.game-construct figure {
padding: 0 0.25em;
}
} }
#targeting { #targeting {
@ -88,153 +182,34 @@
stroke: whitesmoke; stroke: whitesmoke;
} }
.resolving-skill {
grid-area: target;
align-self: center;
text-align: center;
height: auto;
}
/* some stupid bug in chrome makes it fill the entire screen */ /* some stupid bug in chrome makes it fill the entire screen */
@media screen and (-webkit-min-device-pixel-ratio:0) { @media screen and (-webkit-min-device-pixel-ratio:0) {
#targeting { #targeting {
max-height: 10em; max-height: 10em;
} }
} }
/*
.resolving #targeting {
opacity: 0;
}
*/
.game-construct .name {
width: 100%;
margin-bottom: 0.25em;
text-align: center;
grid-area: name;
}
.game-construct .stats {
grid-area: stats;
display: flex;
justify-content: center;
text-align: center;
}
.game-construct .stats div {
padding: 0 0.5em;
display: flex;
flex-flow: column;
white-space: nowrap;
text-align: center;
}
.game-construct .stats .value {
display: none;
}
.game-construct figcaption {
white-space: nowrap;
font-size: 100%;
}
@media (max-width: 1500px) {
.game-construct figure {
padding: 0 0.25em;
}
}
.game-construct .skills {
grid-area: skills;
display: flex;
flex-flow: column-reverse;
justify-self: center;
width: 100%;
}
.game-construct .skills button {
width: 100%;
}
.game-construct .effects {
grid-area: effects;
white-space: nowrap;
width: 100%;
text-align: center;
font-size: 1.5em;
}
.game-btn {
flex: 0 0 25%;
}
.game-btn:first-child {
margin-right: 0.5em;
}
.game-construct button {
color: #888;
flex: 1 1 100%;
padding: 0;
margin: 0 0.5em;
border-width: 0px;
}
.game-construct button.active {
color: whitesmoke;
}
.game-construct button[disabled], .game-construct button[disabled]:hover {
color: #333333;
text-decoration: line-through
}
.game-construct button:hover {
color: whitesmoke;
}
.game-construct.ko {
animation: none;
opacity: 0.20;
/* filter: grayscale(100%);
*/}
.game-construct.ko button:hover {
color: #333;
}
.game-construct.unfocus {
opacity: 0.35;
/* filter: blur(5px);
*/}
.game-construct.unfocus.ko {
opacity: 0.20;
/* filter: blur(5px) grayscale(100%);
*/}
.combat-text {
font-size: 2em;
font-family: 'Jura';
position: absolute;
top: 5%;
}
.combat-text svg {
height: 7em;
max-width: 100%;
}
.combat-anim { .combat-anim {
font-size: 2em; width: 100%;
font-family: 'Jura';
position: absolute; position: absolute;
object-fit: contain; display: flex;
top: 15%; flex-flow: column;
max-width: 100%;
} }
.combat-anim svg { .combat-anim svg {
height: 7em; flex: 1;
// chrome shit
width: 100%; width: 100%;
height: 100%;
} }
.game-construct.active-skill {
/* filter: drop-shadow(0 0 0.2em silver);
*/}
.game-construct.red-damage { .game-construct.red-damage {
color: #a52a2a; color: #a52a2a;
/*ensure construct doesn't get opacity lowered because of being KO before the KO animation*/ /*ensure construct doesn't get opacity lowered because of being KO before the KO animation*/
@ -319,11 +294,11 @@
object-fit: contain; object-fit: contain;
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: top;
pointer-events: none; // pointer-events: none;
} }
.resolving .skills button { .animating .skills {
opacity: 0; opacity: 0;
} }

View File

@ -201,7 +201,7 @@
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
pointer-events: none; // pointer-events: none;
} }
.name { .name {

View File

@ -55,7 +55,7 @@
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
pointer-events: none; // pointer-events: none;
height: 100%; height: 100%;
} }
} }

View File

@ -93,6 +93,18 @@ dl {
"main ctrl"; "main ctrl";
padding: 0.5em 1em; padding: 0.5em 1em;
&.animations-test {
grid-template-columns: 1fr 9fr 1fr;
grid-template-areas:
"nav hdr ctrl"
"nav main ctrl"
"nav main ctrl";
nav {
display: initial;
}
}
} }
main { main {

View File

@ -4,12 +4,12 @@
"short_name": "mnml", "short_name": "mnml",
"icons": [ "icons": [
{ {
"src": "./assets/icons/726.png", "src": "./assets/icons/mnml.png",
"sizes": "32x32", "sizes": "32x32",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "./assets/icons/726.png", "src": "./assets/icons/mnml.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
} }

View File

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

View File

@ -6,6 +6,7 @@ const eachSeries = require('async/eachSeries');
const actions = require('./actions'); const actions = require('./actions');
const { TIMES } = require('./constants'); const { TIMES } = require('./constants');
const animations = require('./animations.utils'); const animations = require('./animations.utils');
const { removeTier } = require('./utils');
const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://mnml.gg/api/ws' : 'ws://localhost/api/ws'; const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://mnml.gg/api/ws' : 'ws://localhost/api/ws';
@ -28,6 +29,7 @@ function createSocket(store) {
if (animating) return false; if (animating) return false;
store.dispatch(actions.setAnimating(true)); store.dispatch(actions.setAnimating(true));
// stop fetching the game state til animations are done
return eachSeries(newRes, (r, cb) => { return eachSeries(newRes, (r, cb) => {
if (['Disable', 'TargetKo'].includes(r.event[0])) return cb(); if (['Disable', 'TargetKo'].includes(r.event[0])) return cb();
@ -37,15 +39,14 @@ function createSocket(store) {
const timeout = animations.getTime(sequence); const timeout = animations.getTime(sequence);
const anims = animations.getObjects(r, sequence, game, account); const anims = animations.getObjects(r, sequence, game, account);
const text = animations.getText(r, sequence); const text = animations.getText(r, sequence);
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r))); if (sequence.includes('START_SKILL') && anims.animSource) store.dispatch(actions.setAnimSource(anims.animSource));
if (sequence.includes('END_SKILL') && anims.animTarget) {
if (sequence.includes('START_SKILL')) store.dispatch(actions.setAnimSource(anims.animSource));
if (sequence.includes('END_SKILL')) {
store.dispatch(actions.setAnimTarget(anims.animTarget)); 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')) { if (sequence.includes('POST_SKILL' && text)) {
// timeout to prevent text classes from being added too soon // timeout to prevent text classes from being added too soon
setTimeout( setTimeout(
() => store.dispatch(actions.setAnimText(text)), () => store.dispatch(actions.setAnimText(text)),
@ -58,7 +59,7 @@ function createSocket(store) {
store.dispatch(actions.setAnimText(null)); store.dispatch(actions.setAnimText(null));
store.dispatch(actions.setAnimFocus([])); store.dispatch(actions.setAnimFocus([]));
if (!sequence.includes('END_SKILL') if (!sequence.includes('END_SKILL')
|| ['Banish', 'Invert'].includes(anims.animTarget.skill)) return cb(); || ['Banish', 'Invert'].includes(removeTier(anims.animTarget.skill))) return cb();
return true; return true;
}, timeout); }, timeout);
}, err => { }, err => {
@ -70,7 +71,6 @@ function createSocket(store) {
store.dispatch(actions.setAnimating(false)); store.dispatch(actions.setAnimating(false));
store.dispatch(actions.setSkip(false)); store.dispatch(actions.setSkip(false));
store.dispatch(actions.setResolution(null));
// set the game state so resolutions don't fire twice // set the game state so resolutions don't fire twice
store.dispatch(actions.setGame(game)); store.dispatch(actions.setGame(game));

View File

@ -55,7 +55,7 @@ document.fonts.load('16pt "Jura"').then(() => {
const Animations = () => ( const Animations = () => (
<Provider store={store}> <Provider store={store}>
<div id="mnml"> <div id="mnml" class="animations-test">
<nav> <nav>
{animationsNav(ws)} {animationsNav(ws)}
</nav> </nav>
@ -69,24 +69,6 @@ document.fonts.load('16pt "Jura"').then(() => {
}); });
const SKILLS = [ const SKILLS = [
'Absorb',
'Absorption',
'Amplify',
'Attack',
'Banish',
'Bash',
'Blast',
'Block',
'Break',
'Buff',
'Chaos',
'CounterAttack',
'Counter',
'Curse',
'Debuff',
'Decay',
'DecayTick',
'Electrify',
'Electrocute', 'Electrocute',
'ElectrocuteTick', 'ElectrocuteTick',
'Haste', 'Haste',
@ -113,4 +95,22 @@ const SKILLS = [
'Sustain', 'Sustain',
'Triage', 'Triage',
'TriageTick', 'TriageTick',
'Absorb',
'Absorption',
'Amplify',
'Attack',
'Banish',
'Bash',
'Blast',
'Block',
'Break',
'Buff',
'Chaos',
'CounterAttack',
'Counter',
'Curse',
'Debuff',
'Decay',
'DecayTick',
'Electrify',
]; ];

View File

@ -1,9 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default;
const { connect } = require('preact-redux'); const { connect } = require('preact-redux');
const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
@ -12,116 +12,74 @@ const addState = connect(
} }
); );
function laser(dimensions, colour) {
const { x, y, length } = dimensions;
return (
<rect
width="14"
height={length}
x={x}
y={y}
fill="url(#grad1)"
stroke-width="2"
stroke={colour}
filter="url(#strikeFilter)"
/>
);
}
class Strike extends Component { class Strike extends Component {
constructor(props) { constructor(props) {
super(); super();
this.team = props.team; this.props = props;
this.animations = []; this.animations = [];
this.colour = props.colour;
const coord = [0, 50, 100, 150, 200];
const points = coord.map(pos => ({
x: pos + Math.random() * 40,
y: 50 + Math.random() * 100,
length: 150 + Math.random() * 150,
}));
this.charges = points.map(pos => laser(pos, this.colour));
} }
render() { render() {
// const { x, y } = (this.props && this.props.direction) || { x: 0, y: 0 };
// const angle = (Math.atan(y / x) * (180 / Math.PI)) + 90;
// console.log(x, -y);
// console.log(angle);
// can't get this shit to work
return ( return (
<svg <svg
class={'skill-animation'} class='strike-anim'
version="1.1" version="1.1"
id="strike" id="strike"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400"> viewBox="0 0 400 400">
<defs> <filter id='strikeFilter'>
<radialGradient id="grad1" cx="50%" cy="0%" r="85%" fx="50%" fy="50%"> <feTurbulence type="turbulence" baseFrequency="0" numOctaves="1" result="turbulence"></feTurbulence>
<stop offset="0%" style="stop-color:#dba9a9;stop-opacity:0.6" /> <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
<stop offset="100%" style={`stop-color:${this.colour};stop-opacity:1`} />
</radialGradient>
</defs>
<filter id="strikeFilter">
<feGaussianBlur stdDeviation="4"/>
<feTurbulence type="turbulence" baseFrequency="0.01" numOctaves="3" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
</filter> </filter>
{this.charges} <g>
<rect x="200" y="400"
width="12" height="100" stroke-width="0" fill={COLOURS.RED} style={{ filter: 'url("#strikeFilter")' }}
/>
</g>
</svg> </svg>
); );
} }
componentDidMount() { componentDidMount() {
let rotate = 0; // Self target value
if (this.props.direction.y) {
if (!this.props.direction.x) rotate = this.props.direction.y > 0 ? 0 : 180;
else {
rotate = this.props.direction.y > 0
? -Math.atan(this.props.direction.y / this.props.direction.x) * 180 / Math.PI
: -Math.atan(this.props.direction.y / this.props.direction.x) * 180 / Math.PI + 180;
}
} else if (this.props.direction.x) {
rotate = this.props.direction.x > 0 ? 270 : 90;
}
anime.set('#strike', {
rotate,
});
anime.set('#strike', {
translateY: (window.screen.height) * 0.35 * this.props.direction.y,
translateX: 0,
});
this.animations.push(anime({ this.animations.push(anime({
targets: '#strike', targets: ['#strike rect'],
opacity: [ easing: 'easeOutExpo',
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, y: [800, 100, 100],
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, x: [200, 0, 200],
], height: [200, 10, 0],
easing: 'easeInOutSine', width: [20, 400, 0],
delay: TIMES.TARGET_DELAY_MS / 2,
duration: TIMES.TARGET_DURATION_MS,
})); }));
this.animations.push(anime({ this.animations.push(anime({
targets: '#strike', targets: ['#strikeFilter feTurbulence', '#strikeFilter feDisplacementMap'],
translateY: 0, baseFrequency: 2,
translateX: 0, scale: 50,
loop: false, numOctaves: 5,
delay: TIMES.TARGET_DELAY_MS, easing: 'easeOutSine',
duration: (TIMES.TARGET_DURATION_MS * 1 / 2), delay: TIMES.TARGET_DELAY_MS + (TIMES.TARGET_DURATION_MS / 3),
easing: 'easeInQuad', duration: TIMES.TARGET_DURATION_MS / 2,
}));
this.animations.push(anime({
targets: '#strikeFilter feDisplacementMap',
scale: 200,
loop: false,
delay: (TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 1 / 4),
easing: 'easeInQuad',
})); }));
} }
// this is necessary because
// skipping / timing / unmounting race conditions
// can cause the animations to cut short, this will ensure the values are reset
// because preact will recycle all these components
componentWillUnmount() { componentWillUnmount() {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb(); this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Strike); module.exports = addState(Strike);

View File

@ -0,0 +1,21 @@
const anime = require('animejs').default;
function wiggle(id, idle) {
const duration = 300;
const target = document.getElementById(id);
const x = (window.screen.width / anime.random(-100, 100)) * 0.1;
const y = (window.screen.height / anime.random(-20, 100)) * 0.1;
return anime({
targets: target,
rotate: 0,
translateX: [x, -x, 0],
translateY: [y, -y, 0],
duration,
easing: 'easeInOutSine',
// direction: 'alternate',
begin: idle.pause,
complete: idle.restart,
});
}
module.exports = wiggle;

View File

@ -7,12 +7,14 @@ const { connect } = require('preact-redux');
const banish = require('./anims/banish'); const banish = require('./anims/banish');
const idleAnimation = require('./anims/idle'); const idleAnimation = require('./anims/idle');
const invert = require('./anims/invert'); const invert = require('./anims/invert');
const wiggle = require('./anims/wiggle');
const sourceCast = require('./anims/source.cast'); const sourceCast = require('./anims/source.cast');
const { ConstructAnimation } = require('./animations');
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
const { animSource, animTarget } = state; const { animSource, animTarget, animText } = state;
return { animSource, animTarget }; return { animSource, animTarget, animText };
} }
); );
@ -27,15 +29,22 @@ class ConstructAvatar extends Component {
} }
render() { render() {
const { construct } = this.props;
return ( return (
<div <div
class="avatar" class="avatar"
id={this.props.construct.id} id={construct.id}
style={{ 'background-image': `url(/imgs/${this.props.construct.img}.svg)` }} onClick={this.onClick.bind(this)}
/> style={{ 'background-image': `url(/imgs/${construct.img}.svg)` }}>
<ConstructAnimation construct={construct} />
</div>
); );
} }
onClick() {
return this.animations.push(wiggle(this.props.construct.id, this.idle));
}
componentDidMount() { componentDidMount() {
this.idle = idleAnimation(this.props.construct.id); this.idle = idleAnimation(this.props.construct.id);
return this.animations.push(this.idle); return this.animations.push(this.idle);
@ -51,11 +60,17 @@ class ConstructAvatar extends Component {
this.resetAnimations(); this.resetAnimations();
} }
shouldComponentUpdate({ animSource, animTarget, construct }) { shouldComponentUpdate(newProps) {
const { animSource, animTarget, animText, construct } = newProps;
if (construct !== this.props.construct) { if (construct !== this.props.construct) {
return true; return true;
} }
if (animText && animText.constructId === construct.id) {
return wiggle(construct.id, this.idle);
}
if (animSource === this.props.animSource && animTarget === this.props.animTarget) { if (animSource === this.props.animSource && animTarget === this.props.animTarget) {
// console.warn(construct.name, 'thinks its same props') // console.warn(construct.name, 'thinks its same props')
return false; return false;
@ -70,6 +85,7 @@ class ConstructAvatar extends Component {
return sourceCast(animSource.constructId, animSource.direction, this.idle); return sourceCast(animSource.constructId, animSource.direction, this.idle);
} }
// this is the target // this is the target
if (animTarget && animTarget.constructId.includes(construct.id)) { if (animTarget && animTarget.constructId.includes(construct.id)) {
// console.warn(construct.name, 'should update') // console.warn(construct.name, 'should update')
@ -94,13 +110,13 @@ const addStateText = connect(
function constructText(props) { function constructText(props) {
const { construct, animText } = props; const { construct, animText } = props;
if (!construct || !animText) return false; if (!construct) return false;
const text = animText.constructId === construct.id const text = animText && animText.constructId === construct.id
? animText.text ? animText.text
: null; : construct.name;
return <div class={'combat-text'}>{text}</div>; return <h3 class={'name'}>{text}</h3>;
} }
module.exports = { module.exports = {

View File

@ -33,9 +33,12 @@ function FaceoffConstruct(args) {
return ( return (
<div class='game-construct'> <div class='game-construct'>
<div class="left"></div>
<div class="right">
<h3 class="name"> {construct.name} </h3> <h3 class="name"> {construct.name} </h3>
<ConstructAvatar construct={construct} /> <ConstructAvatar construct={construct} />
</div> </div>
</div>
) )
} }

View File

@ -5,7 +5,6 @@ const range = require('lodash/range');
const { STATS } = require('../utils'); const { STATS } = require('../utils');
const { ConstructAvatar, ConstructText } = require('./construct'); const { ConstructAvatar, ConstructText } = require('./construct');
const { ConstructAnimation } = require('./animations');
const shapes = require('./shapes'); const shapes = require('./shapes');
const SkillBtn = require('./skill.btn'); const SkillBtn = require('./skill.btn');
@ -95,7 +94,7 @@ class GameConstruct extends Component {
.map(j => <SkillBtn key={j} construct={construct} i={j} j={i} animating={animating} />); .map(j => <SkillBtn key={j} construct={construct} i={j} j={i} animating={animating} />);
let crypSkills = <div> &nbsp; </div>; let crypSkills = <div> &nbsp; </div>;
if (player && !animating) crypSkills = (<div class="skills"> {skills} </div>); if (player) crypSkills = (<div class="skills"> {skills} </div>);
const effects = construct.effects.length const effects = construct.effects.length
? construct.effects.map(c => <div key={c.effect}>{c.effect} - {c.duration}T</div>) ? construct.effects.map(c => <div key={c.effect}>{c.effect} - {c.duration}T</div>)
@ -106,13 +105,15 @@ class GameConstruct extends Component {
onClick={() => selectSkillTarget(construct.id)} onClick={() => selectSkillTarget(construct.id)}
style={ activeSkill ? { cursor: 'pointer' } : {}} style={ activeSkill ? { cursor: 'pointer' } : {}}
class={`game-construct ${ko} ${classes}`} > class={`game-construct ${ko} ${classes}`} >
<h3 class="name"> {construct.name} </h3> <div class="left">
{crypSkills} {crypSkills}
<div class="effects"> {effects} </div>
</div>
<div class="right">
<div class="stats"> {stats} </div> <div class="stats"> {stats} </div>
<ConstructAvatar construct={construct} /> <ConstructAvatar construct={construct} />
<ConstructAnimation construct={construct} />
<ConstructText construct={construct} /> <ConstructText construct={construct} />
<div class="effects"> {effects} </div> </div>
</div> </div>
); );
} }

View File

@ -12,7 +12,7 @@ const addState = connect(
ws, ws,
game, game,
account, account,
resolution, animating,
activeSkill, activeSkill,
activeConstruct, activeConstruct,
} = state; } = state;
@ -32,7 +32,7 @@ const addState = connect(
return { return {
game, game,
account, account,
resolution, animating,
activeSkill, activeSkill,
activeConstruct, activeConstruct,
selectSkillTarget, selectSkillTarget,
@ -57,7 +57,7 @@ function Game(props) {
const { const {
game, game,
account, account,
resolution, animating,
setActiveSkill, setActiveSkill,
setActiveConstruct, setActiveConstruct,
} = props; } = props;
@ -87,7 +87,7 @@ function Game(props) {
); );
} }
const gameClasses = `game ${resolution ? 'resolving': ''}`; const gameClasses = `game ${animating ? 'animating' : ''}`;
function gameClick(e) { function gameClick(e) {
e.stopPropagation(); e.stopPropagation();

View File

@ -92,13 +92,14 @@ function Play(args) {
<section class="top"> <section class="top">
<div class="news"> <div class="news">
<h1>v{VERSION}</h1> <h1>v{VERSION}</h1>
<p>use the buttons on the right to join an instance.</p> <p>Use the buttons on the right to join an instance.</p>
<p> <p>
select <b>PVP</b> to play against other players.<br /> Select <b>PVP</b> to play against other players.<br />
click <b>LEARN</b> to practice the game without time controls. Select <b>INVITE</b> then click <b>COPY LINK</b> to generate an instance invitation for a friend.<br />
Click <b>LEARN</b> to practice the game without time controls.
</p> </p>
<p> <p>
if you enjoy the game please support its development by <b>subscribing</b> or purchasing <b>credits</b>.<br /> If you enjoy the game please support its development by <b>subscribing</b> or purchasing <b>credits</b>.<br />
glhf glhf
</p> </p>
<p>--ntr & mashy</p> <p>--ntr & mashy</p>

View File

@ -44,7 +44,7 @@ function Skill(props) {
return ( return (
<button <button
disabled='true' disabled='true'
class='construct-skill-btn disabled'> class='construct-skill-btn disabled'>&nbsp;
</button> </button>
); );
} }

View File

@ -17,8 +17,9 @@ class TargetSvg extends Component {
this.onResize = throttle(() => { this.onResize = throttle(() => {
const svg = document.getElementById('targeting'); const svg = document.getElementById('targeting');
if (!svg) return setTimeout(this.onResize, 500);
const { width, height } = svg.getBoundingClientRect(); const { width, height } = svg.getBoundingClientRect();
const path = document.querySelector('#targeting path'); // const path = document.querySelector('#targeting path');
this.setState({ width, height }); this.setState({ width, height });
}, 500); }, 500);
} }
@ -34,15 +35,7 @@ class TargetSvg extends Component {
if (!animTarget) return false; if (!animTarget) return false;
return ( return (
<svg id="targeting" viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none" class="targeting-arrows"> <h1 class="resolving-skill">{animTarget.skill}</h1>
<text
x={`${(width / 2) - 50}`}
y={`${(height / 2) + 16}`}
font-family="Jura"
font-size="2em">
{animTarget.skill}
</text>
</svg>
); );
} }
@ -59,17 +52,11 @@ class TargetSvg extends Component {
? playerTeam.constructs.findIndex(c => c.id === cast.target_construct_id) ? playerTeam.constructs.findIndex(c => c.id === cast.target_construct_id)
: otherTeam.constructs.findIndex(c => c.id === cast.target_construct_id); : otherTeam.constructs.findIndex(c => c.id === cast.target_construct_id);
const pathOffset = [
[0, -1, -2],
[1, 0, -1],
[2, 1, 0],
][source][target];
const sourceY = height; const sourceY = height;
const sourceX = (source * width / 3) + width / 6; const sourceX = (source * width / 3) + width / 24;
const targetX = (target * width / 3) + width / 6 const targetX = (target * width / 3) + width / 6
+ (defensive ? width / 64 : 0) + (defensive ? width / 64 : 0)
+ (pathOffset * width / 32); + (source * width / 18);
const targetY = defensive ? height : 0; const targetY = defensive ? height : 0;
const bendStart = height * (0.7 - 0.1 * source); const bendStart = height * (0.7 - 0.1 * source);
const bendEnd = height * 0.20; const bendEnd = height * 0.20;

View File

@ -74,12 +74,12 @@ function registerEvents(store) {
const text = animations.getText(r, sequence); const text = animations.getText(r, sequence);
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game))); store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
if (sequence.includes('START_SKILL')) store.dispatch(actions.setAnimSource(anims.animSource)); if (sequence.includes('START_SKILL') && anims.animSource) store.dispatch(actions.setAnimSource(anims.animSource));
if (sequence.includes('END_SKILL')) { if (sequence.includes('END_SKILL') && anims.animTarget) {
store.dispatch(actions.setAnimTarget(anims.animTarget)); store.dispatch(actions.setAnimTarget(anims.animTarget));
if (!['Banish', 'Invert'].includes(removeTier(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')) { if (sequence.includes('POST_SKILL') && text) {
// timeout to prevent text classes from being added too soon // timeout to prevent text classes from being added too soon
setTimeout( setTimeout(
() => store.dispatch(actions.setAnimText(text)), () => store.dispatch(actions.setAnimText(text)),

View File

@ -35,7 +35,7 @@ function testGame(uuid) {
"constructs": [ "constructs": [
{ {
"id": "82e8b940-411c-42a1-8fc2-484ec7207734", "id": "82e8b940-411c-42a1-8fc2-484ec7207734",
"img": "8446736d-d682-4588-b8a0-5b7ba53bdb55", "img": "b1be1dfe-f8b5-4467-8406-11f22ffb9e95",
"account": "8552e0bf-340d-4fc8-b6fc-3d56b68fe2a1", "account": "8552e0bf-340d-4fc8-b6fc-3d56b68fe2a1",
"red_damage": { "red_damage": {
"base": 256, "base": 256,
@ -109,7 +109,7 @@ function testGame(uuid) {
}, },
{ {
"id": "96ca4a0e-fed2-4ea2-9ec5-ae308f8dde4b", "id": "96ca4a0e-fed2-4ea2-9ec5-ae308f8dde4b",
"img": "8446736d-d682-4588-b8a0-5b7ba53bdb55", "img": "b1be1dfe-f8b5-4467-8406-11f22ffb9e95",
"account": "8552e0bf-340d-4fc8-b6fc-3d56b68fe2a1", "account": "8552e0bf-340d-4fc8-b6fc-3d56b68fe2a1",
"red_damage": { "red_damage": {
"base": 256, "base": 256,
@ -186,7 +186,7 @@ function testGame(uuid) {
{ {
"id": "ea302c35-d326-475c-a867-8ad5b162165a", "id": "ea302c35-d326-475c-a867-8ad5b162165a",
"account": "8552e0bf-340d-4fc8-b6fc-3d56b68fe2a1", "account": "8552e0bf-340d-4fc8-b6fc-3d56b68fe2a1",
"img": "8446736d-d682-4588-b8a0-5b7ba53bdb55", "img": "b1be1dfe-f8b5-4467-8406-11f22ffb9e95",
"red_damage": { "red_damage": {
"base": 256, "base": 256,
"value": Math.floor(Math.random() * 10000), "value": Math.floor(Math.random() * 10000),
@ -302,7 +302,7 @@ function testGame(uuid) {
{ {
"id": "3aa0f284-1e1b-4054-b38a-b2d50db471bd", "id": "3aa0f284-1e1b-4054-b38a-b2d50db471bd",
"account": uuid, "account": uuid,
"img": "8446736d-d682-4588-b8a0-5b7ba53bdb55", "img": "b1be1dfe-f8b5-4467-8406-11f22ffb9e95",
"red_damage": { "red_damage": {
"base": 256, "base": 256,
"value": Math.floor(Math.random() * 10000), "value": Math.floor(Math.random() * 10000),
@ -389,7 +389,7 @@ function testGame(uuid) {
{ {
"id": "50e5d94e-8ebe-495c-a916-3eb509ff4683", "id": "50e5d94e-8ebe-495c-a916-3eb509ff4683",
"account": uuid, "account": uuid,
"img": "8446736d-d682-4588-b8a0-5b7ba53bdb55", "img": "b1be1dfe-f8b5-4467-8406-11f22ffb9e95",
"red_damage": { "red_damage": {
"base": 256, "base": 256,
"value": Math.floor(Math.random() * 10000), "value": Math.floor(Math.random() * 10000),
@ -470,7 +470,7 @@ function testGame(uuid) {
{ {
"id": "5d49fe65-27f0-4372-90a3-334ef906a0f5", "id": "5d49fe65-27f0-4372-90a3-334ef906a0f5",
"account": uuid, "account": uuid,
"img": "8446736d-d682-4588-b8a0-5b7ba53bdb55", "img": "b1be1dfe-f8b5-4467-8406-11f22ffb9e95",
"red_damage": { "red_damage": {
"base": 256, "base": 256,
"value": Math.floor(Math.random() * 10000), "value": Math.floor(Math.random() * 10000),

View File

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

View File

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

View File

@ -46,7 +46,6 @@ impl Colours {
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub struct ConstructSkill { pub struct ConstructSkill {
pub skill: Skill, pub skill: Skill,
pub self_targeting: bool,
pub cd: Cooldown, pub cd: Cooldown,
// used for Uon client // used for Uon client
pub disabled: bool, pub disabled: bool,
@ -56,7 +55,6 @@ impl ConstructSkill {
pub fn new(skill: Skill) -> ConstructSkill { pub fn new(skill: Skill) -> ConstructSkill {
ConstructSkill { ConstructSkill {
skill, skill,
self_targeting: skill.self_targeting(),
cd: skill.base_cd(), cd: skill.base_cd(),
disabled: false, disabled: false,
} }

View File

@ -13,7 +13,6 @@ pub enum Effect {
Sustain, Sustain,
Curse, Curse,
Haste, Haste,
Hex,
Hybrid, Hybrid,
Invert, Invert,
Counter, Counter,
@ -38,7 +37,6 @@ pub enum Effect {
Absorption, Absorption,
// magic immunity // magic immunity
Link,
// effects over time // effects over time
Triage, Triage,
@ -66,17 +64,9 @@ pub enum Effect {
impl Effect { impl Effect {
pub fn immune(&self, skill: Skill) -> bool { pub fn immune(&self, skill: Skill) -> bool {
match self { match self {
Effect::Counter => match skill {
Skill::Attack => true,
Skill::Stun => true,
_ => skill.colours().contains(&Colour::Red)
},
Effect::Banish => true, Effect::Banish => true,
Effect::Sustain => [ Effect::Sustain => [
Skill::Stun, Skill::Stun,
Skill::Hex,
Skill::HexPlus,
Skill::HexPlusPlus,
Skill::Silence, Skill::Silence,
Skill::SilencePlus, Skill::SilencePlus,
Skill::SilencePlusPlus, Skill::SilencePlusPlus,
@ -98,7 +88,6 @@ impl Effect {
match self { match self {
Effect::Stun => true, Effect::Stun => true,
Effect::Hex => true,
Effect::Banish => true, Effect::Banish => true,
Effect::Silence => skill.colours().contains(&Colour::Blue), Effect::Silence => skill.colours().contains(&Colour::Blue),
Effect::Restrict => skill.colours().contains(&Colour::Red), Effect::Restrict => skill.colours().contains(&Colour::Red),
@ -125,8 +114,6 @@ impl Effect {
Effect::Haste => vec![Stat::Speed], Effect::Haste => vec![Stat::Speed],
Effect::Slow => vec![Stat::Speed], Effect::Slow => vec![Stat::Speed],
Effect::Link => vec![Stat::BlueDamageTaken, Stat::GreenDamageTaken, Stat::RedDamageTaken],
_ => vec![], _ => vec![],
} }
} }
@ -146,15 +133,13 @@ impl Effect {
_ => 100, _ => 100,
}), }),
Effect::Link => value >> 1,
Effect::Absorption => value + match meta { Effect::Absorption => value + match meta {
Some(EffectMeta::AddedDamage(d)) => d, Some(EffectMeta::AddedDamage(d)) => d,
_ => panic!("absorb meta not damage"), _ => panic!("absorb meta not damage"),
}, },
_ => { _ => {
info!("{:?} does not have a mod effect", self); warn!("{:?} does not have a mod effect", self);
return value; return value;
}, },
} }
@ -173,7 +158,6 @@ impl Effect {
Effect::Intercept => Some(Colour::Green), Effect::Intercept => Some(Colour::Green),
// magic // magic
Effect::Hex => Some(Colour::Blue),
Effect::Curse => Some(Colour::Blue), Effect::Curse => Some(Colour::Blue),
Effect::Banish => None, Effect::Banish => None,
// Effect::Banish => rng.gen_bool(0.5), // Effect::Banish => rng.gen_bool(0.5),
@ -194,7 +178,6 @@ impl Effect {
// magic // magic
Effect::Hybrid => Some(Colour::Green), Effect::Hybrid => Some(Colour::Green),
Effect::Link => Some(Colour::Green),
Effect::Invert => Some(Colour::Green), Effect::Invert => Some(Colour::Green),
// effects over time // effects over time

View File

@ -236,7 +236,7 @@ impl Game {
target = find_target(); target = find_target();
} }
pve_skills.push((mobs.id, mob.id, Some(target.id), s)); pve_skills.push((mobs.id, mob.id, target.id, s));
}, },
None => continue, None => continue,
}; };
@ -258,7 +258,7 @@ impl Game {
self self
} }
fn add_skill(&mut self, player_id: Uuid, source_construct_id: Uuid, target_construct_id: Option<Uuid>, skill: Skill) -> Result<&mut Game, Error> { fn add_skill(&mut self, player_id: Uuid, source_construct_id: Uuid, target_construct_id: Uuid, skill: Skill) -> Result<&mut Game, Error> {
// check player in game // check player in game
self.player_by_id(player_id)?; self.player_by_id(player_id)?;
@ -266,17 +266,9 @@ impl Game {
return Err(err_msg("game not in skill phase")); return Err(err_msg("game not in skill phase"));
} }
let final_target_id = match skill.self_targeting() {
true => source_construct_id,
false => match target_construct_id {
Some(t) => t,
None => return Err(err_msg("skill requires a target")),
}
};
// target checks // target checks
{ {
let target = match self.construct_by_id(final_target_id) { let target = match self.construct_by_id(target_construct_id) {
Some(c) => c, Some(c) => c,
None => return Err(err_msg("target construct not in game")), None => return Err(err_msg("target construct not in game")),
}; };
@ -318,7 +310,7 @@ impl Game {
self.stack.remove(s); self.stack.remove(s);
} }
let skill = Cast::new(source_construct_id, player_id, final_target_id, skill); let skill = Cast::new(source_construct_id, player_id, target_construct_id, skill);
self.stack.push(skill); self.stack.push(skill);
return Ok(self); return Ok(self);
@ -887,7 +879,7 @@ fn game_json_file_write(g: &Game) -> Result<String, Error> {
Ok(dest) Ok(dest)
} }
pub fn game_skill(tx: &mut Transaction, account: &Account, game_id: Uuid, construct_id: Uuid, target_construct_id: Option<Uuid>, skill: Skill) -> Result<Game, Error> { pub fn game_skill(tx: &mut Transaction, account: &Account, game_id: Uuid, construct_id: Uuid, target_construct_id: Uuid, skill: Skill) -> Result<Game, Error> {
let mut game = game_get(tx, game_id)?; let mut game = game_get(tx, game_id)?;
game.add_skill(account.id, construct_id, target_construct_id, skill)?; game.add_skill(account.id, construct_id, target_construct_id, skill)?;
@ -1039,8 +1031,8 @@ mod tests {
let x_construct = x_player.constructs[0].clone(); let x_construct = x_player.constructs[0].clone();
let y_construct = y_player.constructs[0].clone(); let y_construct = y_player.constructs[0].clone();
game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Attack).unwrap(); game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Attack).unwrap();
game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
@ -1068,8 +1060,8 @@ mod tests {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns(); game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
} }
game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Stun).unwrap(); game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Stun).unwrap();
game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
@ -1105,8 +1097,8 @@ mod tests {
// remove all mitigation // remove all mitigation
game.player_by_id(x_player.id).unwrap().construct_by_id(x_construct.id).unwrap().red_life.force(0); game.player_by_id(x_player.id).unwrap().construct_by_id(x_construct.id).unwrap().red_life.force(0);
game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Stun).unwrap(); game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Stun).unwrap();
game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
@ -1135,8 +1127,8 @@ mod tests {
assert!(game.player_by_id(y_player.id).unwrap().constructs[0].skill_on_cd(Skill::Stun).is_some()); assert!(game.player_by_id(y_player.id).unwrap().constructs[0].skill_on_cd(Skill::Stun).is_some());
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Block).is_none()); assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Block).is_none());
game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Attack).unwrap(); game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Attack).unwrap();
game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
@ -1149,8 +1141,8 @@ mod tests {
// second round // second round
// now we block and it should go back on cd // now we block and it should go back on cd
// game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Stun).unwrap(); // game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Stun).unwrap();
game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
@ -1179,15 +1171,15 @@ mod tests {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns(); game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
} }
game.add_skill(x_player.id, x_construct.id, None, Skill::Counter).unwrap(); game.add_skill(x_player.id, x_construct.id, x_construct.id, Skill::Counter).unwrap();
game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Stun).unwrap(); game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); game = game.resolve_phase_start();
// should not be stunned because of counter // don't get stunned but not really stunning ¯\_(ツ)_/¯
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].is_stunned() == false); assert!(game.player_by_id(x_player.id).unwrap().constructs[0].is_stunned() == false);
// riposte // riposte
assert_eq!(game.player_by_id(y_player.id).unwrap().constructs[0].green_life(), ( assert_eq!(game.player_by_id(y_player.id).unwrap().constructs[0].green_life(), (
@ -1214,14 +1206,14 @@ mod tests {
} }
// apply buff // apply buff
game.add_skill(x_player.id, x_construct.id, Some(x_construct.id), Skill::Electrify).unwrap(); game.add_skill(x_player.id, x_construct.id, x_construct.id, Skill::Electrify).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); // game = game.resolve_phase_start();
assert!(game.construct_by_id(x_construct.id).unwrap().affected(Effect::Electric)); // assert!(game.construct_by_id(x_construct.id).unwrap().affected(Effect::Electric));
// attack and receive debuff // attack and receive debuff
game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); game = game.resolve_phase_start();
@ -1229,55 +1221,55 @@ mod tests {
assert!(game.construct_by_id(y_construct.id).unwrap().affected(Effect::Electrocute)); assert!(game.construct_by_id(y_construct.id).unwrap().affected(Effect::Electrocute));
} }
#[test] // #[test]
fn link_test() { // fn link_test() {
let mut game = create_test_game(); // let mut game = create_test_game();
let x_player = game.players[0].clone(); // let x_player = game.players[0].clone();
let y_player = game.players[1].clone(); // let y_player = game.players[1].clone();
let x_construct = x_player.constructs[0].clone(); // let x_construct = x_player.constructs[0].clone();
let y_construct = y_player.constructs[0].clone(); // let y_construct = y_player.constructs[0].clone();
game.construct_by_id(x_construct.id).unwrap().learn_mut(Skill::Link); // game.construct_by_id(x_construct.id).unwrap().learn_mut(Skill::Link);
while game.construct_by_id(x_construct.id).unwrap().skill_on_cd(Skill::Link).is_some() { // while game.construct_by_id(x_construct.id).unwrap().skill_on_cd(Skill::Link).is_some() {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns(); // game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
} // }
// apply buff // // apply buff
game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Link).unwrap(); // game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Link).unwrap();
game.player_ready(x_player.id).unwrap(); // game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); // game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); // game = game.resolve_phase_start();
assert!(game.construct_by_id(x_construct.id).unwrap().affected(Effect::Link)); // assert!(game.construct_by_id(x_construct.id).unwrap().affected(Effect::Link));
let Resolution { source: _, target: _, event, stages: _ } = game.resolved.pop().unwrap(); // let Resolution { source: _, target: _, event, stages: _ } = game.resolved.pop().unwrap();
match event { // match event {
Event::Effect { effect, skill: _, duration: _, construct_effects: _ } => assert_eq!(effect, Effect::Link), // Event::Effect { effect, skill: _, duration: _, construct_effects: _ } => assert_eq!(effect, Effect::Link),
_ => panic!("not siphon"), // _ => panic!("not siphon"),
}; // };
let Resolution { source: _, target: _, event, stages: _ } = game.resolved.pop().unwrap(); // let Resolution { source: _, target: _, event, stages: _ } = game.resolved.pop().unwrap();
match event { // match event {
Event::Recharge { red: _, blue: _, skill: _ } => (), // Event::Recharge { red: _, blue: _, skill: _ } => (),
_ => panic!("link result was not recharge"), // _ => panic!("link result was not recharge"),
} // }
// attack and receive link hit // // attack and receive link hit
game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); // game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap(); // game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); // game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); // game = game.resolve_phase_start();
let Resolution { source: _, target, event, stages: _ } = game.resolved.pop().unwrap(); // let Resolution { source: _, target, event, stages: _ } = game.resolved.pop().unwrap();
assert_eq!(target.id, y_construct.id); // assert_eq!(target.id, y_construct.id);
match event { // match event {
Event::Damage { amount, skill: _, mitigation: _, colour: _} => // Event::Damage { amount, skill: _, mitigation: _, colour: _} =>
assert_eq!(amount, x_construct.red_power().pct(Skill::Attack.multiplier()) >> 1), // assert_eq!(amount, x_construct.red_power().pct(Skill::Attack.multiplier()) >> 1),
_ => panic!("not damage link"), // _ => panic!("not damage link"),
}; // };
} // }
// #[test] // #[test]
// fn absorb_test() { // fn absorb_test() {
@ -1296,14 +1288,14 @@ mod tests {
// } // }
// // apply buff // // apply buff
// game.add_skill(x_player.id, x_construct.id, Some(x_construct.id), Skill::Absorb).unwrap(); // game.add_skill(x_player.id, x_construct.id, x_construct.id, Skill::Absorb).unwrap();
// game.player_ready(x_player.id).unwrap(); // game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap(); // game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start(); // game = game.resolve_phase_start();
// assert!(game.construct_by_id(x_construct.id).unwrap().affected(Effect::Absorb)); // assert!(game.construct_by_id(x_construct.id).unwrap().affected(Effect::Absorb));
// // attack and receive debuff // // attack and receive debuff
// game.add_skill(y_player.id, y_construct.id, Some(x_construct.id), Skill::TestAttack).unwrap(); // game.add_skill(y_player.id, y_construct.id, x_construct.id, Skill::TestAttack).unwrap();
// game.player_ready(x_player.id).unwrap(); // game.player_ready(x_player.id).unwrap();
// game.player_ready(y_player.id).unwrap(); // game.player_ready(y_player.id).unwrap();
// game = game.resolve_phase_start(); // game = game.resolve_phase_start();
@ -1330,10 +1322,10 @@ mod tests {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns(); game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
} }
game.add_skill(i_player.id, i_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(i_player.id, i_construct.id, x_construct.id, Skill::Attack).unwrap();
game.add_skill(i_player.id, j_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(i_player.id, j_construct.id, x_construct.id, Skill::Attack).unwrap();
game.add_skill(x_player.id, x_construct.id, Some(i_construct.id), Skill::Ruin).unwrap(); game.add_skill(x_player.id, x_construct.id, i_construct.id, Skill::Ruin).unwrap();
game.add_skill(x_player.id, y_construct.id, Some(i_construct.id), Skill::Attack).unwrap(); game.add_skill(x_player.id, y_construct.id, i_construct.id, Skill::Attack).unwrap();
game.player_ready(i_player.id).unwrap(); game.player_ready(i_player.id).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
@ -1380,10 +1372,10 @@ mod tests {
game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns(); game.construct_by_id(x_construct.id).unwrap().reduce_cooldowns();
} }
game.add_skill(i_player.id, i_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(i_player.id, i_construct.id, x_construct.id, Skill::Attack).unwrap();
game.add_skill(i_player.id, j_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(i_player.id, j_construct.id, x_construct.id, Skill::Attack).unwrap();
game.add_skill(x_player.id, x_construct.id, Some(i_construct.id), Skill::Intercept).unwrap(); game.add_skill(x_player.id, x_construct.id, i_construct.id, Skill::Intercept).unwrap();
game.add_skill(x_player.id, y_construct.id, Some(i_construct.id), Skill::Attack).unwrap(); game.add_skill(x_player.id, y_construct.id, i_construct.id, Skill::Attack).unwrap();
game.player_ready(i_player.id).unwrap(); game.player_ready(i_player.id).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
@ -1411,10 +1403,10 @@ mod tests {
let x_construct = x_player.constructs[0].clone(); let x_construct = x_player.constructs[0].clone();
let y_construct = x_player.constructs[1].clone(); let y_construct = x_player.constructs[1].clone();
game.add_skill(i_player.id, i_construct.id, Some(x_construct.id), Skill::Attack).unwrap() game.add_skill(i_player.id, i_construct.id, x_construct.id, Skill::Attack).unwrap()
.add_skill(i_player.id, j_construct.id, Some(x_construct.id), Skill::Attack).unwrap() .add_skill(i_player.id, j_construct.id, x_construct.id, Skill::Attack).unwrap()
.add_skill(x_player.id, x_construct.id, Some(i_construct.id), Skill::Attack).unwrap() .add_skill(x_player.id, x_construct.id, i_construct.id, Skill::Attack).unwrap()
.add_skill(x_player.id, y_construct.id, Some(i_construct.id), Skill::Attack).unwrap() .add_skill(x_player.id, y_construct.id, i_construct.id, Skill::Attack).unwrap()
.player_ready(i_player.id).unwrap() .player_ready(i_player.id).unwrap()
.player_ready(x_player.id).unwrap(); .player_ready(x_player.id).unwrap();
@ -1430,10 +1422,10 @@ mod tests {
assert!(game.player_by_id(x_player.id).unwrap().skills_required() == 2); assert!(game.player_by_id(x_player.id).unwrap().skills_required() == 2);
// add some more skills // add some more skills
game.add_skill(i_player.id, j_construct.id, Some(x_construct.id), Skill::Attack).unwrap(); game.add_skill(i_player.id, j_construct.id, x_construct.id, Skill::Attack).unwrap();
game.add_skill(x_player.id, x_construct.id, Some(j_construct.id), Skill::Attack).unwrap(); game.add_skill(x_player.id, x_construct.id, j_construct.id, Skill::Attack).unwrap();
game.add_skill(x_player.id, y_construct.id, Some(j_construct.id), Skill::Attack).unwrap(); game.add_skill(x_player.id, y_construct.id, j_construct.id, Skill::Attack).unwrap();
assert!(game.add_skill(x_player.id, x_construct.id, Some(i_construct.id), Skill::Attack).is_err()); assert!(game.add_skill(x_player.id, x_construct.id, i_construct.id, Skill::Attack).is_err());
game.player_ready(i_player.id).unwrap(); game.player_ready(i_player.id).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
@ -1475,7 +1467,7 @@ mod tests {
} }
// apply buff // apply buff
game.add_skill(x_player.id, x_construct.id, Some(y_construct.id), Skill::Decay).unwrap(); game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Decay).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); game = game.resolve_phase_start();
@ -1490,7 +1482,7 @@ mod tests {
game.resolved.clear(); game.resolved.clear();
// remove // remove
game.add_skill(y_player.id, y_construct.id, Some(y_construct.id), Skill::Purify).unwrap(); game.add_skill(y_player.id, y_construct.id, y_construct.id, Skill::Purify).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); game = game.resolve_phase_start();
@ -1503,14 +1495,14 @@ mod tests {
} }
}; };
game.add_skill(y_player.id, x_construct.id, Some(y_construct.id), Skill::Siphon).unwrap(); game.add_skill(y_player.id, x_construct.id, y_construct.id, Skill::Siphon).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); game = game.resolve_phase_start();
game.resolved.clear(); game.resolved.clear();
game.add_skill(y_player.id, y_construct.id, Some(y_construct.id), Skill::Purify).unwrap(); game.add_skill(y_player.id, y_construct.id, y_construct.id, Skill::Purify).unwrap();
game.player_ready(x_player.id).unwrap(); game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap(); game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start(); game = game.resolve_phase_start();

View File

@ -273,12 +273,12 @@ mod tests {
// hieroglyph(); // hieroglyph();
// } // }
#[test] // #[test]
fn shapes_img_test() { // fn shapes_img_test() {
for i in 0..100 { // for i in 0..100 {
shapes_write(Uuid::new_v4()).unwrap(); // shapes_write(Uuid::new_v4()).unwrap();
} // }
} // }
} }

View File

@ -143,12 +143,6 @@ pub enum Item {
#[serde(rename = "Decay++")] #[serde(rename = "Decay++")]
DecayPlusPlus, DecayPlusPlus,
Hex,
#[serde(rename = "Hex+")]
HexPlus,
#[serde(rename = "Hex++")]
HexPlusPlus,
Haste, Haste,
#[serde(rename = "Haste+")] #[serde(rename = "Haste+")]
HastePlus, HastePlus,
@ -726,12 +720,6 @@ impl Item {
Item::HealPlus | Item::HealPlus |
Item::HealPlusPlus => format!("Heals target for {:?}% GreenPower.", self.into_skill().unwrap().multiplier()), Item::HealPlusPlus => format!("Heals target for {:?}% GreenPower.", self.into_skill().unwrap().multiplier()),
Item::Hex|
Item::HexPlus |
Item::HexPlusPlus => format!("Blue based skill that applies Hex for {:?}T. \
Hexed targets cannot cast any skills.",
self.into_skill().unwrap().effect()[0].get_duration()),
Item::Hybrid| Item::Hybrid|
Item::HybridPlus | Item::HybridPlus |
Item::HybridPlusPlus => format!( Item::HybridPlusPlus => format!(
@ -785,9 +773,11 @@ impl Item {
Item::Link| Item::Link|
Item::LinkPlus | Item::LinkPlus |
Item::LinkPlusPlus => format!( Item::LinkPlusPlus => format!(
"Caster links with target. Linked constructs split incoming Damage evenly. "Form a link with target swapping relative life values.
Recharges target BlueLife based on {:?}% of BluePower", If the target construct has more green life than caster, {:?}% of green life difference as blue damage to the target and heal to the caster.
self.into_skill().unwrap().multiplier()), Stuns caster for {:?}T in the process.",
self.into_skill().unwrap().multiplier(),
self.into_skill().unwrap().effect()[0].get_duration()),
Item::Silence| Item::Silence|
Item::SilencePlus | Item::SilencePlus |
@ -813,10 +803,9 @@ impl Item {
Item::Restrict| Item::Restrict|
Item::RestrictPlus | Item::RestrictPlus |
Item::RestrictPlusPlus => format!( Item::RestrictPlusPlus => format!(
"Block the target from using red skills for {:?}T and deals {:?}% RedPower as red damage. {}", "Block the target from using red skills for {:?}T and deals {:?}% RedPower as red damage. Deals 35% more damage per red skill on target",
self.into_skill().unwrap().effect()[0].get_duration(), self.into_skill().unwrap().effect()[0].get_duration(),
self.into_skill().unwrap().multiplier(), self.into_skill().unwrap().multiplier()),
"Deals 35% more damage per red skill on target"),
Item::Bash| Item::Bash|
Item::BashPlus | Item::BashPlus |
@ -866,97 +855,97 @@ impl Item {
fn combo(&self) -> Vec<Item> { fn combo(&self) -> Vec<Item> {
match self { match self {
Item::Intercept => vec![Item::Buff, Item::Red, Item::Red], Item::Intercept => vec![Item::Buff, Item::Red, Item::Red],
Item::Triage => vec![Item::Buff, Item::Green, Item::Green],
Item::Absorb => vec![Item::Buff, Item::Blue, Item::Blue],
Item::Amplify => vec![Item::Buff, Item::Red, Item::Blue],
Item::Haste => vec![Item::Buff, Item::Red, Item::Green],
Item::Hybrid => vec![Item::Buff, Item::Green, Item::Blue],
Item::InterceptPlus => vec![Item::Intercept, Item::Intercept, Item::Intercept], Item::InterceptPlus => vec![Item::Intercept, Item::Intercept, Item::Intercept],
Item::InterceptPlusPlus => vec![Item::InterceptPlus, Item::InterceptPlus, Item::InterceptPlus], Item::InterceptPlusPlus => vec![Item::InterceptPlus, Item::InterceptPlus, Item::InterceptPlus],
Item::Triage => vec![Item::Buff, Item::Green, Item::Green],
Item::TriagePlus => vec![Item::Triage, Item::Triage, Item::Triage], Item::TriagePlus => vec![Item::Triage, Item::Triage, Item::Triage],
Item::TriagePlusPlus => vec![Item::TriagePlus, Item::TriagePlus, Item::TriagePlus], Item::TriagePlusPlus => vec![Item::TriagePlus, Item::TriagePlus, Item::TriagePlus],
Item::Link => vec![Item::Buff, Item::Blue, Item::Blue],
Item::LinkPlus => vec![Item::Link, Item::Link, Item::Link],
Item::LinkPlusPlus => vec![Item::LinkPlusPlus, Item::LinkPlusPlus, Item::LinkPlusPlus],
Item::Haste => vec![Item::Buff, Item::Red, Item::Green],
Item::HastePlus => vec![Item::Haste, Item::Haste, Item::Haste], Item::HastePlus => vec![Item::Haste, Item::Haste, Item::Haste],
Item::HastePlusPlus => vec![Item::HastePlus, Item::HastePlus, Item::HastePlus], Item::HastePlusPlus => vec![Item::HastePlus, Item::HastePlus, Item::HastePlus],
Item::Hybrid => vec![Item::Buff, Item::Green, Item::Blue],
Item::HybridPlus => vec![Item::Hybrid, Item::Hybrid, Item::Hybrid], Item::HybridPlus => vec![Item::Hybrid, Item::Hybrid, Item::Hybrid],
Item::HybridPlusPlus => vec![Item::HybridPlus, Item::HybridPlus, Item::HybridPlus], Item::HybridPlusPlus => vec![Item::HybridPlus, Item::HybridPlus, Item::HybridPlus],
Item::Amplify => vec![Item::Buff, Item::Red, Item::Blue], Item::AbsorbPlus => vec![Item::Absorb, Item::Absorb, Item::Absorb],
Item::AbsorbPlusPlus => vec![Item::AbsorbPlus, Item::AbsorbPlus, Item::AbsorbPlus],
Item::AmplifyPlus => vec![Item::Amplify, Item::Amplify, Item::Amplify], Item::AmplifyPlus => vec![Item::Amplify, Item::Amplify, Item::Amplify],
Item::AmplifyPlusPlus => vec![Item::AmplifyPlus, Item::AmplifyPlus, Item::AmplifyPlus], Item::AmplifyPlusPlus => vec![Item::AmplifyPlus, Item::AmplifyPlus, Item::AmplifyPlus],
Item::Purge => vec![Item::Debuff, Item::Green, Item::Green], // Needs flavour
Item::Invert => vec![Item::Debuff, Item::Red, Item::Blue],
Item::Restrict => vec![Item::Debuff, Item::Red, Item::Red], Item::Restrict => vec![Item::Debuff, Item::Red, Item::Red],
Item::Silence => vec![Item::Debuff, Item::Blue, Item::Blue],
Item::Curse => vec![Item::Debuff, Item::Red, Item::Green],
Item::Decay => vec![Item::Debuff, Item::Green, Item::Blue],
Item::RestrictPlus => vec![Item::Restrict, Item::Restrict, Item::Restrict], Item::RestrictPlus => vec![Item::Restrict, Item::Restrict, Item::Restrict],
Item::RestrictPlusPlus => vec![Item::RestrictPlus, Item::RestrictPlus, Item::RestrictPlus], Item::RestrictPlusPlus => vec![Item::RestrictPlus, Item::RestrictPlus, Item::RestrictPlus],
Item::Purge => vec![Item::Debuff, Item::Green, Item::Green], // Needs flavour
Item::PurgePlus => vec![Item::Purge, Item::Purge, Item::Purge], // Needs flavour Item::PurgePlus => vec![Item::Purge, Item::Purge, Item::Purge], // Needs flavour
Item::PurgePlusPlus => vec![Item::PurgePlus, Item::PurgePlus, Item::PurgePlus], // Needs flavour Item::PurgePlusPlus => vec![Item::PurgePlus, Item::PurgePlus, Item::PurgePlus], // Needs flavour
Item::Silence => vec![Item::Debuff, Item::Blue, Item::Blue],
Item::SilencePlus => vec![Item::Silence, Item::Silence, Item::Silence], Item::SilencePlus => vec![Item::Silence, Item::Silence, Item::Silence],
Item::SilencePlusPlus => vec![Item::SilencePlus, Item::SilencePlus, Item::SilencePlus], Item::SilencePlusPlus => vec![Item::SilencePlus, Item::SilencePlus, Item::SilencePlus],
Item::Curse => vec![Item::Debuff, Item::Red, Item::Green],
Item::CursePlus => vec![Item::Curse, Item::Curse, Item::Curse], Item::CursePlus => vec![Item::Curse, Item::Curse, Item::Curse],
Item::CursePlusPlus => vec![Item::CursePlus, Item::CursePlus, Item::CursePlus], Item::CursePlusPlus => vec![Item::CursePlus, Item::CursePlus, Item::CursePlus],
Item::Decay => vec![Item::Debuff, Item::Green, Item::Blue],
Item::DecayPlus => vec![Item::Decay, Item::Decay, Item::Decay], Item::DecayPlus => vec![Item::Decay, Item::Decay, Item::Decay],
Item::DecayPlusPlus => vec![Item::DecayPlus, Item::DecayPlus, Item::DecayPlus], Item::DecayPlusPlus => vec![Item::DecayPlus, Item::DecayPlus, Item::DecayPlus],
Item::Invert => vec![Item::Debuff, Item::Red, Item::Blue],
Item::InvertPlus => vec![Item::Invert, Item::Invert, Item::Invert], Item::InvertPlus => vec![Item::Invert, Item::Invert, Item::Invert],
Item::InvertPlusPlus => vec![Item::InvertPlus, Item::InvertPlus, Item::InvertPlus], Item::InvertPlusPlus => vec![Item::InvertPlus, Item::InvertPlus, Item::InvertPlus],
Item::Counter => vec![Item::Block, Item::Red, Item::Red], Item::Counter => vec![Item::Block, Item::Red, Item::Red],
Item::Reflect => vec![Item::Block, Item::Green, Item::Blue],
Item::Purify => vec![Item::Block, Item::Green, Item::Green],
Item::Sustain => vec![Item::Block, Item::Red, Item::Green],
Item::Electrify => vec![Item::Block, Item::Blue, Item::Blue],
Item::Recharge => vec![Item::Block, Item::Red, Item::Blue],
Item::CounterPlus => vec![Item::Counter, Item::Counter, Item::Counter], Item::CounterPlus => vec![Item::Counter, Item::Counter, Item::Counter],
Item::CounterPlusPlus => vec![Item::CounterPlus, Item::CounterPlus, Item::CounterPlus], // Add red recharge Item::CounterPlusPlus => vec![Item::CounterPlus, Item::CounterPlus, Item::CounterPlus], // Add red recharge
Item::Purify => vec![Item::Block, Item::Green, Item::Green],
Item::PurifyPlus => vec![Item::Purify, Item::Purify, Item::Purify], Item::PurifyPlus => vec![Item::Purify, Item::Purify, Item::Purify],
Item::PurifyPlusPlus => vec![Item::PurifyPlus, Item::PurifyPlus, Item::PurifyPlus], Item::PurifyPlusPlus => vec![Item::PurifyPlus, Item::PurifyPlus, Item::PurifyPlus],
Item::Electrify => vec![Item::Block, Item::Blue, Item::Blue],
Item::ElectrifyPlus => vec![Item::Electrify, Item::Electrify, Item::Electrify], Item::ElectrifyPlus => vec![Item::Electrify, Item::Electrify, Item::Electrify],
Item::ElectrifyPlusPlus => vec![Item::ElectrifyPlus, Item::ElectrifyPlus, Item::ElectrifyPlus], Item::ElectrifyPlusPlus => vec![Item::ElectrifyPlus, Item::ElectrifyPlus, Item::ElectrifyPlus],
Item::Sustain => vec![Item::Block, Item::Red, Item::Green],
Item::SustainPlus => vec![Item::Sustain, Item::Sustain, Item::Sustain], Item::SustainPlus => vec![Item::Sustain, Item::Sustain, Item::Sustain],
Item::SustainPlusPlus => vec![Item::SustainPlus, Item::SustainPlus, Item::SustainPlus], Item::SustainPlusPlus => vec![Item::SustainPlus, Item::SustainPlus, Item::SustainPlus],
Item::Reflect => vec![Item::Block, Item::Green, Item::Blue],
Item::ReflectPlus => vec![Item::Reflect, Item::Reflect, Item::Reflect], Item::ReflectPlus => vec![Item::Reflect, Item::Reflect, Item::Reflect],
Item::ReflectPlusPlus => vec![Item::ReflectPlus, Item::ReflectPlus, Item::ReflectPlus], Item::ReflectPlusPlus => vec![Item::ReflectPlus, Item::ReflectPlus, Item::ReflectPlus],
Item::Recharge => vec![Item::Block, Item::Red, Item::Blue],
Item::RechargePlus => vec![Item::Recharge, Item::Recharge, Item::Recharge], Item::RechargePlus => vec![Item::Recharge, Item::Recharge, Item::Recharge],
Item::RechargePlusPlus => vec![Item::RechargePlus, Item::RechargePlus, Item::RechargePlus], Item::RechargePlusPlus => vec![Item::RechargePlus, Item::RechargePlus, Item::RechargePlus],
Item::Bash => vec![Item::Stun, Item::Red, Item::Red], Item::Bash => vec![Item::Stun, Item::Red, Item::Red],
Item::Sleep => vec![Item::Stun, Item::Green, Item::Green],
Item::Ruin => vec![Item::Stun, Item::Blue, Item::Blue],
Item::Link => vec![Item::Stun, Item::Blue, Item::Green],
Item::Banish => vec![Item::Stun, Item::Red, Item::Blue],
Item::Break => vec![Item::Stun, Item::Red, Item::Green],
Item::BashPlus => vec![Item::Bash, Item::Bash, Item::Bash], Item::BashPlus => vec![Item::Bash, Item::Bash, Item::Bash],
Item::BashPlusPlus => vec![Item::BashPlus, Item::BashPlus, Item::BashPlus], Item::BashPlusPlus => vec![Item::BashPlus, Item::BashPlus, Item::BashPlus],
Item::Sleep => vec![Item::Stun, Item::Green, Item::Green],
Item::SleepPlus => vec![Item::Sleep, Item::Sleep, Item::Sleep], Item::SleepPlus => vec![Item::Sleep, Item::Sleep, Item::Sleep],
Item::SleepPlusPlus => vec![Item::SleepPlus, Item::SleepPlus, Item::SleepPlus], Item::SleepPlusPlus => vec![Item::SleepPlus, Item::SleepPlus, Item::SleepPlus],
Item::Ruin => vec![Item::Stun, Item::Blue, Item::Blue],
Item::RuinPlus => vec![Item::Ruin, Item::Ruin, Item::Ruin], Item::RuinPlus => vec![Item::Ruin, Item::Ruin, Item::Ruin],
Item::RuinPlusPlus => vec![Item::RuinPlus, Item::RuinPlus, Item::RuinPlus], Item::RuinPlusPlus => vec![Item::RuinPlus, Item::RuinPlus, Item::RuinPlus],
Item::Break => vec![Item::Stun, Item::Red, Item::Green],
Item::BreakPlus => vec![Item::Break, Item::Break, Item::Break], Item::BreakPlus => vec![Item::Break, Item::Break, Item::Break],
Item::BreakPlusPlus => vec![Item::BreakPlus, Item::BreakPlus, Item::BreakPlus], Item::BreakPlusPlus => vec![Item::BreakPlus, Item::BreakPlus, Item::BreakPlus],
Item::Absorb => vec![Item::Stun, Item::Green, Item::Blue], Item::LinkPlus => vec![Item::Link, Item::Link, Item::Link],
Item::AbsorbPlus => vec![Item::Absorb, Item::Absorb, Item::Absorb], Item::LinkPlusPlus => vec![Item::LinkPlusPlus, Item::LinkPlusPlus, Item::LinkPlusPlus],
Item::AbsorbPlusPlus => vec![Item::AbsorbPlus, Item::AbsorbPlus, Item::AbsorbPlus],
Item::Banish => vec![Item::Stun, Item::Red, Item::Blue],
Item::BanishPlus => vec![Item::Banish, Item::Banish, Item::Banish], Item::BanishPlus => vec![Item::Banish, Item::Banish, Item::Banish],
Item::BanishPlusPlus => vec![Item::BanishPlus, Item::BanishPlus, Item::BanishPlus], Item::BanishPlusPlus => vec![Item::BanishPlus, Item::BanishPlus, Item::BanishPlus],
Item::Strike => vec![Item::Attack, Item::Red, Item::Red], Item::Strike => vec![Item::Attack, Item::Red, Item::Red],
Item::Chaos => vec![Item::Attack, Item::Red, Item::Blue],
Item::Heal => vec![Item::Attack, Item::Green, Item::Green],
Item::Blast => vec![Item::Attack, Item::Blue, Item::Blue],
Item::Slay => vec![Item::Attack, Item::Red, Item::Green],
Item::Siphon => vec![Item::Attack, Item::Green, Item::Blue],
Item::StrikePlus => vec![Item::Strike, Item::Strike, Item::Strike], Item::StrikePlus => vec![Item::Strike, Item::Strike, Item::Strike],
Item::StrikePlusPlus => vec![Item::StrikePlus, Item::StrikePlus, Item::StrikePlus], Item::StrikePlusPlus => vec![Item::StrikePlus, Item::StrikePlus, Item::StrikePlus],
Item::Heal => vec![Item::Attack, Item::Green, Item::Green],
Item::HealPlus => vec![Item::Heal, Item::Heal, Item::Heal], Item::HealPlus => vec![Item::Heal, Item::Heal, Item::Heal],
Item::HealPlusPlus => vec![Item::HealPlus, Item::HealPlus, Item::HealPlus], Item::HealPlusPlus => vec![Item::HealPlus, Item::HealPlus, Item::HealPlus],
Item::Blast => vec![Item::Attack, Item::Blue, Item::Blue],
Item::BlastPlus => vec![Item::Blast, Item::Blast, Item::Blast], Item::BlastPlus => vec![Item::Blast, Item::Blast, Item::Blast],
Item::BlastPlusPlus => vec![Item::BlastPlus, Item::BlastPlus, Item::BlastPlus], Item::BlastPlusPlus => vec![Item::BlastPlus, Item::BlastPlus, Item::BlastPlus],
Item::Slay => vec![Item::Attack, Item::Red, Item::Green],
Item::SlayPlus => vec![Item::Slay, Item::Slay, Item::Slay], Item::SlayPlus => vec![Item::Slay, Item::Slay, Item::Slay],
Item::SlayPlusPlus => vec![Item::SlayPlus, Item::SlayPlus, Item::SlayPlus], Item::SlayPlusPlus => vec![Item::SlayPlus, Item::SlayPlus, Item::SlayPlus],
Item::Siphon => vec![Item::Attack, Item::Green, Item::Blue],
Item::SiphonPlus => vec![Item::Siphon, Item::Siphon, Item::Siphon], Item::SiphonPlus => vec![Item::Siphon, Item::Siphon, Item::Siphon],
Item::SiphonPlusPlus => vec![Item::SiphonPlus, Item::SiphonPlus, Item::SiphonPlus], Item::SiphonPlusPlus => vec![Item::SiphonPlus, Item::SiphonPlus, Item::SiphonPlus],
Item::Chaos => vec![Item::Attack, Item::Red, Item::Blue],
Item::ChaosPlus => vec![Item::Chaos, Item::Chaos, Item::Chaos], Item::ChaosPlus => vec![Item::Chaos, Item::Chaos, Item::Chaos],
Item::ChaosPlusPlus => vec![Item::ChaosPlus, Item::ChaosPlus, Item::ChaosPlus], Item::ChaosPlusPlus => vec![Item::ChaosPlus, Item::ChaosPlus, Item::ChaosPlus],
@ -998,22 +987,18 @@ impl Item {
Item::LifeGBPlusPlus => vec![Item::LifeGBPlus, Item::LifeGBPlus, Item::LifeGBPlus], Item::LifeGBPlusPlus => vec![Item::LifeGBPlus, Item::LifeGBPlus, Item::LifeGBPlus],
Item::LifeRBPlusPlus => vec![Item::LifeRBPlus, Item::LifeRBPlus, Item::LifeRBPlus], Item::LifeRBPlusPlus => vec![Item::LifeRBPlus, Item::LifeRBPlus, Item::LifeRBPlus],
Item::SpeedRR => vec![Item::Speed, Item::Red, Item::Red], Item::SpeedRR => vec![Item::Speed, Item::Red, Item::Red],
Item::SpeedGG => vec![Item::Speed, Item::Green, Item::Green], Item::SpeedGG => vec![Item::Speed, Item::Green, Item::Green],
Item::SpeedBB => vec![Item::Speed, Item::Blue, Item::Blue], Item::SpeedBB => vec![Item::Speed, Item::Blue, Item::Blue],
Item::SpeedRG => vec![Item::Speed, Item::Red, Item::Green], Item::SpeedRG => vec![Item::Speed, Item::Red, Item::Green],
Item::SpeedGB => vec![Item::Speed, Item::Green, Item::Blue], Item::SpeedGB => vec![Item::Speed, Item::Green, Item::Blue],
Item::SpeedRB => vec![Item::Speed, Item::Red, Item::Blue], Item::SpeedRB => vec![Item::Speed, Item::Red, Item::Blue],
Item::SpeedRRPlus => vec![Item::SpeedRR, Item::SpeedRR, Item::SpeedRR], Item::SpeedRRPlus => vec![Item::SpeedRR, Item::SpeedRR, Item::SpeedRR],
Item::SpeedGGPlus => vec![Item::SpeedGG, Item::SpeedGG, Item::SpeedGG], Item::SpeedGGPlus => vec![Item::SpeedGG, Item::SpeedGG, Item::SpeedGG],
Item::SpeedBBPlus => vec![Item::SpeedBB, Item::SpeedBB, Item::SpeedBB], Item::SpeedBBPlus => vec![Item::SpeedBB, Item::SpeedBB, Item::SpeedBB],
Item::SpeedRGPlus => vec![Item::SpeedRG, Item::SpeedRG, Item::SpeedRG], Item::SpeedRGPlus => vec![Item::SpeedRG, Item::SpeedRG, Item::SpeedRG],
Item::SpeedGBPlus => vec![Item::SpeedGB, Item::SpeedGB, Item::SpeedGB], Item::SpeedGBPlus => vec![Item::SpeedGB, Item::SpeedGB, Item::SpeedGB],
Item::SpeedRBPlus => vec![Item::SpeedRB, Item::SpeedRB, Item::SpeedRB], Item::SpeedRBPlus => vec![Item::SpeedRB, Item::SpeedRB, Item::SpeedRB],
Item::SpeedRRPlusPlus => vec![Item::SpeedRRPlus, Item::SpeedRRPlus, Item::SpeedRRPlus], Item::SpeedRRPlusPlus => vec![Item::SpeedRRPlus, Item::SpeedRRPlus, Item::SpeedRRPlus],
Item::SpeedGGPlusPlus => vec![Item::SpeedGGPlus, Item::SpeedGGPlus, Item::SpeedGGPlus], Item::SpeedGGPlusPlus => vec![Item::SpeedGGPlus, Item::SpeedGGPlus, Item::SpeedGGPlus],
Item::SpeedBBPlusPlus => vec![Item::SpeedBBPlus, Item::SpeedBBPlus, Item::SpeedBBPlus], Item::SpeedBBPlusPlus => vec![Item::SpeedBBPlus, Item::SpeedBBPlus, Item::SpeedBBPlus],
@ -1062,22 +1047,19 @@ impl From<Skill> for Item {
Skill::DecayPlusPlus => Item::DecayPlusPlus, Skill::DecayPlusPlus => Item::DecayPlusPlus,
Skill::Electrify => Item::Electrify, Skill::Electrify => Item::Electrify,
Skill::ElectrifyPlus => Item::ElectrifyPlus, Skill::ElectrifyPlus => Item::ElectrifyPlus,
Skill::ElectrifyPlusPlus => Item::ElectrifyPlusPlus, Skill::ElectrifyPlusPlus=> Item::ElectrifyPlusPlus,
Skill::Haste => Item::Haste, Skill::Haste => Item::Haste,
Skill::HastePlus => Item::HastePlus, Skill::HastePlus => Item::HastePlus,
Skill::HastePlusPlus => Item::HastePlusPlus, Skill::HastePlusPlus => Item::HastePlusPlus,
Skill::Heal => Item::Heal, Skill::Heal => Item::Heal,
Skill::HealPlus => Item::HealPlus, Skill::HealPlus => Item::HealPlus,
Skill::HealPlusPlus => Item::HealPlusPlus, Skill::HealPlusPlus => Item::HealPlusPlus,
Skill::Hex => Item::Hex,
Skill::HexPlus => Item::HexPlus,
Skill::HexPlusPlus => Item::HexPlusPlus,
Skill::Hybrid => Item::Hybrid, Skill::Hybrid => Item::Hybrid,
Skill::HybridPlus => Item::HybridPlus, Skill::HybridPlus => Item::HybridPlus,
Skill::HybridPlusPlus => Item::HybridPlusPlus, Skill::HybridPlusPlus => Item::HybridPlusPlus,
Skill::Intercept => Item::Intercept, Skill::Intercept => Item::Intercept,
Skill::InterceptPlus => Item::InterceptPlus, Skill::InterceptPlus => Item::InterceptPlus,
Skill::InterceptPlusPlus => Item::InterceptPlusPlus, Skill::InterceptPlusPlus=> Item::InterceptPlusPlus,
Skill::Invert => Item::Invert, Skill::Invert => Item::Invert,
Skill::InvertPlus => Item::InvertPlus, Skill::InvertPlus => Item::InvertPlus,
Skill::InvertPlusPlus => Item::InvertPlusPlus, Skill::InvertPlusPlus => Item::InvertPlusPlus,

View File

@ -82,7 +82,7 @@ pub enum RpcRequest {
GameState { id: Uuid }, GameState { id: Uuid },
GameReady { id: Uuid }, GameReady { id: Uuid },
GameSkill { game_id: Uuid, construct_id: Uuid, target_construct_id: Option<Uuid>, skill: Skill }, GameSkill { game_id: Uuid, construct_id: Uuid, target_construct_id: Uuid, skill: Skill },
GameSkillClear { game_id: Uuid }, GameSkillClear { game_id: Uuid },
AccountState {}, AccountState {},

View File

@ -177,10 +177,6 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut
Skill::HealPlus | Skill::HealPlus |
Skill::HealPlusPlus => heal(source, target, resolutions, skill), Skill::HealPlusPlus => heal(source, target, resolutions, skill),
Skill::Hex|
Skill::HexPlus |
Skill::HexPlusPlus => hex(source, target, resolutions, skill),
Skill::Absorb| Skill::Absorb|
Skill::AbsorbPlus | Skill::AbsorbPlus |
Skill::AbsorbPlusPlus => absorb(source, target, resolutions, skill), Skill::AbsorbPlusPlus => absorb(source, target, resolutions, skill),
@ -320,26 +316,16 @@ fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) ->
}; };
} }
// beware that link doesn't cause any damage if target.affected(Effect::Counter) {
// because then applying it will proc this
if target.affected(Effect::Link) {
resolutions = link_hit(&source, &target, resolutions, game, event)
}
},
Event::Immunity { skill: _, immunity } => match immunity.contains(&Effect::Counter) {
true => {
let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter() let ConstructEffect { effect: _, duration: _, meta, tick: _ } = target.effects.iter()
.find(|e| e.effect == Effect::Counter).unwrap().clone(); .find(|e| e.effect == Effect::Counter).unwrap().clone();
match meta { match meta {
Some(EffectMeta::Skill(s)) => { Some(EffectMeta::Skill(s)) => {
resolutions = riposte(&mut target, &mut source, resolutions, s); resolutions = counter_attack(&mut target, &mut source, resolutions, s);
}, },
_ => panic!("no counter skill"), _ => panic!("no counter skill"),
}; };
}
},
false => (),
}, },
_ => (), _ => (),
}; };
@ -558,12 +544,6 @@ pub enum Skill {
#[serde(rename = "Decay++")] #[serde(rename = "Decay++")]
DecayPlusPlus, DecayPlusPlus,
Hex,
#[serde(rename = "Hex+")]
HexPlus,
#[serde(rename = "Hex++")]
HexPlusPlus,
Haste, Haste,
#[serde(rename = "Haste+")] #[serde(rename = "Haste+")]
HastePlus, HastePlus,
@ -763,9 +743,6 @@ impl Skill {
Skill::ElectrocuteTickPlus => 100, Skill::ElectrocuteTickPlus => 100,
Skill::ElectrocuteTickPlusPlus => 130, Skill::ElectrocuteTickPlusPlus => 130,
Skill::Counter=> 110,
Skill::CounterPlus => 145,
Skill::CounterPlusPlus => 200,
Skill::CounterAttack=> 70, Skill::CounterAttack=> 70,
Skill::CounterAttackPlus => 95, Skill::CounterAttackPlus => 95,
Skill::CounterAttackPlusPlus => 120, Skill::CounterAttackPlusPlus => 120,
@ -774,14 +751,18 @@ impl Skill {
Skill::PurifyPlus => 70, Skill::PurifyPlus => 70,
Skill::PurifyPlusPlus => 105, Skill::PurifyPlusPlus => 105,
Skill::Reflect=> 45, //restore blue life (heal) Skill::Reflect=> 45, //Recharge blue life (heal)
Skill::ReflectPlus => 70, Skill::ReflectPlus => 70,
Skill::ReflectPlusPlus => 100, Skill::ReflectPlusPlus => 100,
Skill::Recharge=> 85, //restore red and blue life (heal) Skill::Recharge=> 85, //Recharge red and blue life (heal)
Skill::RechargePlus => 130, Skill::RechargePlus => 130,
Skill::RechargePlusPlus => 200, Skill::RechargePlusPlus => 200,
Skill::Sustain => 120, // Recharge red life (heal)
Skill::SustainPlus => 150,
Skill::SustainPlusPlus => 230,
// Stun Base // Stun Base
Skill::Sleep=> 240, //Green dmg (heal) Skill::Sleep=> 240, //Green dmg (heal)
Skill::SleepPlus => 300, Skill::SleepPlus => 300,
@ -805,9 +786,9 @@ impl Skill {
// Buff base // Buff base
Skill::HybridBlast => 25, Skill::HybridBlast => 25,
Skill::HasteStrike => 30, Skill::HasteStrike => 30,
Skill::Link=> 140, Skill::Link=> 75,
Skill::LinkPlus => 200, Skill::LinkPlus => 100,
Skill::LinkPlusPlus => 300, Skill::LinkPlusPlus => 150,
Skill::Intercept=> 80, Skill::Intercept=> 80,
Skill::InterceptPlus => 110, Skill::InterceptPlus => 110,
Skill::InterceptPlusPlus => 150, Skill::InterceptPlusPlus => 150,
@ -837,22 +818,22 @@ impl Skill {
Skill::Buff => vec![ConstructEffect {effect: Effect::Buff, duration: 2, Skill::Buff => vec![ConstructEffect {effect: Effect::Buff, duration: 2,
meta: Some(EffectMeta::Multiplier(125)), tick: None }], meta: Some(EffectMeta::Multiplier(125)), tick: None }],
Skill::Electrify => vec![ConstructEffect {effect: Effect::Electric, duration: 2, Skill::Electrify => vec![ConstructEffect {effect: Effect::Electric, duration: 1,
meta: Some(EffectMeta::Skill(Skill::Electrocute)), tick: None}], meta: Some(EffectMeta::Skill(Skill::Electrocute)), tick: None}],
Skill::ElectrifyPlus => vec![ConstructEffect {effect: Effect::Electric, duration: 3, Skill::ElectrifyPlus => vec![ConstructEffect {effect: Effect::Electric, duration: 1,
meta: Some(EffectMeta::Skill(Skill::ElectrocutePlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::ElectrocutePlus)), tick: None}],
Skill::ElectrifyPlusPlus => vec![ConstructEffect {effect: Effect::Electric, duration: 4, Skill::ElectrifyPlusPlus => vec![ConstructEffect {effect: Effect::Electric, duration: 1,
meta: Some(EffectMeta::Skill(Skill::ElectrocutePlusPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::ElectrocutePlusPlus)), tick: None}],
Skill::Electrocute => vec![ConstructEffect {effect: Effect::Electrocute, duration: 3, Skill::Electrocute => vec![ConstructEffect {effect: Effect::Electrocute, duration: 2,
meta: Some(EffectMeta::Skill(Skill::ElectrocuteTick)), tick: None}], meta: Some(EffectMeta::Skill(Skill::ElectrocuteTick)), tick: None}],
Skill::ElectrocutePlus => vec![ConstructEffect {effect: Effect::Electrocute, duration: 4, Skill::ElectrocutePlus => vec![ConstructEffect {effect: Effect::Electrocute, duration: 3,
meta: Some(EffectMeta::Skill(Skill::ElectrocuteTickPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::ElectrocuteTickPlus)), tick: None}],
Skill::ElectrocutePlusPlus => vec![ConstructEffect {effect: Effect::Electrocute, duration: 5, Skill::ElectrocutePlusPlus => vec![ConstructEffect {effect: Effect::Electrocute, duration: 4,
meta: Some(EffectMeta::Skill(Skill::ElectrocuteTickPlusPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::ElectrocuteTickPlusPlus)), tick: None}],
Skill::Sustain => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }], Skill::Sustain => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }],
Skill::SustainPlus => vec![ConstructEffect {effect: Effect::Sustain, duration: 2, meta: None, tick: None }], Skill::SustainPlus => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }],
Skill::SustainPlusPlus => vec![ConstructEffect {effect: Effect::Sustain, duration: 3, meta: None, tick: None }], Skill::SustainPlusPlus => vec![ConstructEffect {effect: Effect::Sustain, duration: 1, meta: None, tick: None }],
Skill::Curse => vec![ConstructEffect {effect: Effect::Curse, duration: 2, Skill::Curse => vec![ConstructEffect {effect: Effect::Curse, duration: 2,
meta: Some(EffectMeta::Multiplier(150)), tick: None}], meta: Some(EffectMeta::Multiplier(150)), tick: None}],
@ -864,13 +845,16 @@ impl Skill {
Skill::Debuff => vec![ConstructEffect {effect: Effect::Slow, duration: 3, Skill::Debuff => vec![ConstructEffect {effect: Effect::Slow, duration: 3,
meta: Some(EffectMeta::Multiplier(50)), tick: None }], meta: Some(EffectMeta::Multiplier(50)), tick: None }],
Skill::Decay => vec![ConstructEffect {effect: Effect::Wither, duration: 3, meta: Some(EffectMeta::Multiplier(50)), tick: None }, Skill::Decay => vec![ConstructEffect {effect: Effect::Wither, duration: 3,
meta: Some(EffectMeta::Multiplier(50)), tick: None },
ConstructEffect {effect: Effect::Decay, duration: 3, ConstructEffect {effect: Effect::Decay, duration: 3,
meta: Some(EffectMeta::Skill(Skill::DecayTick)), tick: None}], meta: Some(EffectMeta::Skill(Skill::DecayTick)), tick: None}],
Skill::DecayPlus => vec![ConstructEffect {effect: Effect::Wither, duration: 3, meta: Some(EffectMeta::Multiplier(35)), tick: None }, Skill::DecayPlus => vec![ConstructEffect {effect: Effect::Wither, duration: 3,
meta: Some(EffectMeta::Multiplier(35)), tick: None },
ConstructEffect {effect: Effect::Decay, duration: 3, ConstructEffect {effect: Effect::Decay, duration: 3,
meta: Some(EffectMeta::Skill(Skill::DecayTickPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::DecayTickPlus)), tick: None}],
Skill::DecayPlusPlus => vec![ConstructEffect {effect: Effect::Wither, duration: 4, meta: Some(EffectMeta::Multiplier(20)), tick: None }, Skill::DecayPlusPlus => vec![ConstructEffect {effect: Effect::Wither, duration: 4,
meta: Some(EffectMeta::Multiplier(20)), tick: None },
ConstructEffect {effect: Effect::Decay, duration: 4, ConstructEffect {effect: Effect::Decay, duration: 4,
meta: Some(EffectMeta::Skill(Skill::DecayTickPlusPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::DecayTickPlusPlus)), tick: None}],
@ -880,9 +864,6 @@ impl Skill {
meta: Some(EffectMeta::Multiplier(175)), tick: None }], meta: Some(EffectMeta::Multiplier(175)), tick: None }],
Skill::HastePlusPlus => vec![ConstructEffect {effect: Effect::Haste, duration: 4, Skill::HastePlusPlus => vec![ConstructEffect {effect: Effect::Haste, duration: 4,
meta: Some(EffectMeta::Multiplier(225)), tick: None }], meta: Some(EffectMeta::Multiplier(225)), tick: None }],
Skill::Hex => vec![ConstructEffect {effect: Effect::Hex, duration: 2, meta: None, tick: None}],
Skill::HexPlus => vec![ConstructEffect {effect: Effect::Hex, duration: 3, meta: None, tick: None}],
Skill::HexPlusPlus => vec![ConstructEffect {effect: Effect::Hex, duration: 4, meta: None, tick: None}],
Skill::Absorb => vec![ConstructEffect {effect: Effect::Absorb, duration: 2, Skill::Absorb => vec![ConstructEffect {effect: Effect::Absorb, duration: 2,
meta: Some(EffectMeta::Skill(Skill::Absorption)), tick: None}], meta: Some(EffectMeta::Skill(Skill::Absorption)), tick: None}],
@ -906,35 +887,44 @@ impl Skill {
Skill::InvertPlus => vec![ConstructEffect {effect: Effect::Invert, duration: 3, meta: None, tick: None}], Skill::InvertPlus => vec![ConstructEffect {effect: Effect::Invert, duration: 3, meta: None, tick: None}],
Skill::InvertPlusPlus => vec![ConstructEffect {effect: Effect::Invert, duration: 4, meta: None, tick: None}], Skill::InvertPlusPlus => vec![ConstructEffect {effect: Effect::Invert, duration: 4, meta: None, tick: None}],
Skill::Counter => vec![ConstructEffect {effect: Effect::Counter, duration: 2, Skill::Counter => vec![ConstructEffect {effect: Effect::Counter, duration: 1,
meta: Some(EffectMeta::Skill(Skill::CounterAttack)), tick: None}], meta: Some(EffectMeta::Skill(Skill::CounterAttack)), tick: None},
Skill::CounterPlus => vec![ConstructEffect {effect: Effect::Counter, duration: 2, ConstructEffect {effect: Effect::Block, duration: 1,
meta: Some(EffectMeta::Skill(Skill::CounterAttackPlus)), tick: None}], meta: Some(EffectMeta::Multiplier(60)), tick: None}],
Skill::CounterPlusPlus => vec![ConstructEffect {effect: Effect::Counter, duration: 2, Skill::CounterPlus => vec![ConstructEffect {effect: Effect::Counter, duration: 1,
meta: Some(EffectMeta::Skill(Skill::CounterAttackPlusPlus)), tick: None}], meta: Some(EffectMeta::Skill(Skill::CounterAttackPlus)), tick: None},
ConstructEffect {effect: Effect::Block, duration: 1,
meta: Some(EffectMeta::Multiplier(40)), tick: None}],
Skill::CounterPlusPlus => vec![ConstructEffect {effect: Effect::Counter, duration: 1,
meta: Some(EffectMeta::Skill(Skill::CounterAttackPlusPlus)), tick: None},
ConstructEffect {effect: Effect::Block, duration: 1,
meta: Some(EffectMeta::Multiplier(20)), tick: None}],
Skill::Reflect => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }], Skill::Reflect => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }],
Skill::ReflectPlus => vec![ConstructEffect {effect: Effect::Reflect, duration: 2, meta: None, tick: None }], Skill::ReflectPlus => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }],
Skill::ReflectPlusPlus => vec![ConstructEffect {effect: Effect::Reflect, duration: 3, meta: None, tick: None }], Skill::ReflectPlusPlus => vec![ConstructEffect {effect: Effect::Reflect, duration: 1, meta: None, tick: None }],
Skill::Break => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}, Skill::Break => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None},
ConstructEffect {effect: Effect::Vulnerable, duration: 3, meta: Some(EffectMeta::Multiplier(150)), tick: None}], ConstructEffect {effect: Effect::Vulnerable, duration: 3,
meta: Some(EffectMeta::Multiplier(150)), tick: None}],
Skill::BreakPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}, Skill::BreakPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None},
ConstructEffect {effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(200)), tick: None}], ConstructEffect {effect: Effect::Vulnerable, duration: 4,
meta: Some(EffectMeta::Multiplier(200)), tick: None}],
Skill::BreakPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}, Skill::BreakPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None},
ConstructEffect {effect: Effect::Vulnerable, duration: 4, meta: Some(EffectMeta::Multiplier(250)), tick: None}], ConstructEffect {effect: Effect::Vulnerable, duration: 4,
meta: Some(EffectMeta::Multiplier(250)), tick: None}],
Skill::Ruin => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}], Skill::Ruin => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
Skill::RuinPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}], Skill::RuinPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
Skill::RuinPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}], Skill::RuinPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
Skill::Purge => vec![ConstructEffect {effect: Effect::Purge, duration: 1, meta: None, tick: None}], Skill::Purge => vec![ConstructEffect {effect: Effect::Purge, duration: 2, meta: None, tick: None}],
Skill::PurgePlus => vec![ConstructEffect {effect: Effect::Purge, duration: 2, meta: None, tick: None}], Skill::PurgePlus => vec![ConstructEffect {effect: Effect::Purge, duration: 3, meta: None, tick: None}],
Skill::PurgePlusPlus => vec![ConstructEffect {effect: Effect::Purge, duration: 3, meta: None, tick: None}], Skill::PurgePlusPlus => vec![ConstructEffect {effect: Effect::Purge, duration: 4, meta: None, tick: None}],
Skill::Link => vec![ConstructEffect {effect: Effect::Link, duration: 2, meta: None, tick: None}], Skill::Link => vec![ConstructEffect {effect: Effect::Stun, duration: 3, meta: None, tick: None}],
Skill::LinkPlus => vec![ConstructEffect {effect: Effect::Link, duration: 3, meta: None, tick: None}], Skill::LinkPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}],
Skill::LinkPlusPlus => vec![ConstructEffect {effect: Effect::Link, duration: 4, meta: None, tick: None}], Skill::LinkPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
Skill::Silence => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}], Skill::Silence => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}],
Skill::SilencePlus => vec![ConstructEffect {effect: Effect::Silence, duration: 3, meta: None, tick: None}], Skill::SilencePlus => vec![ConstructEffect {effect: Effect::Silence, duration: 3, meta: None, tick: None}],
@ -991,7 +981,7 @@ impl Skill {
Skill::Block => None, // reduce damage Skill::Block => None, // reduce damage
Skill::Counter| Skill::Counter|
Skill::CounterPlus | Skill::CounterPlus |
Skill::CounterPlusPlus => Some(2), // avoid all damage Skill::CounterPlusPlus => None, // avoid all damage
Skill::Restrict=> Some(2), Skill::Restrict=> Some(2),
Skill::RestrictPlus => Some(2), Skill::RestrictPlus => Some(2),
@ -1051,8 +1041,8 @@ impl Skill {
Skill::SilencePlus => Some(2), Skill::SilencePlus => Some(2),
Skill::SilencePlusPlus => Some(2), Skill::SilencePlusPlus => Some(2),
Skill::Purify=> None, Skill::Purify |
Skill::PurifyPlus => None, Skill::PurifyPlus |
Skill::PurifyPlusPlus => None, Skill::PurifyPlusPlus => None,
Skill::Purge=> None, Skill::Purge=> None,
@ -1063,21 +1053,17 @@ impl Skill {
Skill::BanishPlus => Some(1), Skill::BanishPlus => Some(1),
Skill::BanishPlusPlus => Some(1), Skill::BanishPlusPlus => Some(1),
Skill::Hex=> Some(1),
Skill::HexPlus => Some(2),
Skill::HexPlusPlus => Some(2),
Skill::Haste=> Some(2), Skill::Haste=> Some(2),
Skill::HastePlus => Some(2), Skill::HastePlus => Some(2),
Skill::HastePlusPlus => Some(2), Skill::HastePlusPlus => Some(2),
Skill::Reflect=> Some(2), Skill::Reflect |
Skill::ReflectPlus => Some(2), Skill::ReflectPlus |
Skill::ReflectPlusPlus => Some(2), Skill::ReflectPlusPlus => None,
Skill::Recharge=> Some(2), Skill::Recharge=> None,
Skill::RechargePlus => Some(2), Skill::RechargePlus => None,
Skill::RechargePlusPlus => Some(2), Skill::RechargePlusPlus => None,
Skill::Ruin=> Some(3), Skill::Ruin=> Some(3),
Skill::RuinPlus => Some(2), Skill::RuinPlus => Some(2),
@ -1091,17 +1077,17 @@ impl Skill {
Skill::SleepPlus => Some(3), Skill::SleepPlus => Some(3),
Skill::SleepPlusPlus => Some(3), Skill::SleepPlusPlus => Some(3),
Skill::Sustain=> Some(1), Skill::Sustain |
Skill::SustainPlus => Some(2), Skill::SustainPlus |
Skill::SustainPlusPlus => Some(3), Skill::SustainPlusPlus => Some(1),
Skill::Intercept=> Some(2), Skill::Intercept=> Some(2),
Skill::InterceptPlus => Some(2), Skill::InterceptPlus => Some(2),
Skill::InterceptPlusPlus => Some(2), Skill::InterceptPlusPlus => Some(2),
Skill::Electrify=>Some(1), Skill::Electrify |
Skill::ElectrifyPlus =>Some(1), Skill::ElectrifyPlus |
Skill::ElectrifyPlusPlus =>Some(1), Skill::ElectrifyPlusPlus => None,
Skill::Absorb| Skill::Absorb|
@ -1180,10 +1166,6 @@ impl Skill {
pub fn speed(&self) -> u64 { pub fn speed(&self) -> u64 {
match self { match self {
Skill::Strike=> Item::from(Skill::Strike).speed().pct(150),
Skill::StrikePlus => Skill::Strike.speed(),
Skill::StrikePlusPlus => Skill::Strike.speed(),
Skill::SiphonTick| Skill::SiphonTick|
Skill::SiphonTickPlus | Skill::SiphonTickPlus |
Skill::SiphonTickPlusPlus => Skill::Siphon.speed(), Skill::SiphonTickPlusPlus => Skill::Siphon.speed(),
@ -1213,20 +1195,6 @@ impl Skill {
} }
} }
pub fn self_targeting(&self) -> bool {
match self {
Skill::Block |
Skill::Sustain|
Skill::SustainPlus |
Skill::SustainPlusPlus |
Skill::Counter|
Skill::CounterPlus |
Skill::CounterPlusPlus => true,
_ => false,
}
}
pub fn defensive(&self) -> bool { pub fn defensive(&self) -> bool {
let mut rng = thread_rng(); let mut rng = thread_rng();
@ -1376,8 +1344,14 @@ fn sleep(source: &mut Construct, target: &mut Construct, mut results: Resolution
} }
fn sustain(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn sustain(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
skill.effect().into_iter() let red_amount = source.red_power().pct(skill.multiplier());
.for_each(|e| (results.push(Resolution::new(source, target).event(target.add_effect(skill, e))))); results.push(Resolution::new(source, target)
.event(target.recharge(skill, red_amount, 0)));
results.push(Resolution::new(source, target)
.event(target.add_effect(skill, skill.effect()[0]))
.stages(EventStages::PostOnly));
return results; return results;
} }
@ -1413,19 +1387,17 @@ fn buff(source: &mut Construct, target: &mut Construct, mut results: Resolutions
} }
fn counter(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn counter(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let red_amount = source.red_power().pct(skill.multiplier());
results.push(Resolution::new(source, target) results.push(Resolution::new(source, target)
.event(target.recharge(skill, red_amount, 0)) .event(target.add_effect(skill, skill.effect()[0])));
.stages(EventStages::StartEnd));
results.push(Resolution::new(source, target) results.push(Resolution::new(source, target)
.event(target.add_effect(skill, skill.effect()[0])) .event(target.add_effect(skill, skill.effect()[1]))
.stages(EventStages::PostOnly)); .stages(EventStages::PostOnly));
return results; return results;
} }
fn riposte(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn counter_attack(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());
target.deal_red_damage(skill, amount) target.deal_red_damage(skill, amount)
.into_iter() .into_iter()
@ -1602,12 +1574,6 @@ fn ruin(source: &mut Construct, target: &mut Construct, mut results: Resolutions
return results;; return results;;
} }
fn hex(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])));
return results;;
}
fn absorb(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn absorb(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0]))); results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
return results;; return results;;
@ -1688,40 +1654,23 @@ fn siphon_tick(source: &mut Construct, target: &mut Construct, mut results: Reso
} }
fn link(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn link(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let blue_amount = source.blue_power().pct(skill.multiplier()); let swap = match target.green_life().checked_sub(source.green_life()) {
results.push(Resolution::new(source, target).event(target.recharge(skill, 0, blue_amount))); Some(s) => s.pct(skill.multiplier()),
None => 0
let link = skill.effect()[0].set_meta(EffectMeta::LinkTarget(target.id));
results.push(Resolution::new(source, target).event(source.add_effect(skill, link)).stages(EventStages::PostOnly));
return results;
}
fn link_hit(source: &Construct, target: &Construct, mut results: Resolutions, game: &mut Game, event: Event) -> Resolutions {
match event {
Event::Damage { amount, skill, mitigation: _, colour } => {
let link = target.effects.iter().find(|e| e.effect == Effect::Link).unwrap();
if let Some(EffectMeta::LinkTarget(link_target_id)) = link.meta {
let mut link_target = game.construct_by_id(link_target_id).unwrap();
let res = match colour {
Colour::Red => link_target.deal_red_damage(skill, amount),
Colour::Blue => link_target.deal_blue_damage(skill, amount),
Colour::Green => link_target.deal_green_damage(skill, amount),
}; };
results.push(Resolution::new(target, link_target).event(Event::Skill { skill: Skill::Link})); target.deal_blue_damage(skill, swap)
res.into_iter().for_each(|e| results.push(Resolution::new(&source, &link_target) .into_iter()
.event(e).stages(EventStages::EndPost))); .for_each(|e| results.push(Resolution::new(source, target).event(e)));
} else {
panic!("not a link target {:?}", link); source.deal_green_damage(skill, swap)
} .into_iter()
.for_each(|e| results.push(Resolution::new(source, source).event(e).stages(EventStages::PostOnly)));
results.push(Resolution::new(source, source)
.event(source.add_effect(skill, skill.effect()[0])).stages(EventStages::PostOnly));
return results; return results;
},
_ => panic!("{:?} link hit not damage event", event),
}
} }
fn silence(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn silence(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
@ -1758,7 +1707,10 @@ fn purge(source: &mut Construct, target: &mut Construct, mut results: Resolution
.event(Event::Removal { effect: ce.effect, construct_effects: target.effects.clone() })); .event(Event::Removal { effect: ce.effect, construct_effects: target.effects.clone() }));
} }
let mut turns = 1; let effect = skill.effect()[0];
results.push(Resolution::new(source, target).event(target.add_effect(skill, effect)).stages(EventStages::PostOnly));
/*let mut turns = 1;
for cs in target.skills.iter_mut() { for cs in target.skills.iter_mut() {
if Effect::Purge.disables_skill(cs.skill) { if Effect::Purge.disables_skill(cs.skill) {
turns += 1; turns += 1;
@ -1766,10 +1718,8 @@ fn purge(source: &mut Construct, target: &mut Construct, mut results: Resolution
} }
if turns > 1 { if turns > 1 {
let mut effect = skill.effect()[0];
effect.duration = effect.duration * turns; effect.duration = effect.duration * turns;
results.push(Resolution::new(source, target).event(target.add_effect(skill, effect)).stages(EventStages::PostOnly)); }*/
}
return results; return results;
} }
@ -1878,7 +1828,7 @@ mod tests {
sustain(&mut y.clone(), &mut y, vec![], Skill::Sustain); sustain(&mut y.clone(), &mut y, vec![], Skill::Sustain);
assert!(y.affected(Effect::Sustain)); assert!(y.affected(Effect::Sustain));
let mut results = hex(&mut x, &mut y, vec![], Skill::Hex); let mut results = ruin(&mut x, &mut y, vec![], Skill::Ruin);
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0); let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
match event { match event {
Event::Immunity { skill: _, immunity } => assert!(immunity.contains(&Effect::Sustain)), Event::Immunity { skill: _, immunity } => assert!(immunity.contains(&Effect::Sustain)),
@ -2113,8 +2063,8 @@ mod tests {
.learn(Skill::HealPlus); .learn(Skill::HealPlus);
purge(&mut x, &mut y, vec![], Skill::Purge); purge(&mut x, &mut y, vec![], Skill::Purge);
// current turn + 2 turns at lvl 1 // 2 turns at lvl 1
assert!(y.effects.iter().any(|e| e.effect == Effect::Purge && e.duration == 3)); assert!(y.effects.iter().any(|e| e.effect == Effect::Purge && e.duration == 2));
assert!(y.disabled(Skill::Heal).is_some()); assert!(y.disabled(Skill::Heal).is_some());
} }
} }