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
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [1.8.1] - 2019-11-07
## [1.8.0] - 2019-10-31
# Added
### Fixed
- 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
- 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
# Changed
### Changed
- Construct life changed
- 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
- Amplify
Now increases green power
- Now increases green power
- Absorb
- 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
- Banish
Reduced cooldown to 1T
- Reduced cooldown to 1T
- Decay
Removed cooldown
- Removed cooldown
- 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
- 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
# Added
### Added
- Step by step tutorial
- 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
@ -59,7 +89,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- 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
# Changed
### Changed
- Vbox phase
- Made general performance improvements
- 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
- 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
- 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
# Added
### Added
- Offering of draws
- Neither player receives a point if they agree to a draw
- Bots automatically agree to draws
## [1.6.5] - 2019-10-25
# Fixed
### Fixed
- Stripe being blocked no longer causes unrecoverable error
- Automatic ready up is now throttled after abandons
- Player width styling
# Changed
### Changed
- Improved wiggle animation
- Intercept is now considered defensive by bots
- Password restrictions relaxed
@ -214,245 +245,4 @@ We've updated the UI during the vbox / buy phase to give a better indication of
* You can no longer select invalid combinations.
* Controls
* 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%
* Abandon button now asks for confirmation.

View File

@ -1 +1 @@
1.8.0
1.8.1

View File

@ -8,15 +8,30 @@
* mobile info page
* Invert recharge
## SOON
* supporter gold name in instance (anyone whos put any money into game)
* change cooldowns to delay & recharge
- delay is cooldown before skill can first be used
- recharge is cooldown after using skill
- 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
- reduce number of items for creating t2/t3 items from 3 -> 2
- add lost complexity by adding skill spec items
@ -27,55 +42,11 @@
- Strike + SpeedRR -> StrikeSpeed (strike has Y% more speed)
- 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
* bot game grind
* (maybe) return of the combat log (last few events with condensed descriptions)
- click in to scroll
$$$
* Items
* Colour scheme
* targeting highlight colour
* number of constructs
* Highlight (dota) colour
* fx colours + styles
* mnml tv
* modules
* troll life -> dmg
@ -84,6 +55,28 @@ $$$
* fuck magic
* 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
* 10d chaos maths, not rock paper scissors
* phys is faster and chaotic

View File

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

View File

