Merge branch 'release/1.8.1'

This commit is contained in:
Mashy 2019-11-09 13:53:24 +10:00
commit 7e1a9d8585
37 changed files with 556 additions and 646 deletions

View File

@ -1,9 +1,39 @@
# Change Log ## [1.8.1] - 2019-11-07
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [1.8.0] - 2019-10-31 ### Fixed
# Added - An issue where skills would not be put on cooldown after being used.
### Changed
- Game phase
- Background for text overlapping with avatars in game phase (mobile)
- Fixed issue where effect text would show when not highlighted
- Avatar size doesn't decrease as effects are applied (now overlap)
- Added back KO! event when a construct is knocked out
- Invert
- Now reverses recharge into damage
- Link
- Reworked completely
- Now stuns target for 1T with 1T CD
- Deals 20/45/70% blue power multiplied by number of effects on target as blue damage
- Applies stun before effect multiplier calculation
- Restrict
- Changed cooldown from 2T -> 1T
- Duration now 2T at all levels
- Ruin
- Cooldown now 2T at all levels (down from 3T)
- Now deals damage to each target (40/70/100)%
- Silence
- Changed cooldown from 2T -> 1T
- Duration now 2T at all levels
## [1.8.0] - 2019-11-06
### Added
- Drag and drop for vbox interactions can be used instead of single click / double click - Drag and drop for vbox interactions can be used instead of single click / double click
- Base white items and be directly equipped from vbox rather than going through the inventory - Base white items and be directly equipped from vbox rather than going through the inventory
@ -11,7 +41,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- You can swap skills and specs between constructs without using the inventory - You can swap skills and specs between constructs without using the inventory
# Changed ### Changed
- Construct life changed - Construct life changed
- You now start with 800 green life (down from 950) - You now start with 800 green life (down from 950)
@ -28,7 +58,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Game constructs and animations are much larger in mobile view - Game constructs and animations are much larger in mobile view
- Amplify - Amplify
Now increases green power - Now increases green power
- Absorb - Absorb
- Reduced duration and cooldown from 2T -> 1T (Absorption duration unchanged) - Reduced duration and cooldown from 2T -> 1T (Absorption duration unchanged)
@ -36,13 +66,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Now recharges blue life based on 95 / 120 / 155 blue power - Now recharges blue life based on 95 / 120 / 155 blue power
- Banish - Banish
Reduced cooldown to 1T - Reduced cooldown to 1T
- Decay - Decay
Removed cooldown - Removed cooldown
- Haste / Hybrid - Haste / Hybrid
Fixed issue when hybridblast and hastestrike wouldn't trigger from upgraded + skills - Fixed issue when hybridblast and hastestrike wouldn't trigger from upgraded + skills
- Intercept - Intercept
- Reduced duration to 1T down from 2T - Reduced duration to 1T down from 2T
@ -50,7 +80,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [1.7.0] - 2019-10-31 ## [1.7.0] - 2019-10-31
# Added ### Added
- Step by step tutorial - Step by step tutorial
- Will activate during the learn game for the first round - Will activate during the learn game for the first round
- There is a button which will exit tutorial so you can continune the normal practice mode - There is a button which will exit tutorial so you can continune the normal practice mode
@ -59,7 +89,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- You can now preview what item combos will create! - You can now preview what item combos will create!
- Click into the item in the info section table (top right) and it will be replaced with the new item - Click into the item in the info section table (top right) and it will be replaced with the new item
# Changed ### Changed
- Vbox phase - Vbox phase
- Made general performance improvements - Made general performance improvements
- Removed the default info state (should be smoother to navigate now) - Removed the default info state (should be smoother to navigate now)
@ -71,7 +101,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Made general performance improvements - Made general performance improvements
- Now has default tutorial text for the first round (tells the player to select skills and then the targets) - Now has default tutorial text for the first round (tells the player to select skills and then the targets)
- Moved the login page demo to a new info tab, increased the speed of demo and it now creates random combos - Moved the login page demo to a new info tab
- Increased the speed of demo and it now creates random combos
- Banish - Banish
- Cooldown reduced to 2T (was 3T) - Cooldown reduced to 2T (was 3T)
@ -131,18 +162,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [1.6.6] - 2019-10-27 ## [1.6.6] - 2019-10-27
# Added ### Added
- Offering of draws - Offering of draws
- Neither player receives a point if they agree to a draw - Neither player receives a point if they agree to a draw
- Bots automatically agree to draws - Bots automatically agree to draws
## [1.6.5] - 2019-10-25 ## [1.6.5] - 2019-10-25
# Fixed ### Fixed
- Stripe being blocked no longer causes unrecoverable error - Stripe being blocked no longer causes unrecoverable error
- Automatic ready up is now throttled after abandons - Automatic ready up is now throttled after abandons
- Player width styling - Player width styling
# Changed ### Changed
- Improved wiggle animation - Improved wiggle animation
- Intercept is now considered defensive by bots - Intercept is now considered defensive by bots
- Password restrictions relaxed - Password restrictions relaxed
@ -215,244 +246,3 @@ We've updated the UI during the vbox / buy phase to give a better indication of
* Controls * Controls
* Abandon button now asks for confirmation. * Abandon button now asks for confirmation.
## [1.1.5] - 2019-10-10
### Changed
`Recharge` Skill multiplier reduced 85/130/200 -> 70/110/170
`Absorption` Skill duration reduced 5/7/9 -> 3/5/7
## [1.1.4 2019-09-18]
### Changed
Removed self targetting, all skills can be used on any target
`Reflect` No cooldown, 1T duration
`Purify` No cooldown
`Recharge` No cooldown
`Banish`
Now deals 40 / 75 / 125% target red / blue life before applying banish debuff
Constant 2T duration at all levels
Constant 3T cooldown at all levels
`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 `Buff` as a skill
Increases Speed and RedDamage by 25%
Duration 2T
No CD
### Changed
`Sustain` now grants immunity to disables.
## [0.1.2] - 2019-05-07
### Added
New skill `Link `
Combines - Buff + BB
Links targets together so dmg taken is split
Recharge 140% source blue damage as blue life to target
New skill `Hybrid`
Combines - Buff + GB
New buff that does the following -
Increase target green damage by 50%
Blue attacks do an additional blast equal to 25% source green damage
### Fixed
- Ruin sends a skill event so ruin only casts once, followed by debuffs
- Client side skip for source strangling effect straight to POST_SKILL
### Changed
- Removed Empower (Buff + RR) -> combined effect with amplify
- Skill Slow removed
- Debuff is now a usable skill `(add buff as a usable skill also)`
Applies slow effect previously applied by skill Slow
Slow effect lasts 3T
Cooldown 1T
- Amplify
Changed to Buff + RB (was Buff + BB)
Inc red and blue multiplier changed 200% -> 150%
Increases both red and blue power.
- Attack
Multiplier changed 100% -> 80%
- Blast
Multiplier changed 130% -> 110%
- Chaos
Base Multiplier changed 50% -> 40%
RNG range changed from (0 - 20%) -> (0 - 30%)
Same dmg range but more RNG
- Curse
Inc red and blue multiplier changed 200% -> 150%
`(More reworks soon to make this skill fun)`
- Haste
Changed to Buff + RG (was Buff + RB)
Buff causes target to deal an extra attack when using red attack base skills (strike / slay / chaos)
Extra attack does 25% source speed as red damage
Cooldown increased to 2T
Speed bonus reduced 200% -> 150%
- Heal
Changed multiplier 120% -> 130%
- Counter
Changed duration 1T -> 2T
Changed cooldown 0T -> 2T
Now recharges 110% red damage as red life
CounterAttack multiplier reduced 100% -> 70%
- Siphon
Multiplier changed 30% -> 40%
- Strangle
No longer provides immunity to source or target
Damage multiplier for strangle tick changed 30% -> 65%
- Strike
Change multipliers T1/T2/T3 (110/130/150 -> 90/110/130)
- Intercept
Changed to Buff + RR (was Buff + RG)
Now recharges 80% source red damage as red life to target
- Break
Stun duration reduced from 2T -> 1T
Vulnerable dmg bonus reduced 200% -> 150%
- Triage
Multiplier changed 65% -> 75%
## [Unreleased]
## [0.1.1] - 2019-05-03
>>>>>>> rebalance
### Added
Event::Skill
needed to convey the use of skill which is followed by other events
used for skill Purify to show that it is used and then followed by removal and other events
New Skill `Sleep`
Combined using Stun + GG
Stuns target for 3T (might need to be 4T)
Deals 240% green damage (heal)
Concept - high duration stun with the drawback of a big heal on the target
Base cooldown 3T
(Could be played aggressively or defensively)
### Fixed
### Changed
Switch purify with reflect
- Purify
Now GG + block (was GB + block)
Now applies a small heal 45% multiplier green damage (power) for each debuff removed
- Reflect
Now GB + block (was GG + block)
Recharges blue life at 45% blue damage
- Server function recharge changed to take skill, red amount and blue amount as inputs
- Recharge Skill reworked
No longer restores full Red and Blue life
Now restores Red life and Blue life based on respective red and blue damage
Recharge value calculated at 85% multiplier with red and blue damage
Silence
No longer Stun + GB (already exists as debuff BB)
Now also deals damage amount of 55% base blue damage
This damage amount does 45% more damage per blue skill blocked
Maximum = (0.55)(1.35)*base_blue_damage
Cooldown changed 1T -> 2T
Debuff duration increased 2T -> 3T
Restrict
Now also deals damage amount of 40% base blue damage
This damage amount does 45% more damage per red skill blocked
Maximum = (0.40)(1.35)*base_red_damage
Cooldown changed 1T -> 2T
Debuff duration increased 2T -> 3T
Switch sustain with intercept
Sustain now GR + Block (was GR + Buff)
Intercept now GR + Buff
No longer self-target only
Hex is now Stun + GB (was Stun + RB)
Banish is now Stun + RB (was Stun + RG) for rng theme
Break is now Stun + RG (was Stun + GG)
- Better fit as it applies inc Red taken debuff (vulnerability)
## [0.1.0] - 2019-05-02
### Added
New skill `Chaos`
- 50% base red & blue with an additional rng 20% blue & red
- Combo'd with Attack + Red + Blue
New skill `Slay`
- 70% base red, heals (green damage) for equivalent dmg dealt
- Combo'd with Attack + Red + Green
New effect `Wither`
- Reduces green damage taken by 50%
### Fixed
- Siphon deals damage before applying heal
- Changed tests to incorporate skill damage multipliers
### Changed
- Changed Decay, Siphon and Purify vbox items
`Decay`
- 1 blue + 1 green + debuff
- now also applies new debuff `Wither`
- Cooldown increased 0T -> 1T
`Siphon`
- 1 blue + 1 green + attack
- Cooldown reduced 1T -> 0T
`Purify` 1 blue + 1 green + block
- Changed skill damage multipliers
`Blast` 100% -> 130%
`ElectrocuteTick` 100% -> 80%
`Decay` 50% -> 25%
`Heal` 100% -> 120%
`SiphonTick` 100% -> 30%
`StrangleTick` 100% -> 30%
`Strike` 100% -> 110%
`TriageTick` 100% -> 65%

View File

@ -1 +1 @@
1.8.0 1.8.1

View File

@ -8,15 +8,30 @@
* mobile info page * mobile info page
* Invert recharge
## SOON ## SOON
* supporter gold name in instance (anyone whos put any money into game)
* change cooldowns to delay & recharge * change cooldowns to delay & recharge
- delay is cooldown before skill can first be used - delay is cooldown before skill can first be used
- recharge is cooldown after using skill - recharge is cooldown after using skill
- every x speed reduces delay of skills - every x speed reduces delay of skills
* audio
* elo + leaderboards
* reconnect based on time delta
* ACP
* essential
## LATER
* theme toasts
* rework vecs into sets
* remove names so games/instances are copy
* consolidate game and instance
* combo rework * combo rework
- reduce number of items for creating t2/t3 items from 3 -> 2 - reduce number of items for creating t2/t3 items from 3 -> 2
- add lost complexity by adding skill spec items - add lost complexity by adding skill spec items
@ -27,55 +42,11 @@
- Strike + SpeedRR -> StrikeSpeed (strike has Y% more speed) - Strike + SpeedRR -> StrikeSpeed (strike has Y% more speed)
- Strike + LifeRR -> StrikeLife (Strike recharges X% of damage as red life) - Strike + LifeRR -> StrikeLife (Strike recharges X% of damage as red life)
* ACP
* essential
* audio
* treats
* susbcriber gold name in instance
* client animation bpm
* background colour changes depending on time of day
* rework vecs into sets
* remove names so games/instances are copy
*$$$*
* instead of red noise, red and black bar gradient
* eth adapter
* illusions
* vaporwave
* crop circles
* insects
* sacred geometry
* skulls / day of the dead
* Aztec
* youkai
* Industrial
*CLIENT*
theme toasts
reconnect based on time delta
consolidate game and instance
* return of the combat log (last few events with condensed descriptions)
- click in to scroll
* elo + leaderboards
* reflect event stages (for animations)
* mnml tv
## LATER
* constants * constants
* bot game grind * (maybe) return of the combat log (last few events with condensed descriptions)
- click in to scroll
$$$ * mnml tv
* Items
* Colour scheme
* targeting highlight colour
* number of constructs
* Highlight (dota) colour
* fx colours + styles
* modules * modules
* troll life -> dmg * troll life -> dmg
@ -84,6 +55,28 @@ $$$
* fuck magic * fuck magic
* empower on ko * empower on ko
## $$$
* Items
* instead of red noise, red and black bar gradient
* eth adapter
*sets*
* illusions
* vaporwave
* crop circles
* insects
* sacred geometry
* skulls / day of the dead
* Aztec
* youkai
* Industrial
* Colour scheme
* targeting highlight colour
* Highlight (dota) colour
* fx colours + styles
* treats
* client animation bpm
* background colour changes depending on time of day
# Mechanics # Mechanics
* 10d chaos maths, not rock paper scissors * 10d chaos maths, not rock paper scissors
* phys is faster and chaotic * phys is faster and chaotic

View File

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

View File

@ -1,14 +1,3 @@
require('./assets/styles/styles.less'); require('./assets/styles/styles.less');
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/vbox.less');
require('./assets/styles/game.less');
require('./assets/styles/player.less');
require('./assets/styles/styles.mobile.less');
require('./assets/styles/instance.mobile.less');
require('./src/animations.test.jsx'); require('./src/animations.test.jsx');

View File

@ -1,5 +1,3 @@
@import 'colours.less';
.account { .account {
grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr;

View File

@ -140,7 +140,7 @@ svg {
} }
} }
button { button, button:hover, button:active {
&.blue { &.blue {
border-color: @blue; border-color: @blue;
} }
@ -150,6 +150,16 @@ button {
&.green { &.green {
border-color: @green; border-color: @green;
} }
&.red-border {
border-color: @red;
}
&.blue-border {
border-color: @blue;
}
&.green-border {
border-color: @green;
}
} }
@keyframes rgb { @keyframes rgb {
@ -181,3 +191,12 @@ button {
color: @blue; color: @blue;
} }
} }
@keyframes target-ko {
0% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}

View File

@ -1,5 +1,3 @@
@import 'colours.less';
aside { aside {
grid-area: ctrl; grid-area: ctrl;
display: grid; display: grid;

View File

@ -1,6 +1,3 @@
@import 'colours.less';
/* GAME */
.game { .game {
overflow: hidden; overflow: hidden;
// display: grid; // display: grid;
@ -10,10 +7,6 @@
// "opponent" // "opponent"
// "target " // "target "
// "player "; // "player ";
.skill-description {
font-size: 75%;
}
} }
.game .team, .faceoff .team { .game .team, .faceoff .team {
@ -35,6 +28,14 @@
position: absolute; position: absolute;
bottom: 0; bottom: 0;
height: 50%; height: 50%;
.avatar {
z-index: 1;
position: absolute;
top: 0;
height: 100%;
width: 100%;
}
} }
.opponent { .opponent {
@ -53,6 +54,13 @@
"right" "right"
"left"; "left";
.avatar {
position: absolute;
top: 3em;
height: 100%;
width: 100%;
}
.right { .right {
height: 100%; height: 100%;
display: grid; display: grid;
@ -60,7 +68,7 @@
grid-template-areas: grid-template-areas:
"stats" "stats"
"name" "name"
"avatar"; "avatar"
} }
.effects { .effects {
@ -86,7 +94,7 @@
justify-items: center; justify-items: center;
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: 1fr 2fr; grid-template-rows: minmax(min-content, 1fr) min-content;
grid-template-areas: grid-template-areas:
"left" "left"
"right"; "right";
@ -130,6 +138,10 @@
margin-bottom: 0.25em; margin-bottom: 0.25em;
text-align: center; text-align: center;
grid-area: name; grid-area: name;
z-index: 2;
span {
background-color: black;
}
} }
.skills { .skills {
@ -138,9 +150,15 @@
width: 100%; width: 100%;
height: 2em; height: 2em;
margin-right: 1em; margin-right: 1em;
span {
background-color: black;
}
} }
button.active { button.active {
background: #2c2c2c; background: #2c2c2c;
span {
background-color: #2c2c2c;
}
} }
} }
@ -151,6 +169,9 @@
width: 100%; width: 100%;
text-align: center; text-align: center;
font-size: 1.5em; font-size: 1.5em;
span {
background-color: black;
}
} }
.stats { .stats {
@ -177,6 +198,10 @@
font-size: 100%; font-size: 100%;
} }
&.ko-transition {
animation: target-ko 1s ease-in-out 0s 1;
}
&.ko { &.ko {
animation: none; animation: none;
opacity: 0.20; opacity: 0.20;
@ -214,24 +239,36 @@
top: 35%; top: 35%;
height: 15%; height: 15%;
width: calc(90% - 1.25em); width: calc(90% - 1.25em);
z-index: 2;
span {
background-color: black;
}
} }
.resolving-skill { .resolving-skill {
grid-area: target; grid-area: target;
text-align: center; text-align: center;
align-self: center; align-self: center;
height: auto; // height: auto;
svg { svg {
display: inline; display: inline;
height: 1em; height: 1em;
margin-right: 0.1em margin-right: 0.1em
} }
display: flex;
flex-flow: column;
justify-content: center;
} }
.skill-description { .skill-description {
padding-left: 1em; padding-left: 1em;
padding-right: 1em; padding-right: 1em;
text-align: center; text-align: center;
z-index: 2;
span {
background-color: black;
}
svg { svg {
display: inline; display: inline;
height: 1em; height: 1em;

View File

@ -1,5 +1,3 @@
@import 'colours.less';
.instance { .instance {
overflow: hidden; overflow: hidden;
display: grid; display: grid;
@ -349,7 +347,7 @@
text-align: center; text-align: center;
overflow: hidden; overflow: hidden;
display: grid; display: grid;
grid-template-rows: 1fr 0.5fr 1.5fr; grid-template-rows: 1fr 0.5fr 1fr;
grid-template-areas: grid-template-areas:
"opponent" "opponent"
"text" "text"
@ -379,11 +377,15 @@
.faceoff-text { .faceoff-text {
grid-area: text; grid-area: text;
font-size: 200%;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 1em; letter-spacing: 1em;
font-weight: bold; font-weight: bold;
display: flex;
flex-flow: column;
justify-content: center;
color: @black; color: @black;
animation: faceoff 4s ease-in-out 0s 2 alternate; animation: faceoff 4s ease-in-out 0s 2 alternate;

View File

@ -1,5 +1,3 @@
@import 'colours.less';
// tablet / ipad // tablet / ipad
@media (max-width: 1100px) { @media (max-width: 1100px) {
.instance { .instance {

View File

@ -1,5 +1,3 @@
@import 'colours.less';
.menu { .menu {
height: 100%; height: 100%;
display: grid; display: grid;

View File

@ -1,5 +1,3 @@
@import 'colours.less';
.player-box { .player-box {
display: grid; display: grid;
overflow: hidden; overflow: hidden;

View File

@ -1,4 +1,15 @@
@import 'colours.less'; @import 'colours.less';
@import 'account.less';
@import 'menu.less';
@import 'nav.less';
@import 'footer.less';
@import 'controls.less';
@import 'instance.less';
@import 'vbox.less';
@import 'game.less';
@import 'player.less';
@import 'styles.mobile.less';
@import 'instance.mobile.less';
html body { html body {
margin: 0; margin: 0;

View File

@ -40,7 +40,7 @@
} }
svg { svg {
height: 1em; height: 1.5em;
} }
} }
@ -54,6 +54,13 @@
font-size: 1em; font-size: 1em;
} }
.skill-description {
font-size: 0.8em;
svg {
height: 1em;
}
}
.player { .player {
.game-construct { .game-construct {
grid-template-areas: grid-template-areas:
@ -85,7 +92,7 @@
} }
.avatar { .avatar {
bottom: 0px; top: 3em;
} }
} }
} }

View File

@ -1,5 +1,3 @@
@import 'colours.less';
.vbox { .vbox {
margin-bottom: 2em; margin-bottom: 2em;

View File

@ -1,15 +1,4 @@
require('./assets/styles/styles.less'); require('./assets/styles/styles.less');
require('./assets/styles/account.less');
require('./assets/styles/menu.less');
require('./assets/styles/nav.less');
require('./assets/styles/footer.less');
require('./assets/styles/controls.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.less');
require('./assets/styles/instance.mobile.less');
// kick it off // kick it off
require('./src/app'); require('./src/app');

View File

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

View File

@ -30,46 +30,54 @@ 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 // 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 (!r.event) return cb();
const timeout = animations.getTime(r.stages);
// convert server enum into anims keywords const anims = animations.getObjects(r, game, account);
// todo make serersideonly const text = animations.getText(r);
const sequence = animations.getSequence(r);
const timeout = animations.getTime(sequence);
const anims = animations.getObjects(r, sequence, game, account);
const text = animations.getText(r, sequence);
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game))); store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
if (anims.animSkill) store.dispatch(actions.setAnimSkill(anims.animSkill));
if (sequence.includes('START_SKILL') && anims.animSource) store.dispatch(actions.setAnimSource(anims.animSource)); if (r.stages.includes('START_SKILL') && anims.animSource) {
if (sequence.includes('END_SKILL') && anims.animTarget) { store.dispatch(actions.setAnimSource(anims.animSource));
}
if (r.stages.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 (animations.isCbAnim(anims.animSkill)) store.dispatch(actions.setAnimCb(cb));
} }
if (sequence.includes('POST_SKILL' && text)) {
if (r.stages.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( if (timeout === TIMES.POST_SKILL_DURATION_MS) {
() => store.dispatch(actions.setAnimText(text)), store.dispatch(actions.setAnimText(text));
timeout - TIMES.POST_SKILL_DURATION_MS } else {
); setTimeout(
() => store.dispatch(actions.setAnimText(text)),
timeout - TIMES.POST_SKILL_DURATION_MS
);
}
} }
return setTimeout(() => { return setTimeout(() => {
store.dispatch(actions.setAnimSkill(null));
store.dispatch(actions.setAnimSource(null)); store.dispatch(actions.setAnimSource(null));
store.dispatch(actions.setAnimTarget(null)); store.dispatch(actions.setAnimTarget(null));
store.dispatch(actions.setAnimText(null)); store.dispatch(actions.setAnimText(null));
store.dispatch(actions.setAnimFocus([])); store.dispatch(actions.setAnimFocus([]));
if (!sequence.includes('END_SKILL') if (r.stages.includes('END_SKILL') && animations.isCbAnim(anims.animSkill)) return true;
|| ['Banish', 'Invert'].includes(removeTier(anims.animTarget.skill))) return cb(); return cb();
return true;
}, timeout); }, timeout);
}, err => { }, err => {
if (err) return console.error(err); if (err) return console.error(err);
// clear animation state // clear animation state
store.dispatch(actions.setAnimSkill(null));
store.dispatch(actions.setAnimSource(null)); store.dispatch(actions.setAnimSource(null));
store.dispatch(actions.setAnimTarget(null)); store.dispatch(actions.setAnimTarget(null));
store.dispatch(actions.setAnimText(null)); store.dispatch(actions.setAnimText(null));
store.dispatch(actions.setAnimating(false)); store.dispatch(actions.setAnimating(false));
store.dispatch(actions.setGameEffectInfo(null));
store.dispatch(actions.setSkip(false)); store.dispatch(actions.setSkip(false));
// set the game state so resolutions don't fire twice // set the game state so resolutions don't fire twice

View File

@ -111,7 +111,7 @@ function getText(resolution) {
function generatePostSkill() { function generatePostSkill() {
const [type, event] = resolution.event; const [type, event] = resolution.event;
if (type === 'Ko') { if (type === 'Ko') {
return { text: 'KO!', css: 'ko' }; return { text: 'KO!', css: 'ko-transition' };
} }
if (type === 'Disable') { if (type === 'Disable') {

View File

@ -20,6 +20,7 @@ function projectile(x, y, radius, colour) {
cy={y} cy={y}
r={radius} r={radius}
fill={colour} fill={colour}
stroke="none"
filter={colour === '#a52a2a' ? 'url(#chaosRedFilter)' : 'url(#chaosBlueFilter)'} filter={colour === '#a52a2a' ? 'url(#chaosRedFilter)' : 'url(#chaosBlueFilter)'}
/> />
); );

View File

@ -18,7 +18,7 @@ class Refl extends Component {
this.animations = []; this.animations = [];
} }
render({ team }) { render({ player }) {
const oneX = anime.random(32, 96); const oneX = anime.random(32, 96);
const twoX = anime.random(192, 224); const twoX = anime.random(192, 224);
@ -28,7 +28,7 @@ class Refl extends Component {
version="1.1" version="1.1"
id="reflect" id="reflect"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
style={{ transform: team ? '' : 'rotate3d(1, 0, 0, 180deg)' }} style={{ transform: player ? '' : 'rotate3d(1, 0, 0, 180deg)' }}
viewBox="0 0 256 256"> viewBox="0 0 256 256">
<defs> <defs>
<filter id="reflectFilterGreen"> <filter id="reflectFilterGreen">

View File

@ -20,6 +20,7 @@ function projectile(x, y, radius, colour) {
<circle <circle
cx={x} cx={x}
cy={y} cy={y}
stroke="none"
r={radius} r={radius}
fill={colour} fill={colour}
/> />
@ -28,7 +29,7 @@ function projectile(x, y, radius, colour) {
function sword(colour) { function sword(colour) {
return ( return (
<polygon points='150,150 100,75, 150,300, 200,75' fill={colour} id="sword" filter="url(#slayFilter)"></polygon> <polygon points='150,150 100,75, 150,300, 200,75' stroke="none" fill={colour} id="sword" filter="url(#slayFilter)"></polygon>
); );
} }

View File

@ -8,33 +8,33 @@ module.exports = {
Slay: () => 'red-green-border', Slay: () => 'red-green-border',
Siphon: () => 'blue-green-border', Siphon: () => 'blue-green-border',
// Stun // Stun
Link: () => 'blue-green-border',
Bash: () => 'red-border', Bash: () => 'red-border',
Sleep: () => 'green-border',
Ruin: () => 'blue-border', Ruin: () => 'blue-border',
Break: () => 'red-green-border', Sleep: () => 'green-border',
Banish: () => 'red-blue-border', Banish: () => 'red-blue-border',
Break: () => 'red-green-border',
Link: () => 'blue-green-border',
// Block // Block
Counter: () => 'red-border', Counter: () => 'red-border',
Purify: () => 'green-border',
Electrify: () => 'blue-border', Electrify: () => 'blue-border',
Purify: () => 'green-border',
Recharge: () => 'red-blue-border',
Sustain: () => 'red-green-border', Sustain: () => 'red-green-border',
Reflect: () => 'blue-green-border', Reflect: () => 'blue-green-border',
Recharge: () => 'blue-red-border',
// Buff // Buff
Intercept: () => 'red-border', Intercept: () => 'red-border',
Triage: () => 'green-border',
Haste: () => 'red-green-border',
Absorb: () => 'blue-border', Absorb: () => 'blue-border',
Hybrid: () => 'blue-green-border', Triage: () => 'green-border',
Amplify: () => 'red-blue-border', Amplify: () => 'red-blue-border',
Haste: () => 'red-green-border',
Hybrid: () => 'blue-green-border',
// Debuff // Debuff
Restrict: () => 'red-border', Restrict: () => 'red-border',
Purge: () => 'green-border',
Silence: () => 'blue-border', Silence: () => 'blue-border',
Purge: () => 'green-border',
Curse: () => 'red-blue-border',
Invert: () => 'red-green-border', Invert: () => 'red-green-border',
Decay: () => 'blue-green-border', Decay: () => 'blue-green-border',
Curse: () => 'red-blue-border',
// // Lifes Upgrades // // Lifes Upgrades
// LifeGG: () => 'green-border', // LifeGG: () => 'green-border',

View File

@ -111,7 +111,7 @@ function constructText(props) {
? animText.text ? animText.text
: construct.name; : construct.name;
return <h3 class={'name'}>{text}</h3>; return <h3 class={'name'}><span>{text}</span></h3>;
} }
module.exports = { module.exports = {

View File

@ -117,7 +117,8 @@ class GameConstruct extends Component {
setGameEffectInfo, setGameEffectInfo,
} = this.props; } = this.props;
const ko = construct.green_life.value === 0 ? 'ko' : ''; const koEvent = animText ? animText.text === 'KO!' && animText.constructId === construct.id : false;
const ko = construct.green_life.value === 0 && !koEvent ? 'ko' : '';
const classes = eventClasses(animating, animFocus, construct, animText); const classes = eventClasses(animating, animFocus, construct, animText);
const stats = ['RedLife', 'GreenLife', 'BlueLife'].map((s, j) => ( const stats = ['RedLife', 'GreenLife', 'BlueLife'].map((s, j) => (
@ -143,21 +144,21 @@ class GameConstruct extends Component {
const fullInfo = itemInfo.items.find(k => k.item === gameSkillInfo.skill) || INFO[gameSkillInfo.skill]; const fullInfo = itemInfo.items.find(k => k.item === gameSkillInfo.skill) || INFO[gameSkillInfo.skill];
const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat)/; const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat)/;
const infoDescription = reactStringReplace(fullInfo.description, regEx, match => shapes[match]()); const infoDescription = reactStringReplace(fullInfo.description, regEx, match => shapes[match]());
const speed = <div> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </div>; const speed = <span> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </span>;
return ( return (
<div class="skill-description"> <div class="skill-description">
<h2> {gameSkillInfo.skill} </h2> <h2><span> {gameSkillInfo.skill} </span></h2>
<div> {infoDescription} </div> <span>{infoDescription} </span>
{speed} {speed}
</div>); </div>);
} }
const effects = construct.effects.length const effects = construct.effects.length
? construct.effects.map(c => ? construct.effects.map(c =>
<div <div key={c.effect}>
key={c.effect} <span key={c.effect} onMouseOver={e => hoverInfo(e, c)}
onMouseOver={e => hoverInfo(e, c)} onMouseOut={e => hoverInfo(e, null)}> {c.effect} - {c.duration}T
onMouseOut={e => hoverInfo(e, null)} </span>
> {c.effect} - {c.duration}T</div>) </div>)
: null; : null;
return (<div class="effects"> {effects} </div>); return (<div class="effects"> {effects} </div>);
}; };

View File

@ -4,7 +4,6 @@ const { connect } = require('preact-redux');
const Main = require('./main'); const Main = require('./main');
// const Nav = require('./nav'); // const Nav = require('./nav');
const Controls = require('./controls'); const Controls = require('./controls');
const Footer = require('./footer');
const addState = connect( const addState = connect(
({ game, instance }) => ({ game, instance }) ({ game, instance }) => ({ game, instance })
@ -24,7 +23,6 @@ function Mnml(args) {
<div id="mnml"> <div id="mnml">
<Main /> <Main />
<Controls /> <Controls />
<Footer />
<div id="rotate" class={rotateClass} > <div id="rotate" class={rotateClass} >
</div> </div>
</div> </div>

View File

@ -91,7 +91,7 @@ function Skill(props) {
onMouseOut={e => hoverInfo(e, null)} onMouseOut={e => hoverInfo(e, null)}
type="submit" type="submit"
onClick={onClick}> onClick={onClick}>
{s.skill} {cdText} <span>{s.skill} {cdText}</span>
</button> </button>
); );
} }

View File

@ -52,6 +52,7 @@ class TargetSvg extends Component {
itemInfo, itemInfo,
} = props; } = props;
const { width, height } = state; const { width, height } = state;
if (!game) return false; // game will be null when battle ends if (!game) return false; // game will be null when battle ends
if (game.phase === 'Finished') return false; // Clear everything if its over (in case of abandon) if (game.phase === 'Finished') return false; // Clear everything if its over (in case of abandon)
@ -59,7 +60,7 @@ class TargetSvg extends Component {
if (tutorialGame) { if (tutorialGame) {
return ( return (
<div class="resolving-skill"> <div class="resolving-skill">
<h2> Select your skills, click on targets and then hit <b>READY</b>.</h2> <h2>Select your skills, click on targets and then hit <b>READY</b>.</h2>
</div> </div>
); );
} }
@ -73,7 +74,7 @@ class TargetSvg extends Component {
return ( return (
<div class="resolving-skill"> <div class="resolving-skill">
<h1>{gameEffectInfo.effect}</h1> <h1>{gameEffectInfo.effect}</h1>
<div> {infoDescription} </div> <div>{infoDescription}</div>
</div> </div>
); );
} }
@ -91,7 +92,7 @@ class TargetSvg extends Component {
return ( return (
<div class="resolving-skill"> <div class="resolving-skill">
<h1>{animSkill}</h1> <h1>{animSkill}</h1>
<div> {itemSourceDescription} </div> <div>{itemSourceDescription}</div>
</div> </div>
); );
} }

View File

@ -74,7 +74,7 @@ function registerEvents(store) {
// stop fetching the game state til animations are done // stop fetching the game state til animations are done
const newRes = game.resolved.slice(currentGame.resolved.length); const newRes = game.resolved.slice(currentGame.resolved.length);
return eachSeries(newRes, (r, cb) => { return eachSeries(newRes, (r, cb) => {
if (!r.event || r.stages === '') return cb(); if (!r.event) return cb();
const timeout = animations.getTime(r.stages); const timeout = animations.getTime(r.stages);
const anims = animations.getObjects(r, game, account); const anims = animations.getObjects(r, game, account);
const text = animations.getText(r); const text = animations.getText(r);

View File

@ -240,6 +240,8 @@ function convertItem(v) {
} }
function effectInfo(i) { function effectInfo(i) {
const hybridBlast = 25;
const hasteStrike = 30;
function multiplier(s) { // Update later to use server info in future function multiplier(s) { // Update later to use server info in future
if (s === 'CounterAttack') return 120; if (s === 'CounterAttack') return 120;
if (s === 'CounterAttack+') return 160; if (s === 'CounterAttack+') return 160;
@ -270,9 +272,9 @@ function effectInfo(i) {
case 'Buff': return `Increases construct RedPower BluePower SpeedStat by ${i.meta[1] - 100}%`; case 'Buff': return `Increases construct RedPower BluePower SpeedStat by ${i.meta[1] - 100}%`;
case 'Sustain': return 'Construct cannot be KO while active. Additionally provides immunity to disables'; case 'Sustain': return 'Construct cannot be KO while active. Additionally provides immunity to disables';
case 'Curse': return `Construct will take ${i.meta[1] - 100}% increased red and blue damage`; case 'Curse': return `Construct will take ${i.meta[1] - 100}% increased red and blue damage`;
case 'Haste': return `Construct has ${i.meta[1] - 100}% increased SpeedStat. Red attack skills will trigger a HasteStrike dealing 30% SpeedStat as red damage.`; case 'Haste': return `Construct has ${i.meta[1] - 100}% increased SpeedStat. Red attack skills will trigger a HasteStrike dealing ${hasteStrike}% SpeedStat as red damage.`;
case 'Hybrid': return `Construct has ${i.meta[1] - 100}% increased GreenPower. Blue attack skills will trigger a HybridBlast dealing 25% GreenPower as red damage.`; case 'Hybrid': return `Construct has ${i.meta[1] - 100}% increased GreenPower. Blue attack skills will trigger a HybridBlast dealing ${hybridBlast}% GreenPower as red damage.`;
case 'Invert': return 'Reverses damage and healing. Healing will damage this construct and damage will heal.'; case 'Invert': return 'Reverse healing/recharge into damage and damage into healing/recharge.';
case 'Counter': return `Red damage taken by this construct will trigger a CounterAttack. CounterAttack deals ${multiplier(i.meta[1])}% RedPower as red damage.`; case 'Counter': return `Red damage taken by this construct will trigger a CounterAttack. CounterAttack deals ${multiplier(i.meta[1])}% RedPower as red damage.`;
case 'Purge': return 'Disable construct from casting any green skills'; case 'Purge': return 'Disable construct from casting any green skills';
case 'Reflect': return 'Reflect blue skills back to caster'; case 'Reflect': return 'Reflect blue skills back to caster';

2
ops/package.json Executable file → Normal file
View File

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

View File

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

View File

@ -549,36 +549,104 @@ impl Construct {
} }
} }
pub fn recharge(&mut self, skill: Skill, red_amount: u64, blue_amount: u64) -> Event { pub fn recharge(&mut self, skill: Skill, red_amount: u64, blue_amount: u64) -> Vec<Event> {
let mut events = vec![];
// Should red type immunity block recharge??? // Should red type immunity block recharge???
if let Some(immunity) = self.immune(skill) { if let Some(immunity) = self.immune(skill) {
return Event::Immunity { if !self.is_ko() {
skill, events.push(Event::Immunity { skill, immunity });
immunity, }
}; return events;
} }
// Do we need inversion? match self.affected(Effect::Invert) {
let current_red_life = self.red_life(); false => {
self.red_life.increase(red_amount); // Do we need inversion?
let new_red_life = self.red_life.value; let current_red_life = self.red_life();
let red = new_red_life - current_red_life; self.red_life.increase(red_amount);
let new_red_life = self.red_life.value;
let red = new_red_life - current_red_life;
let current_blue_life = self.blue_life(); let current_blue_life = self.blue_life();
self.blue_life.increase(blue_amount); self.blue_life.increase(blue_amount);
let new_blue_life = self.blue_life.value; let new_blue_life = self.blue_life.value;
let blue = new_blue_life - current_blue_life; let blue = new_blue_life - current_blue_life;
Event::Recharge { red, blue, skill } if red != 0 || blue != 0 {
events.push(Event::Recharge { red, blue, skill });
}
},
true => {
// Recharge takes a red and blue amount so check for them
if red_amount != 0 {
let red_mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken))
.map(|e| (e.effect, e.meta))
.collect::<Vec<(Effect, Option<EffectMeta>)>>();
let red_modified_power = red_mods.iter()
.fold(red_amount, |acc, fx| fx.0.apply(acc, fx.1));
let red_remainder = red_modified_power.saturating_sub(self.red_life.value);
let red_mitigation = red_modified_power.saturating_sub(red_remainder);
// reduce red_life by mitigation amount
self.red_life.reduce(red_mitigation);
// deal remainder to green_life
let red_current_green_life = self.green_life();
self.reduce_green_life(red_remainder);
let red_damage_amount = red_current_green_life - self.green_life();
events.push(Event::Damage {
skill,
amount: red_damage_amount,
mitigation: red_mitigation,
colour: Colour::Red
});
}
if blue_amount != 0 {
let blue_mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken))
.map(|e| (e.effect, e.meta))
.collect::<Vec<(Effect, Option<EffectMeta>)>>();
let blue_modified_power = blue_mods.iter()
.fold(blue_amount, |acc, fx| fx.0.apply(acc, fx.1));
let blue_remainder = blue_modified_power.saturating_sub(self.blue_life.value);
let blue_mitigation = blue_modified_power.saturating_sub(blue_remainder);
// reduce blue_life by mitigation amount
self.blue_life.reduce(blue_mitigation);
// deal remainder to green_life
let blue_current_green_life = self.green_life();
self.reduce_green_life(blue_remainder);
let blue_damage_amount = blue_current_green_life - self.green_life();
events.push(Event::Damage {
skill,
amount: blue_damage_amount,
mitigation: blue_mitigation,
colour: Colour::Blue
});
}
}
}
return events;
} }
pub fn deal_green_damage(&mut self, skill: Skill, amount: u64) -> Vec<Event> { pub fn deal_green_damage(&mut self, skill: Skill, amount: u64) -> Vec<Event> {
let mut events = vec![]; let mut events = vec![];
if let Some(immunity) = self.immune(skill) { if let Some(immunity) = self.immune(skill) {
events.push(Event::Immunity { if !self.is_ko() {
immunity, events.push(Event::Immunity { skill, immunity });
skill, }
});
return events; return events;
} }
@ -629,10 +697,9 @@ impl Construct {
let mut events = vec![]; let mut events = vec![];
if let Some(immunity) = self.immune(skill) { if let Some(immunity) = self.immune(skill) {
events.push(Event::Immunity { if !self.is_ko() {
skill, events.push(Event::Immunity { skill, immunity });
immunity, }
});
return events; return events;
} }
@ -702,10 +769,9 @@ impl Construct {
let mut events = vec![]; let mut events = vec![];
if let Some(immunity) = self.immune(skill) { if let Some(immunity) = self.immune(skill) {
events.push(Event::Immunity { if !self.is_ko() {
skill, events.push(Event::Immunity { skill, immunity });
immunity, }
});
return events; return events;
} }

View File

@ -469,6 +469,14 @@ impl Game {
let mut resolutions = resolution_steps(&cast, &mut self); let mut resolutions = resolution_steps(&cast, &mut self);
r_animation_ms = resolutions.iter().fold(r_animation_ms, |acc, r| acc + r.clone().get_delay()); r_animation_ms = resolutions.iter().fold(r_animation_ms, |acc, r| acc + r.clone().get_delay());
// the cast itself goes into this temp vec to handle cooldowns
// if theres no resolution events, the skill didn't trigger (disable etc)
if resolutions.len() > 0 {
casts.push(cast);
}
self.resolved.append(&mut resolutions); self.resolved.append(&mut resolutions);
// while let Some(resolution) = resolutions.pop() { // while let Some(resolution) = resolutions.pop() {
@ -477,10 +485,6 @@ impl Game {
// self.resolved.push(resolution); // self.resolved.push(resolution);
// } // }
// the cast itself goes into this temp vec
// to handle cooldowns
casts.push(cast);
// sort the stack again in case speeds have changed // sort the stack again in case speeds have changed
self.stack_sort_speed(); self.stack_sort_speed();
}; };
@ -506,14 +510,11 @@ impl Game {
} }
// only reduce cooldowns if no cd was used // only reduce cooldowns if no cd was used
// have to borrow self for the skill check
{ {
if let Some(skill) = resolved.iter().find(|s| s.source_construct_id == construct.id) { if let Some(skill) = resolved.iter()
if skill.used_cooldown() { .filter(|s| s.source_construct_id == construct.id)
.find(|s| s.used_cooldown()) {
construct.skill_set_cd(skill.skill); construct.skill_set_cd(skill.skill);
} else {
construct.reduce_cooldowns();
}
} else { } else {
construct.reduce_cooldowns(); construct.reduce_cooldowns();
} }
@ -984,7 +985,9 @@ mod tests {
.learn(Skill::Siphon) .learn(Skill::Siphon)
.learn(Skill::Amplify) .learn(Skill::Amplify)
.learn(Skill::Stun) .learn(Skill::Stun)
.learn(Skill::Block); .learn(Skill::Block)
.learn(Skill::Sleep)
.learn(Skill::Decay);
let mut y = Construct::new() let mut y = Construct::new()
.named(&"lemongrass tea".to_string()) .named(&"lemongrass tea".to_string())
@ -1199,6 +1202,55 @@ mod tests {
assert!(game.player_by_id(y_player.id).unwrap().constructs[0].skill_on_cd(Skill::Block).is_none()); assert!(game.player_by_id(y_player.id).unwrap().constructs[0].skill_on_cd(Skill::Block).is_none());
} }
#[test]
fn sleep_cooldown_test() {
let mut game = create_test_game();
let x_player = game.players[0].clone();
let y_player = game.players[1].clone();
let x_construct = x_player.constructs[0].clone();
let y_construct = y_player.constructs[0].clone();
for _n in 1..10 {
// should auto progress back to skill phase
assert!(game.phase == Phase::Skill);
// Sleep 2T CD
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Decay).is_none());
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Sleep).is_some());
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
// Sleep 1T CD
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Decay).is_none());
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Sleep).is_some());
game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Decay).unwrap();
// game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Attack).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
// Sleep 0T CD (we use it here)
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Decay).is_none());
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Sleep).is_none());
game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Sleep).unwrap();
game.player_ready(x_player.id).unwrap();
game.player_ready(y_player.id).unwrap();
game = game.resolve_phase_start();
// Sleep back to 2T CD
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Decay).is_none());
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Sleep).is_some());
}
}
#[test] #[test]
fn counter_test() { fn counter_test() {
let mut game = create_test_game(); let mut game = create_test_game();
@ -1390,6 +1442,7 @@ mod tests {
true true
}, },
Event::AoeSkill { skill: _ } => false, Event::AoeSkill { skill: _ } => false,
Event::Damage { amount: _, mitigation: _, colour: _, skill: _ } => false,
_ => panic!("ruin result not effect {:?}", event), _ => panic!("ruin result not effect {:?}", event),
} }
false => false, false => false,
@ -1428,7 +1481,7 @@ mod tests {
game = game.resolve_phase_start(); game = game.resolve_phase_start();
assert!(game.resolved.len() == 5); assert!(game.resolved.len() == 4);
while let Some(r) = game.resolved.pop() { while let Some(r) = game.resolved.pop() {
let Resolution { source , target, event: _, stages: _ } = r; let Resolution { source , target, event: _, stages: _ } = r;
if [i_construct.id, j_construct.id].contains(&source.id) { if [i_construct.id, j_construct.id].contains(&source.id) {

View File

@ -807,8 +807,8 @@ impl Item {
Item::Invert| Item::Invert|
Item::InvertPlus | Item::InvertPlus |
Item::InvertPlusPlus => format!( Item::InvertPlusPlus => format!(
"Reverse healing into damage and damage into healing. "Reverse healing/recharge into damage and damage into healing/recharge.
Any excess red or blue damage is converted into shield recharge. Any excess red or blue damage is converted into shield recharge after healing.
Lasts {:?}T.", Lasts {:?}T.",
self.into_skill().unwrap().effect()[0].get_duration()), self.into_skill().unwrap().effect()[0].get_duration()),
@ -851,17 +851,18 @@ impl Item {
Item::Ruin| Item::Ruin|
Item::RuinPlus | Item::RuinPlus |
Item::RuinPlusPlus => format!( Item::RuinPlusPlus => format!(
"Team wide Stun for {:?}T. Stunned constructs are unable to cast skills.", "Team wide skill. Stun each construct for {:?}T.
self.into_skill().unwrap().effect()[0].get_duration()), Deal {:?}% BluePower as blue damage to each construct.",
self.into_skill().unwrap().effect()[0].get_duration(),
self.into_skill().unwrap().multiplier()),
Item::Link| Item::Link|
Item::LinkPlus | Item::LinkPlus |
Item::LinkPlusPlus => format!( Item::LinkPlusPlus => format!(
"Swap {:?}% of green life difference as blue damage to the target and healing to the caster. "Stun target for {:?}T.
The swap only occurs if the target construct has more green life than caster. Deal blue damage of {:?}% BluePower multiplied by number of effects on target.",
Stuns caster for {:?}T in the process.", self.into_skill().unwrap().effect()[0].get_duration(),
self.into_skill().unwrap().multiplier(), self.into_skill().unwrap().multiplier()),
self.into_skill().unwrap().effect()[0].get_duration()),
Item::Silence| Item::Silence|
Item::SilencePlus | Item::SilencePlus |

View File

@ -353,8 +353,8 @@ fn post_resolve(_skill: Skill, game: &mut Game, mut resolutions: Resolutions) ->
}; };
if target.is_ko() { if target.is_ko() {
// resolutions.push(Resolution::new(&source, &target).event(Event::Ko()).stages(EventStages::PostOnly));
target.effects.clear(); target.effects.clear();
resolutions.push(Resolution::new(&source, &target).event(Event::Ko()).stages(EventStages::PostOnly));
} }
game.update_construct(&mut source); game.update_construct(&mut source);
@ -433,8 +433,6 @@ pub enum EventStages {
EndOnly, // Skip Anim Skip EndOnly, // Skip Anim Skip
#[serde(rename = "POST_SKILL")] #[serde(rename = "POST_SKILL")]
PostOnly, // Skip Skip Anim PostOnly, // Skip Skip Anim
#[serde(rename = "")]
NoStages, // Skip Skip Skip
} }
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
@ -490,7 +488,6 @@ impl Resolution {
EventStages::EndPost => target_duration + post_skill, // Skip Anim Anim EventStages::EndPost => target_duration + post_skill, // Skip Anim Anim
EventStages::EndOnly => target_duration, // Skip Anim Skip EventStages::EndOnly => target_duration, // Skip Anim Skip
EventStages::PostOnly => post_skill, // Skip Skip Anim EventStages::PostOnly => post_skill, // Skip Skip Anim
EventStages::NoStages => 0, // Skip Skip Skip
} }
} }
} }
@ -764,48 +761,48 @@ impl Skill {
// Attack Base // Attack Base
Skill::Attack => 80, // Base Skill::Attack => 80, // Base
Skill::Blast=> 105, // BB Skill::Blast => 105, // BB
Skill::BlastPlus => 140, // BB Skill::BlastPlus => 140, // BB
Skill::BlastPlusPlus => 200, // BB Skill::BlastPlusPlus => 200, // BB
Skill::Chaos=> 40, // BR Skill::Chaos => 40, // BR
Skill::ChaosPlus => 65, // BR Skill::ChaosPlus => 65, // BR
Skill::ChaosPlusPlus => 90, // BR Skill::ChaosPlusPlus => 90, // BR
Skill::Heal=> 125, //GG Skill::Heal => 125, //GG
Skill::HealPlus => 185, //GG Skill::HealPlus => 185, //GG
Skill::HealPlusPlus => 270, //GG Skill::HealPlusPlus => 270, //GG
Skill::SiphonTick=> 25, // GB Skill::SiphonTick => 25, // GB
Skill::SiphonTickPlus => 30, Skill::SiphonTickPlus => 30,
Skill::SiphonTickPlusPlus => 40, Skill::SiphonTickPlusPlus => 40,
Skill::Slay=> 45, // RG Skill::Slay => 45, // RG
Skill::SlayPlus => 65, Skill::SlayPlus => 65,
Skill::SlayPlusPlus => 100, Skill::SlayPlusPlus => 100,
Skill::Strike=> 90, //RR Skill::Strike => 90, //RR
Skill::StrikePlus => 140, Skill::StrikePlus => 140,
Skill::StrikePlusPlus => 200, Skill::StrikePlusPlus => 200,
// Block Base // Block Base
Skill::ElectrocuteTick=> 80, Skill::ElectrocuteTick => 80,
Skill::ElectrocuteTickPlus => 100, Skill::ElectrocuteTickPlus => 100,
Skill::ElectrocuteTickPlusPlus => 130, Skill::ElectrocuteTickPlusPlus => 130,
Skill::CounterAttack=> 120, Skill::CounterAttack => 120,
Skill::CounterAttackPlus => 160, Skill::CounterAttackPlus => 160,
Skill::CounterAttackPlusPlus => 230, Skill::CounterAttackPlusPlus => 230,
Skill::Purify=> 45, //Green dmg (heal) Skill::Purify => 45, //Green dmg (heal)
Skill::PurifyPlus => 70, Skill::PurifyPlus => 70,
Skill::PurifyPlusPlus => 105, Skill::PurifyPlusPlus => 105,
Skill::Reflect=> 45, //Recharge blue life (heal) Skill::Reflect => 45, //Recharge blue life (heal)
Skill::ReflectPlus => 70, Skill::ReflectPlus => 70,
Skill::ReflectPlusPlus => 100, Skill::ReflectPlusPlus => 100,
Skill::Recharge=> 70, //Recharge red and blue life (heal) Skill::Recharge => 70, //Recharge red and blue life (heal)
Skill::RechargePlus => 110, Skill::RechargePlus => 110,
Skill::RechargePlusPlus => 170, Skill::RechargePlusPlus => 170,
@ -814,29 +811,36 @@ impl Skill {
Skill::SustainPlusPlus => 230, Skill::SustainPlusPlus => 230,
// Stun Base // Stun Base
Skill::Sleep=> 200, //Green dmg (heal) Skill::Sleep => 200, //Green dmg (heal)
Skill::SleepPlus => 290, Skill::SleepPlus => 290,
Skill::SleepPlusPlus => 400, Skill::SleepPlusPlus => 400,
Skill::Banish=> 40, //Green dmg (heal) Skill::Banish => 40, //Green dmg (heal)
Skill::BanishPlus => 75, Skill::BanishPlus => 75,
Skill::BanishPlusPlus => 125, Skill::BanishPlusPlus => 125,
Skill::Bash=> 45, Skill::Bash => 45,
Skill::BashPlus => 65, Skill::BashPlus => 65,
Skill::BashPlusPlus => 100, Skill::BashPlusPlus => 100,
Skill::Link=> 75, Skill::Link => 25,
Skill::LinkPlus => 100, Skill::LinkPlus => 40,
Skill::LinkPlusPlus => 150, Skill::LinkPlusPlus => 70,
Skill::Ruin => 40,
Skill::RuinPlus => 70,
Skill::RuinPlusPlus => 100,
// Debuff Base // Debuff Base
Skill::DecayTick=> 33, Skill::DecayTick => 33,
Skill::DecayTickPlus => 45, Skill::DecayTickPlus => 45,
Skill::DecayTickPlusPlus => 70, Skill::DecayTickPlusPlus => 70,
Skill::Silence=> 55, // Deals more per blue skill on target
Skill::Silence => 55, // Deals more per blue skill on target
Skill::SilencePlus => 80, Skill::SilencePlus => 80,
Skill::SilencePlusPlus => 110, Skill::SilencePlusPlus => 110,
Skill::Restrict=> 40, // Deals more per red skill on target
Skill::Restrict => 40, // Deals more per red skill on target
Skill::RestrictPlus => 65, Skill::RestrictPlus => 65,
Skill::RestrictPlusPlus => 100, Skill::RestrictPlusPlus => 100,
@ -849,11 +853,11 @@ impl Skill {
Skill::AbsorbPlus => 120, Skill::AbsorbPlus => 120,
Skill::AbsorbPlusPlus => 155, Skill::AbsorbPlusPlus => 155,
Skill::Intercept=> 80, Skill::Intercept => 80,
Skill::InterceptPlus => 110, Skill::InterceptPlus => 110,
Skill::InterceptPlusPlus => 150, Skill::InterceptPlusPlus => 150,
Skill::TriageTick=> 75, Skill::TriageTick => 75,
Skill::TriageTickPlus => 110, Skill::TriageTickPlus => 110,
Skill::TriageTickPlusPlus => 140, Skill::TriageTickPlusPlus => 140,
@ -971,19 +975,19 @@ impl Skill {
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: 1, meta: None, tick: None}],
Skill::Purge => vec![ConstructEffect {effect: Effect::Purge, duration: 2, 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: 3, 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: 4, meta: None, tick: None}], Skill::PurgePlusPlus => vec![ConstructEffect {effect: Effect::Purge, duration: 4, meta: None, tick: None}],
Skill::Link => vec![ConstructEffect {effect: Effect::Stun, duration: 3, meta: None, tick: None}], Skill::Link => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
Skill::LinkPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 2, meta: None, tick: None}], Skill::LinkPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, meta: None, tick: None}],
Skill::LinkPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 1, 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: 2, meta: None, tick: None}],
Skill::SilencePlusPlus => vec![ConstructEffect {effect: Effect::Silence, duration: 4, meta: None, tick: None}], Skill::SilencePlusPlus => vec![ConstructEffect {effect: Effect::Silence, duration: 2, meta: None, tick: None}],
Skill::Siphon => vec![ConstructEffect {effect: Effect::Siphon, duration: 2, Skill::Siphon => vec![ConstructEffect {effect: Effect::Siphon, duration: 2,
meta: Some(EffectMeta::Skill(Skill::SiphonTick)), tick: None}], meta: Some(EffectMeta::Skill(Skill::SiphonTick)), tick: None}],
@ -997,8 +1001,8 @@ impl Skill {
Skill::SleepPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 4, meta: None, tick: None}], Skill::SleepPlusPlus => vec![ConstructEffect {effect: Effect::Stun, duration: 4, meta: None, tick: None}],
Skill::Restrict => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}], Skill::Restrict => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}],
Skill::RestrictPlus => vec![ConstructEffect {effect: Effect::Restrict, duration: 3, meta: None, tick: None}], Skill::RestrictPlus => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}],
Skill::RestrictPlusPlus => vec![ConstructEffect {effect: Effect::Restrict, duration: 4, meta: None, tick: None}], Skill::RestrictPlusPlus => vec![ConstructEffect {effect: Effect::Restrict, duration: 2, meta: None, tick: None}],
Skill::Bash => vec![ConstructEffect {effect: Effect::Stun, duration: 2, Skill::Bash => vec![ConstructEffect {effect: Effect::Stun, duration: 2,
meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None}], meta: Some(EffectMeta::Skill(Skill::Bash)), tick: None}],
@ -1027,24 +1031,25 @@ impl Skill {
pub fn base_cd(&self) -> Cooldown { pub fn base_cd(&self) -> Cooldown {
match self { match self {
Skill::Attack => None, Skill::Attack => None,
Skill::Debuff => Some(1), Skill::Block => None, // reduce damage
Skill::Buff => None, Skill::Buff => None,
Skill::Debuff => Some(1),
Skill::Stun => Some(2),
Skill::Strike=> None, Skill::Strike=> None,
Skill::StrikePlus => None, Skill::StrikePlus => None,
Skill::StrikePlusPlus => None, Skill::StrikePlusPlus => None,
Skill::Block => None, // reduce damage
Skill::Counter| Skill::Counter|
Skill::CounterPlus | Skill::CounterPlus |
Skill::CounterPlusPlus => None, // avoid all damage Skill::CounterPlusPlus => None, // avoid all damage
Skill::Restrict=> Some(2), Skill::Restrict |
Skill::RestrictPlus => Some(2), Skill::RestrictPlus |
Skill::RestrictPlusPlus => Some(2), Skill::RestrictPlusPlus => Some(2),
Skill::Stun => Some(2),
Skill::Bash=> Some(2), Skill::Bash |
Skill::BashPlus => Some(2), Skill::BashPlus |
Skill::BashPlusPlus => Some(2), Skill::BashPlusPlus => Some(2),
Skill::Heal=> None, Skill::Heal=> None,
@ -1055,30 +1060,31 @@ impl Skill {
Skill::TriagePlus => None, // hot Skill::TriagePlus => None, // hot
Skill::TriagePlusPlus => None, // hot Skill::TriagePlusPlus => None, // hot
Skill::Break=> Some(1), // no damage stun, adds vulnerable Skill::Break | // no damage stun, adds vulnerable
Skill::BreakPlus => Some(1), Skill::BreakPlus |
Skill::BreakPlusPlus => Some(1), Skill::BreakPlusPlus => Some(1),
Skill::Blast=> None, Skill::Blast |
Skill::BlastPlus => None, Skill::BlastPlus |
Skill::BlastPlusPlus => None, Skill::BlastPlusPlus => None,
Skill::Chaos=> None, Skill::Chaos |
Skill::ChaosPlus => None, Skill::ChaosPlus |
Skill::ChaosPlusPlus => None, Skill::ChaosPlusPlus => None,
Skill::Amplify=> Some(1), Skill::Amplify |
Skill::AmplifyPlus => Some(1), Skill::AmplifyPlus |
Skill::AmplifyPlusPlus => Some(1), Skill::AmplifyPlusPlus => Some(1),
Skill::Hybrid|
Skill::Hybrid |
Skill::HybridPlus | Skill::HybridPlus |
Skill::HybridPlusPlus => Some(1), Skill::HybridPlusPlus => Some(1),
Skill::Invert=> Some(2), Skill::Invert |
Skill::InvertPlus => Some(2), Skill::InvertPlus |
Skill::InvertPlusPlus => Some(2), Skill::InvertPlusPlus => Some(2),
Skill::Decay=> None, // dot Skill::Decay => None, // dot
Skill::DecayPlus => None, Skill::DecayPlus => None,
Skill::DecayPlusPlus => None, Skill::DecayPlusPlus => None,
@ -1086,59 +1092,59 @@ impl Skill {
Skill::SiphonPlus | Skill::SiphonPlus |
Skill::SiphonPlusPlus => None, Skill::SiphonPlusPlus => None,
Skill::Curse=> Some(1), Skill::Curse |
Skill::CursePlus => Some(1), Skill::CursePlus |
Skill::CursePlusPlus => Some(1), Skill::CursePlusPlus => Some(1),
Skill::Link=> Some(2), Skill::Link |
Skill::LinkPlus => Some(2), Skill::LinkPlus |
Skill::LinkPlusPlus => Some(2), Skill::LinkPlusPlus => Some(1),
Skill::Silence=> Some(2), Skill::Silence |
Skill::SilencePlus => Some(2), Skill::SilencePlus |
Skill::SilencePlusPlus => Some(2), Skill::SilencePlusPlus => Some(2),
Skill::Purify | Skill::Purify |
Skill::PurifyPlus | Skill::PurifyPlus |
Skill::PurifyPlusPlus => None, Skill::PurifyPlusPlus => None,
Skill::Purge=> Some(1), Skill::Purge |
Skill::PurgePlus => Some(1), Skill::PurgePlus |
Skill::PurgePlusPlus => Some(1), Skill::PurgePlusPlus => Some(1),
Skill::Banish | Skill::Banish |
Skill::BanishPlus | Skill::BanishPlus |
Skill::BanishPlusPlus => Some(1), Skill::BanishPlusPlus => Some(1),
Skill::Haste=> Some(1), Skill::Haste |
Skill::HastePlus => Some(1), Skill::HastePlus |
Skill::HastePlusPlus => Some(1), Skill::HastePlusPlus => Some(1),
Skill::Reflect | Skill::Reflect |
Skill::ReflectPlus | Skill::ReflectPlus |
Skill::ReflectPlusPlus => None, Skill::ReflectPlusPlus => None,
Skill::Recharge=> None, Skill::Recharge |
Skill::RechargePlus => None, Skill::RechargePlus |
Skill::RechargePlusPlus => None, Skill::RechargePlusPlus => None,
Skill::Ruin=> Some(3), Skill::Ruin |
Skill::RuinPlus => Some(2), Skill::RuinPlus |
Skill::RuinPlusPlus => Some(2), Skill::RuinPlusPlus => Some(2),
Skill::Slay=> None, Skill::Slay=> None,
Skill::SlayPlus => None, Skill::SlayPlus => None,
Skill::SlayPlusPlus => None, Skill::SlayPlusPlus => None,
Skill::Sleep=> Some(2), Skill::Sleep |
Skill::SleepPlus => Some(2), Skill::SleepPlus |
Skill::SleepPlusPlus => Some(2), Skill::SleepPlusPlus => Some(2),
Skill::Sustain | Skill::Sustain |
Skill::SustainPlus | Skill::SustainPlus |
Skill::SustainPlusPlus => Some(1), Skill::SustainPlusPlus => Some(1),
Skill::Intercept=> Some(1), Skill::Intercept => Some(1),
Skill::InterceptPlus => Some(1), Skill::InterceptPlus => Some(1),
Skill::InterceptPlusPlus => Some(1), Skill::InterceptPlusPlus => Some(1),
@ -1146,8 +1152,7 @@ impl Skill {
Skill::ElectrifyPlus | Skill::ElectrifyPlus |
Skill::ElectrifyPlusPlus => None, Skill::ElectrifyPlusPlus => None,
Skill::Absorb |
Skill::Absorb|
Skill::AbsorbPlus | Skill::AbsorbPlus |
Skill::AbsorbPlusPlus => Some(1), Skill::AbsorbPlusPlus => Some(1),
@ -1184,17 +1189,17 @@ impl Skill {
pub fn ko_castable(&self) -> bool { pub fn ko_castable(&self) -> bool {
match self { match self {
Skill::ElectrocuteTick| Skill::ElectrocuteTick |
Skill::ElectrocuteTickPlus | Skill::ElectrocuteTickPlus |
Skill::ElectrocuteTickPlusPlus | Skill::ElectrocuteTickPlusPlus |
Skill::DecayTick| Skill::DecayTick |
Skill::DecayTickPlus | Skill::DecayTickPlus |
Skill::DecayTickPlusPlus | Skill::DecayTickPlusPlus |
Skill::SiphonTick| Skill::SiphonTick |
Skill::SiphonTickPlus | Skill::SiphonTickPlus |
Skill::SiphonTickPlusPlus | Skill::SiphonTickPlusPlus |
Skill::TriageTick| Skill::TriageTick |
Skill::TriageTickPlus | Skill::TriageTickPlus |
Skill::TriageTickPlusPlus => true, Skill::TriageTickPlusPlus => true,
_ => false, _ => false,
@ -1203,17 +1208,16 @@ impl Skill {
pub fn is_tick(&self) -> bool { pub fn is_tick(&self) -> bool {
match self { match self {
Skill::ElectrocuteTick| Skill::ElectrocuteTick |
Skill::ElectrocuteTickPlus | Skill::ElectrocuteTickPlus |
Skill::ElectrocuteTickPlusPlus | Skill::ElectrocuteTickPlusPlus |
Skill::DecayTick| Skill::DecayTick |
Skill::DecayTickPlus | Skill::DecayTickPlus |
Skill::DecayTickPlusPlus | Skill::DecayTickPlusPlus |
Skill::SiphonTick| Skill::SiphonTick |
Skill::SiphonTickPlus | Skill::SiphonTickPlus |
Skill::SiphonTickPlusPlus | Skill::SiphonTickPlusPlus |
Skill::TriageTick |
Skill::TriageTick|
Skill::TriageTickPlus | Skill::TriageTickPlus |
Skill::TriageTickPlusPlus => true, Skill::TriageTickPlusPlus => true,
@ -1223,19 +1227,19 @@ impl Skill {
pub fn speed(&self) -> u64 { pub fn speed(&self) -> u64 {
match self { match self {
Skill::SiphonTick| Skill::SiphonTick |
Skill::SiphonTickPlus | Skill::SiphonTickPlus |
Skill::SiphonTickPlusPlus => Skill::Siphon.speed(), Skill::SiphonTickPlusPlus => Skill::Siphon.speed(),
Skill::DecayTick| Skill::DecayTick |
Skill::DecayTickPlus | Skill::DecayTickPlus |
Skill::DecayTickPlusPlus => Skill::Decay.speed(), Skill::DecayTickPlusPlus => Skill::Decay.speed(),
Skill::TriageTick| Skill::TriageTick |
Skill::TriageTickPlus | Skill::TriageTickPlus |
Skill::TriageTickPlusPlus => Skill::Triage.speed(), Skill::TriageTickPlusPlus => Skill::Triage.speed(),
Skill::ElectrocuteTick| Skill::ElectrocuteTick |
Skill::ElectrocuteTickPlus | Skill::ElectrocuteTickPlus |
Skill::ElectrocuteTickPlusPlus => Skill::Electrify.speed(), Skill::ElectrocuteTickPlusPlus => Skill::Electrify.speed(),
@ -1245,7 +1249,7 @@ impl Skill {
pub fn aoe(&self) -> bool { pub fn aoe(&self) -> bool {
match self { match self {
Skill::Ruin| Skill::Ruin |
Skill::RuinPlus | Skill::RuinPlus |
Skill::RuinPlusPlus => true, Skill::RuinPlusPlus => true,
_ => false, _ => false,
@ -1260,44 +1264,44 @@ impl Skill {
Skill::AmplifyPlus | Skill::AmplifyPlus |
Skill::AmplifyPlusPlus | Skill::AmplifyPlusPlus |
Skill::Block | Skill::Block |
Skill::Sustain| Skill::Sustain |
Skill::SustainPlus | Skill::SustainPlus |
Skill::SustainPlusPlus | Skill::SustainPlusPlus |
Skill::Electrify| Skill::Electrify |
Skill::ElectrifyPlus | Skill::ElectrifyPlus |
Skill::ElectrifyPlusPlus | Skill::ElectrifyPlusPlus |
Skill::Haste| Skill::Haste |
Skill::HastePlus | Skill::HastePlus |
Skill::HastePlusPlus | Skill::HastePlusPlus |
Skill::Heal| Skill::Heal |
Skill::HealPlus | Skill::HealPlus |
Skill::HealPlusPlus | Skill::HealPlusPlus |
Skill::Absorb| Skill::Absorb |
Skill::AbsorbPlus | Skill::AbsorbPlus |
Skill::AbsorbPlusPlus | Skill::AbsorbPlusPlus |
Skill::Invert| Skill::Invert |
Skill::InvertPlus | Skill::InvertPlus |
Skill::InvertPlusPlus | Skill::InvertPlusPlus |
Skill::Intercept| Skill::Intercept |
Skill::InterceptPlus | Skill::InterceptPlus |
Skill::InterceptPlusPlus | Skill::InterceptPlusPlus |
Skill::Counter| Skill::Counter |
Skill::CounterPlus | Skill::CounterPlus |
Skill::CounterPlusPlus | Skill::CounterPlusPlus |
Skill::Purify| Skill::Purify |
Skill::PurifyPlus | Skill::PurifyPlus |
Skill::PurifyPlusPlus | Skill::PurifyPlusPlus |
Skill::Recharge| Skill::Recharge |
Skill::RechargePlus | Skill::RechargePlus |
Skill::RechargePlusPlus | Skill::RechargePlusPlus |
Skill::Reflect| Skill::Reflect |
Skill::ReflectPlus | Skill::ReflectPlus |
Skill::ReflectPlusPlus | Skill::ReflectPlusPlus |
Skill::Triage| Skill::Triage |
Skill::TriagePlus | Skill::TriagePlus |
Skill::TriagePlusPlus => true, Skill::TriagePlusPlus => true,
Skill::Banish| Skill::Banish |
Skill::BanishPlus | Skill::BanishPlus |
Skill::BanishPlusPlus => rng.gen_bool(0.5), Skill::BanishPlusPlus => rng.gen_bool(0.5),
@ -1401,21 +1405,12 @@ 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 {
let red_amount = source.red_power().pct(skill.multiplier());
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])));
let e = target.recharge(skill, red_amount, 0);
let stages = match e {
Event::Recharge { red, blue, skill: _ } => {
if red > 0 || blue > 0 { EventStages::PostOnly }
else { EventStages::NoStages }
}
_ => {
warn!("no recharge event found {:?}", e);
EventStages::NoStages
}
};
results.push(Resolution::new(source, target).event(e).stages(stages)); let red_amount = source.red_power().pct(skill.multiplier());
target.recharge(skill, red_amount, 0)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
return results; return results;
} }
@ -1425,18 +1420,9 @@ fn intercept(source: &mut Construct, target: &mut Construct, mut results: Resolu
results.push(Resolution::new(source, target).event(target.add_effect(skill, intercept))); results.push(Resolution::new(source, target).event(target.add_effect(skill, intercept)));
let red_amount = source.red_power().pct(skill.multiplier()); let red_amount = source.red_power().pct(skill.multiplier());
let e = target.recharge(skill, red_amount, 0); target.recharge(skill, red_amount, 0)
let stages = match e { .into_iter()
Event::Recharge { red, blue, skill: _ } => { .for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
if red > 0 || blue > 0 { EventStages::PostOnly }
else { EventStages::NoStages }
}
_ => {
warn!("no recharge event found {:?}", e);
EventStages::NoStages
}
};
results.push(Resolution::new(source, target).event(e).stages(stages));
return results; return results;
} }
@ -1684,6 +1670,11 @@ fn electrocute_tick(source: &mut Construct, target: &mut Construct, mut results:
} }
fn ruin(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn ruin(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
let amount = source.blue_power().pct(skill.multiplier());
target.deal_blue_damage(skill, amount)
.into_iter()
.for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
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()[0]))
.stages(EventStages::PostOnly)); .stages(EventStages::PostOnly));
@ -1693,18 +1684,9 @@ fn ruin(source: &mut Construct, target: &mut Construct, mut results: Resolutions
fn absorb(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { 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])));
let blue_amount = source.blue_power().pct(skill.multiplier()); let blue_amount = source.blue_power().pct(skill.multiplier());
let e = target.recharge(skill, 0, blue_amount); target.recharge(skill, 0, blue_amount)
let stages = match e { .into_iter()
Event::Recharge { red, blue, skill: _ } => { .for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
if red > 0 || blue > 0 { EventStages::PostOnly }
else { EventStages::NoStages }
}
_ => {
warn!("no recharge event found {:?}", e);
EventStages::NoStages
}
};
results.push(Resolution::new(source, target).event(e).stages(stages));
return results;; return results;;
} }
@ -1743,36 +1725,21 @@ fn reflect(source: &mut Construct, target: &mut Construct, mut results: Resoluti
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])));
let blue_amount = source.blue_power().pct(skill.multiplier()); let blue_amount = source.blue_power().pct(skill.multiplier());
let e = target.recharge(skill, 0, blue_amount); target.recharge(skill, 0, blue_amount)
let stages = match e { .into_iter()
Event::Recharge { red, blue, skill: _ } => { .for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
if red > 0 || blue > 0 { EventStages::PostOnly }
else { EventStages::NoStages }
}
_ => {
warn!("no recharge event found {:?}", e);
EventStages::NoStages
}
};
results.push(Resolution::new(source, target).event(e).stages(stages));
return results;; return results;;
} }
fn recharge(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions { fn recharge(source: &mut Construct, target: &mut Construct, mut results: Resolutions, skill: Skill) -> Resolutions {
results.push(Resolution::new(source, target).event(Event::Skill { skill }).stages(EventStages::StartEnd));
let red_amount = source.red_power().pct(skill.multiplier()); let red_amount = source.red_power().pct(skill.multiplier());
let blue_amount = source.blue_power().pct(skill.multiplier()); let blue_amount = source.blue_power().pct(skill.multiplier());
let e = target.recharge(skill, red_amount, blue_amount); target.recharge(skill, red_amount, blue_amount)
let stages = match e { .into_iter()
Event::Recharge { red, blue, skill: _ } => { .for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
if red > 0 || blue > 0 { EventStages::AllStages }
else { EventStages::StartEnd }
}
_ => {
warn!("no recharge event found {:?}", e);
EventStages::NoStages
}
};
results.push(Resolution::new(source, target).event(e).stages(stages));
return results; return results;
} }
@ -1819,27 +1786,12 @@ 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 swap = match target.green_life().checked_sub(source.green_life()) { results.push(Resolution::new(source, target).event(target.add_effect(skill, skill.effect()[0])));
Some(s) => s.pct(skill.multiplier()),
None => 0
};
let link_events = target.deal_blue_damage(skill, swap); let amount = source.blue_power().pct(skill.multiplier().saturating_mul(target.effects.len() as u64));
for e in link_events { target.deal_blue_damage(skill, amount)
match e { .into_iter()
Event::Damage { amount, mitigation: _, colour: _, skill: _ } => { .for_each(|e| results.push(Resolution::new(source, target).event(e).stages(EventStages::PostOnly)));
results.push(Resolution::new(source, target).event(e));
let heal = source.deal_green_damage(skill, amount);
for h in heal {
results.push(Resolution::new(source, source).event(h).stages(EventStages::PostOnly));
};
},
_ => results.push(Resolution::new(source, target).event(e)),
}
}
results.push(Resolution::new(source, source)
.event(source.add_effect(skill, skill.effect()[0])).stages(EventStages::PostOnly));
return results; return results;
} }
@ -2183,6 +2135,7 @@ mod tests {
let mut results = recharge(&mut x, &mut y, vec![], Skill::Recharge); let mut results = recharge(&mut x, &mut y, vec![], Skill::Recharge);
results.remove(0);
let Resolution { source: _, target: _, event, stages: _ } = results.remove(0); let Resolution { source: _, target: _, event, stages: _ } = results.remove(0);
match event { match event {
Event::Recharge { red, blue, skill: _ } => { Event::Recharge { red, blue, skill: _ } => {