@ -1,14 +1,3 @@
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');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,15 @@
@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 {
margin: 0;

View File

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

View File

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

View File

@ -1,15 +1,4 @@
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
require('./src/app');

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ class Refl extends Component {
this.animations = [];
}
render({ team }) {
render({ player }) {
const oneX = anime.random(32, 96);
const twoX = anime.random(192, 224);
@ -28,7 +28,7 @@ class Refl extends Component {
version="1.1"
id="reflect"
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">
<defs>
<filter id="reflectFilterGreen">

View File

@ -20,6 +20,7 @@ function projectile(x, y, radius, colour) {
<circle
cx={x}
cy={y}
stroke="none"
r={radius}
fill={colour}
/>
@ -28,7 +29,7 @@ function projectile(x, y, radius, colour) {
function sword(colour) {
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',
Siphon: () => 'blue-green-border',
// Stun
Link: () => 'blue-green-border',
Bash: () => 'red-border',
Sleep: () => 'green-border',
Ruin: () => 'blue-border',
Break: () => 'red-green-border',
Sleep: () => 'green-border',
Banish: () => 'red-blue-border',
Break: () => 'red-green-border',
Link: () => 'blue-green-border',
// Block
Counter: () => 'red-border',
Purify: () => 'green-border',
Electrify: () => 'blue-border',
Purify: () => 'green-border',
Recharge: () => 'red-blue-border',
Sustain: () => 'red-green-border',
Reflect: () => 'blue-green-border',
Recharge: () => 'blue-red-border',
// Buff
Intercept: () => 'red-border',
Triage: () => 'green-border',
Haste: () => 'red-green-border',
Absorb: () => 'blue-border',
Hybrid: () => 'blue-green-border',
Triage: () => 'green-border',
Amplify: () => 'red-blue-border',
Haste: () => 'red-green-border',
Hybrid: () => 'blue-green-border',
// Debuff
Restrict: () => 'red-border',
Purge: () => 'green-border',
Silence: () => 'blue-border',
Purge: () => 'green-border',
Curse: () => 'red-blue-border',
Invert: () => 'red-green-border',
Decay: () => 'blue-green-border',
Curse: () => 'red-blue-border',
// // Lifes Upgrades
// LifeGG: () => 'green-border',

View File

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

View File

@ -117,7 +117,8 @@ class GameConstruct extends Component {
setGameEffectInfo,
} = 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 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 regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat)/;
const infoDescription = reactStringReplace(fullInfo.description, regEx, match => shapes[match]());
const speed = <div> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </div>;
const speed = <span> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </span>;
return (
<div class="skill-description">
<h2> {gameSkillInfo.skill} </h2>
<div> {infoDescription} </div>
<h2><span> {gameSkillInfo.skill} </span></h2>
<span>{infoDescription} </span>
{speed}
</div>);
}
const effects = construct.effects.length
? construct.effects.map(c =>
<div
key={c.effect}
onMouseOver={e => hoverInfo(e, c)}
onMouseOut={e => hoverInfo(e, null)}
> {c.effect} - {c.duration}T</div>)
<div key={c.effect}>
<span key={c.effect} onMouseOver={e => hoverInfo(e, c)}
onMouseOut={e => hoverInfo(e, null)}> {c.effect} - {c.duration}T
</span>
</div>)
: null;
return (<div class="effects"> {effects} </div>);
};

View File

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

View File

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

View File

@ -52,6 +52,7 @@ class TargetSvg extends Component {
itemInfo,
} = props;
const { width, height } = state;
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)
@ -59,7 +60,7 @@ class TargetSvg extends Component {
if (tutorialGame) {
return (
<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>
);
}
@ -73,7 +74,7 @@ class TargetSvg extends Component {
return (
<div class="resolving-skill">
<h1>{gameEffectInfo.effect}</h1>
<div> {infoDescription} </div>
<div>{infoDescription}</div>
</div>
);
}
@ -91,7 +92,7 @@ class TargetSvg extends Component {
return (
<div class="resolving-skill">
<h1>{animSkill}</h1>
<div> {itemSourceDescription} </div>
<div>{itemSourceDescription}</div>
</div>
);
}

View File

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

View File

@ -240,6 +240,8 @@ function convertItem(v) {
}
function effectInfo(i) {
const hybridBlast = 25;
const hasteStrike = 30;
function multiplier(s) { // Update later to use server info in future
if (s === 'CounterAttack') return 120;
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 'Sustain': return 'Construct cannot be KO while active. Additionally provides immunity to disables';
case 'Curse': return `Construct will take ${i.meta[1] - 100}% increased red and blue damage`;
case 'Haste': return `Construct has ${i.meta[1] - 100}% increased SpeedStat. Red attack skills will trigger a HasteStrike dealing 30% SpeedStat as red damage.`;
case 'Hybrid': return `Construct has ${i.meta[1] - 100}% increased GreenPower. Blue attack skills will trigger a HybridBlast dealing 25% GreenPower as red damage.`;
case 'Invert': return 'Reverses damage and healing. Healing will damage this construct and damage will heal.';
case '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 ${hybridBlast}% GreenPower as red damage.`;
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 'Purge': return 'Disable construct from casting any green skills';
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",
"version": "1.8.0",
"version": "1.8.1",
"description": "",
"main": "index.js",
"scripts": {

View File

@ -1,6 +1,6 @@
[package]
name = "mnml"
version = "1.8.0"
version = "1.8.1"
authors = ["ntr <ntr@smokestack.io>"]
[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???
if let Some(immunity) = self.immune(skill) {
return Event::Immunity {
skill,
immunity,
};
if !self.is_ko() {
events.push(Event::Immunity { skill, immunity });
}
return events;
}
// Do we need inversion?
let current_red_life = self.red_life();
self.red_life.increase(red_amount);
let new_red_life = self.red_life.value;
let red = new_red_life - current_red_life;
match self.affected(Effect::Invert) {
false => {
// Do we need inversion?
let current_red_life = self.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();
self.blue_life.increase(blue_amount);
let new_blue_life = self.blue_life.value;
let blue = new_blue_life - current_blue_life;
let current_blue_life = self.blue_life();
self.blue_life.increase(blue_amount);
let new_blue_life = self.blue_life.value;
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> {
let mut events = vec![];
if let Some(immunity) = self.immune(skill) {
events.push(Event::Immunity {
immunity,
skill,
});
if !self.is_ko() {
events.push(Event::Immunity { skill, immunity });
}
return events;
}
@ -629,10 +697,9 @@ impl Construct {
let mut events = vec![];
if let Some(immunity) = self.immune(skill) {
events.push(Event::Immunity {
skill,
immunity,
});
if !self.is_ko() {
events.push(Event::Immunity { skill, immunity });
}
return events;
}
@ -702,10 +769,9 @@ impl Construct {
let mut events = vec![];
if let Some(immunity) = self.immune(skill) {
events.push(Event::Immunity {
skill,
immunity,
});
if !self.is_ko() {
events.push(Event::Immunity { skill, immunity });
}
return events;
}

View File

@ -469,6 +469,14 @@ impl Game {
let mut resolutions = resolution_steps(&cast, &mut self);
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);
// while let Some(resolution) = resolutions.pop() {
@ -477,10 +485,6 @@ impl Game {
// 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
self.stack_sort_speed();
};
@ -506,14 +510,11 @@ impl Game {
}
// 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 skill.used_cooldown() {
if let Some(skill) = resolved.iter()
.filter(|s| s.source_construct_id == construct.id)
.find(|s| s.used_cooldown()) {
construct.skill_set_cd(skill.skill);
} else {
construct.reduce_cooldowns();
}
} else {
construct.reduce_cooldowns();
}
@ -984,7 +985,9 @@ mod tests {
.learn(Skill::Siphon)
.learn(Skill::Amplify)
.learn(Skill::Stun)
.learn(Skill::Block);
.learn(Skill::Block)
.learn(Skill::Sleep)
.learn(Skill::Decay);
let mut y = Construct::new()
.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());
}
#[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]
fn counter_test() {
let mut game = create_test_game();
@ -1390,6 +1442,7 @@ mod tests {
true
},
Event::AoeSkill { skill: _ } => false,
Event::Damage { amount: _, mitigation: _, colour: _, skill: _ } => false,
_ => panic!("ruin result not effect {:?}", event),
}
false => false,
@ -1428,7 +1481,7 @@ mod tests {
game = game.resolve_phase_start();
assert!(game.resolved.len() == 5);
assert!(game.resolved.len() == 4);
while let Some(r) = game.resolved.pop() {
let Resolution { source , target, event: _, stages: _ } = r;
if [i_construct.id, j_construct.id].contains(&source.id) {

View File

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

View File

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