Merge branch 'release/1.11.0'

This commit is contained in:
ntr
2020-01-03 18:38:03 +10:00
129 changed files with 13894 additions and 8807 deletions
+12
View File
@@ -1,3 +1,15 @@
## [1.10.1] - 2019-12-04
### Changed
- Reduced the number of items to create + and ++ versions from 3 to 2 (need 4 items total to make a ++)
- Reduced the power of ++ skills to be closer to the power of previous + versions
- Tweaked all values of power specs to reduce their power bonuses
## [1.10.0] - 2019-11-29
### Changed
- Reworked the vbox layout
- Floating combat text and faster animations
- Mobile UI fixes
## [1.9.1] - 2019-11-21 ## [1.9.1] - 2019-11-21
### Fixed ### Fixed
- Fixed Item+ Purchasing Bug - Fixed Item+ Purchasing Bug
+162 -104
View File
@@ -1,142 +1,200 @@
# item_info -> # Spec / Skill hybrid specs #
combos [strike, [R R Attack]] Create skills specs by combining an upgraded skills with corresponding colour specs:
specs [spec [bonus amount, [r g b]] Strike (RR + A) can be combined with (PowerRR, SpeedRR, LifeRR)
- Strike + PowerRR -> StrikePower
- Strike + SpeedRR -> StrikeSpeed
- Strike + LifeRR -> StrikeLife
# Playthrough Could also create SkillSpec+ by combining two together.
constructs join game ## Why Skill Specs
stats randomised
initial stash drops - Give tools to players to make cool unique builds
6 skills - Passive utility that you work towards
6 colours - Specialise in a type of skill
6 specs - Repurpose skills that you aren't using much for active use
- More layers of complexity
play first round ## Skill specs philosphy
basically duke it out
# Colours # - Should be more interesting than just another stat "multiplier" we have that already (could be placeholder though)
- Passives should help build a theme / identity to the skills
- Specs should be numerically scalable so they can be `balanced` (also for upgraded versions think continuous vs discrete)
- While specialised should be useful for multiple skills
e.g. StrikeSpeed -> Causes your red attack skills to cast on another target on the same team for (X% damage)
A StrikeSpeed should do something for `some` other active skill that isn't strike
- They don't have to be `competely game changing` to be viable / fun / interesting
### Red ### ## Brainstorming on what kind of things skill specs could do
Real world concepts
Aggressive
Apply Buffs
Fast & Chaotic
### Green ### - Passive
Healing Specialisation - (Abosrb Spec) when you take damage you permanently gain X Power/Speed/Life
Defensive - Grant you Amplify with (X% multiplier) when you KO a target
Purge buffs & debuffs - Convert (X% of your red / blue power -> green power)
- Reduce / Increase damage taken of a certain type (Block type upgrades maybe?)
### Blue ### - Active
Fantasy concepts (magical) - Make a skill cast another skill (strike another guy)
Aggressive & Defensive - Apply an effect when you cast a skill
Apply Debuffs - Change damage / healing / speed of a skill
Slow & Reliable
# Classes # ## The BIG LIST
Class names to be changed ### Attack Base
==================== Strike
Pure Red `Nature` Power
Pure Green `Non-Violence` Life
Pure Blue `Destruction` Speed (Repeat skill at X% multiplier)
Hybrid Red / Blue `Chaos`
Hybrid Red / Green `Purity`
Hybrid Blue / Green `Technology`
Chaos
Power
Life
Speed
Skills Heal
========== Power (Convert X% Red/Blue Power to GreenPower)
Life
Speed
Basic Type Blast
------------------------------------------------------------------------- Power
Attack `Basic offensive skill - deal damage` Life
Buff `Base ally targetted skill - increase ally speed` Speed
Stun `Base enemy disable - disable enemy for 2 rounds`
Block `Base self targetted defensive - reduced damage taken for 2 rounds`
Debuff `Base enemy debuff - reduce enemy speed`
# Attack Base # Slay
Power
Life
Speed
RR - Strike Siphon
GG - Heal Power
BB - Blast Life
RG - Purify Speed
GB - Decay
RB - Blast
# Stun Base # ### Stun Base
Bash
Power
Life
Speed
RR - Strangle Sleep
GG - Break Power
BB - Ruin Life
RG - Banish Speed
GB - Silence
RB - Hex
# Buff Base # Ruin
Power
Life
Speed
RR - Empower Link
GR - Triage Power
BB - Absorb Life
RG - Sustain Speed
GB - Amplify
RB - Haste
# Debuff Base # Banish
Power
Life
Speed
RR - Restrict Break
GG - Purge Power
BB - Curse Life
RG - Slow Speed
GB - Siphon
RB - Invert
# Block Base # ### Block Base
RR - Counter Counter
GG - Reflect Power
BB - Electrify Life
RG - Intercept Speed
GB - Life `rename?`
RB - Recharge
Reflect
Power
Life
Speed
## Advanced combos ## Purify
Power
Life
Speed
Two ways of upgrading Sustain
#1 -> combine more of the same for a stronger version of the same skill / spec (T2 / T3 Combos) Power
#2 -> combine skill with two matching colour specs to change the way the skill works (Spec / Skill hybrid) Life
Speed
### T2 / T3 Combos ### Electrify
Power
Life
Speed
All current specs / items can be further combo'd into T2 and T3 versions Recharge
Power
Life
Speed
# 3 of same base => 1 upgraded tier # ### Buff Base
`3 x T1 Red Damage Spec => T2 Red Damage Spec`
`3 x T2 Red Damage Spec => T3 Red Damage Spec`
`3 x T1 Strike => T2 Strike`
`3 x T2 Strike => T3 Strike`
Upgraded skills will have a combination of higher damage / longer duration / reduced cooldown Intercept
Upgraded skills use the same speed formula as previously Power
Life
Speed
### Spec / Skill hybrid specs ### Triage
Power
Life
Speed
# Strike # Absorb
2 x Red Damage + Strike => Strike damage bonus (crit?) Power (Gain X Power when you take damage)
2 x Red Speed + Strike => Strike reduces enemy speed Life (Gain X Life when you take damage)
2 x Red Life + Strike => Strike reduces enemy healing (% reduction) Speed (Gain X Speed when you take damage)
# Heal # Amplify
2 x Green Damage + Heal => Heal target for additional 20% of caster's maximum life Power
2 x Green Speed + Heal => Heal target gets bonus speed Life
2 x Green Life + Heal => Heal increases target's max hp for 2 turns Speed
etc etc Haste
Power
Life
Speed
30 skills * 3 specs => 90 spec / skill hybrid specs -> might be overcomplicated Hybrid
Power
Life
Speed
### Debuff Base
Purge
Power
Life
Speed
Invert
Power
Life
Speed
Restrict
Power
Life
Speed
Silence
Power
Life
Speed
Curse
Power
Life
Speed
Decay
Power
Life
Speed
-75
View File
@@ -1,75 +0,0 @@
# Everything costs money (gold?)
Items - Base colours / skills / specs and associated upgrades
### Sources of money
- Start with money and gain income after each battle
- Higher income from winning
- Selling items in inventory or equipped on character refunds
- Selling from inventory full refund
- Selling from charcter 50% refund
### Uses for money
- Buying items
- Rerolling vbox
### Base Costs
Base colours have a base 1 cost
Base skills have a base 2 cost
Base specs have a base 3 cost
### Actual Costs
- Costs increase as more of an item is used on constructs in the game
- The cost increases by the base cost for every 6 allocations of base item
- Allocation is based on all constructs in the game
### Example ###
Round #1
All costs are base costs
# Player #1 and Player #2 (They both bought the same things)
Construct #1 Strike (Attack + RR), (2 + 1 + 1) = (4) cost
Construct #1 Empower (Buff + RR), (2 + 1 + 1) = (4) cost
Construct #3 Attack, 2 cost
Total cost - 10
Round #2
Items used on constructs include:
Red x 8
Attack x 4
Buff x 2
The costs of red for round #2 are now (1 + 1) = 2
If they were to buy the same skill setup it would be as follows:
# Player #1 and Player #2 (They both bought the same things)
Construct #1 Strike (Attack + RR), (2 + 2 + 2) = (6) cost
Construct #1 Empower (Buff + RR), (2 + 2 + 2) = (6) cost
Construct #3 Attack, 2 cost
Total cost - 14
### Philosophy of increasing item costs
- Two games will never feel exactly the same
- Costs change over rounds to diversify skill choice and gameplay
- As optimal builds emerge the paths to reach them will change every game
- Rewarded for going (hipster) builds nobody else is trying
- Some reward for hoarding items in your inventory while they cheaper (hodl red)
### Income values
Could try with 9 base income
Income increases by 3 each round and winning bonus of 6
-49
View File
@@ -1,49 +0,0 @@
# Stat Multipliers #
### Defenses ###
Rare `Increased GreenLife`
Common `Increased Evasion rating`
Common `Increased Blue Life rating`
Common `Increased RedLife rating`
Common `Increased Healing done`
Common `Increased Healing received`
Common `Increased Blue Damage`
Common `Increased Red Damage`
Uncommon `Reduced hp loss penalty to evade chance`
Uncommon `Increased base evasion chance per X evasion rating`
Uncommon `Increased % mitigation from red_life`
Uncommon `Increased % mitigation from spell shield`
Uncommon `Increased damage over time`
Rare `gain empower on KO`
Rare `cannot be restrictd`
Rare `cannot be silenced`
Rare `cannot be intercepted`
Rare `25% stun for attack`
Rare `25% hex for blast`
Rare `cooldown reduction`
Rare `effect duration`
Rare `increased phys damage, 0 spell damage`
Rare `increased spell damage, 0 phys damage`
Rare `increased phys damage, silenced`
Rare `increased spell damage, restrictd`
Rare `increased speed, increased durations`
Rare `increased speed, increased cooldowns`
# Nature - Technology - Nonviolence - Destruction - Purity - Chaos #
- Increased power
- Increased speed
- Increased stat
- ??? Related Notables
# ??? Constructs need to have a minimum of X of the construct stat to learn a skill #
+4
View File
@@ -34,8 +34,12 @@ Player Events e.g. chatwheel
Matchmaking + ELO / Leaderboard Matchmaking + ELO / Leaderboard
Game skill private fields Game skill private fields
# Phase 4 (Release -> Full Shill mode)
Refine artwork, icons, scaling etc Refine artwork, icons, scaling etc
Music Music
Skill Specs
Some sort viewable combat log
Marketing materials Marketing materials
Videos Videos
-224
View File
@@ -1,224 +0,0 @@
### Specs ###
Numbers are placeholder
`Specs get a bonus dependent on the total of Red / Green / Blue in player skills & specs`
# Example to meet 5 red gem bonus from skills only
In your player Construct #1 has `Strike`, Construct #2 has `Slay` and `Heal`, Construct #3 has `Restrict`
- RR skill `Strike` contributes 2 red gems to the total red gems (2 total)
- RG skill `Slay` contributes 1 red gem to the total red gems (3 total)
- GG skill `Heal` contirubtes 0 red gems to the total red gems (3 total)
- RR skill `Restrict` contirubtes 2 red gems to the total red gems (5 total)
# Advanced specs also require a minimum number of Red / Green / Blue gems on the construct to take effect
- Tier 1 Basic specs (Damage / Health / Defense) will have no requirements
- Advanced specs will require a certain threshold of red / green / blue gems to be enabled
- Provided spec requirements are met, all specs will add gems to the construct
# Starting from scratch with a vbox
### Round 1
- Buy 4 reds (items)
- Buy two 'Attack' Skills & 1 Stun skill (items)
- Buy 1 Basic Damage Spec (item)
Combine 2 Red + 'Attack' -> Strike
Combine 2 Red + 'Basic Damage Spec' -> Red Damage
Construct #1 -> Give Strike & Red Damage Spec -> Strike + 1 x Red Damage Spec
Construct #2 -> Give Attack -> Attack
Construct #3 -> Give Stun -> Stun
Player Total (4 Red + 2 Basic gems)
### Round 2
- Buy 2 reds & 2 green & 2 blue (all available colour items)
- Buy 2 Basic Damage Spec (item)
- Construct #2 Unequip Attack
- Combine 2 Green + 'Attack' -> Heal
- Construct #3 Unequip Stun
- Combine 2 Blue + 'Stun' -> Ruin
- Combine 2 Red + 'Basic Damage Spec' -> Red Damage
Construct #1 -> Give Red Damage items -> Strike + 2 x Red Damage Spec (6R)
Construct #2 -> Give Heal item -> Heal (2G)
Construct #3 -> Give Ruin item -> Ruin (2B)
## Round 3
- Buy 4 reds
- Buy 1 Attack, 1 Stun, 1 Block (item)
- Buy 2 Basic Damage Spec (item)
- Combine 2 Red + 'Stun' -> Strangle
- Combine 2 Red + 'Block' -> Counter
Construct #1 -> Give 'Stun' & 'Strangle' -> Strike, Stun, Strangle + 2 x Red Damage Spec (10R)
Construct #2 -> 'No change' -> Heal (2G)
Construct #3 -> Give Attack item & 2 Basic Damage Spec -> Attack + Ruin + 2 x Basic Damage Spec (2B)
## Round 4
- Buy 4 reds (getting lucky with reds!)
- Buy 1 Attack, 1 Buff
- Combine 2 Red + 'Attack' -> Strike
- Combine 2 Red + 'Buff' -> Empower
- Construct #1 Unequip 2 x Red Damage spec, Equip Empower -> Strike, Stun, Strangle, Empower (8R)
- Combine 'Strike' + 2 x Red Damage spec -> 'Increased Strike Damage spec'
### Note 'Increased Strike Damage spec' requires 8R on the construct
Construct #1 Equip Increased Strike Damage spec -> Strike, Stun, Strangle, Empower + Increased Strike Damage Spec (14R)
Construct #2 -> 'No change' -> Heal
Construct #3 -> 'No change' -> Attack + Ruin + 2 x Basic Damage Spec
## Round 5
We already lost cause we went all in on 1 red construct like a noob
### Generic Specs
# Basic % GreenLife
`Base` -> 5% inc hp
`Player Bonus` -> 3 basic gems -> +5% // 6 basic gems -> +10% // 12 basic gems -> +15%
Maximum 35% inc hp
# Basic Speed
`Base` -> 5% inc speed
`Player Bonus` -> 3 basic gems -> +10% // 6 basic gems -> +15% // 12 basic gems -> +20%
Maximum 50% inc speed
# Basic Class Spec
`Base` -> +2 red, +2 green +2 blue gems on construct
# Basic Duration
### Increased Damage Combos ###
Generate by combining `Generic Spec (Basic Damage)` with respective RGB
# Red Damage (Dmg + RR)
Add 2 `red gems`
`Base` -> 10% inc red dmg
`Player Bonus` 5 red gems -> +10% // 10 red gems -> +15% // 20 red gems -> +25%
Maximum +60% red damage
# Blue Damage (Dmg + BB) #
Add 2 `blue gems`
`Base` -> 10% inc blue dmg
`Player Bonus` 5 blue gems -> +10% // 10 blue gems -> +15% // 20 blue gems -> +25%
Maximum +60% blue damage
# Healing (Dmg + GG) #
Add 2 `green gems`
`Base` -> 10% inc healing
`Player Bonus` 5 green gems -> +10% // 10 green gems -> +15% // 20 green gems -> +25%
Maximum +60% inc healing
# Red damage and healing (Dmg + RG)
Add 1 red 1 green gem
`Base` -> 5% inc red damage and 5% inc healing
`Player Bonus` (2R + 2G gems) -> +5% + 5% // (5R + 5G gems) -> +10% + 10% % // (10R + 10G) gems -> +15% + 15%
Maximum +35% inc red damage and 35% inc healing
# Red and blue damage (Dmg + RB)
Add 1 red and 1 blue gem
`Base` -> 5% inc red damage and 5% inc healing
`Player Bonus` (2 red + 2 green gems) -> +5% + 5% // (5 red + 5 green gems) -> +10% + 10% % // 20 green gems -> +15% + 15%
Maximum +35% inc damage and 35% inc healing
# Blue damage and healing (Dmg + BG)
Add 1 blue and 1 green gem
`Base` -> 5% inc blue damage and 5% inc healing
`Player Bonus` (2B + 2G gems) -> +5% + 5% // (5B + 5G gems) -> +10% + 10% % // (10B + 10G) gems -> +15% + 15%
Maximum +35% inc blue damage and 35% inc healing
### Increased GreenLife Combos ###
Generate by combining `Generic Spec (Basic GreenLife)` with respective RGB
# Increased % Red Life (Basic %HP + 2R)
Add 2 `red gems`
`Base` -> 10% inc red shield
`Player Bonus` 5 red gems -> +10% // 10 red gems -> +15% // 20 red gems -> +20%
Maximum +55% inc red shield
# Increased % Red Life and GreenLife (Basic %HP + 1R1G)
Add 1 red 1 green gem
`Base` -> 5% inc red shield and 5% inc hp
`Player Bonus` (2R + 2G gems) -> +5% + 5% // (5R + 5G gems) -> +10% + 10% % // (10R + 10G) gems -> +15% + 15%
Maximum +35% inc red shield and 35% inc hp
# Increased % Blue Life (Basic %HP + 2B)
Add 2 `blue gems`
`Base` -> 10% inc red shield
`Player Bonus` 5 blue gems -> +10% // 10 blue gems -> +15% // 20 blue gems -> +20%
Maximum +55% inc blue shield
# Increased % Blue Life and GreenLife (Basic %HP + 1B1G)
Add `1 blue and 1 green gems`
`Base` -> 5% inc red shield and 5% inc hp
`Player Bonus` (2B + 2G gems) -> +5% + 5% // (5B + 5G gems) -> +10% + 10% % // (10B + 10G) gems -> +15% + 15%
Maximum +35% inc blue shield and 35% inc hp
# Increased % GreenLife (Basic %HP + 2G)
Add `2 green gems`
`Base` -> 10% inc hp
`Player Bonus` 5 green gems -> +10% // 10 green gems -> +15% // 20 green gems -> +20%
Maximum +55% inc hp
# Increased % Blue and Red Life (Basic %HP + 1B1R)
Add `1 blue and 1 red gem`
`Base` -> 5% inc red shield and 5% inc hp
`Player Bonus` (2B + 2R gems) -> +5% + 5% // (5B + 5R gems) -> +10% + 10% % // (10B + 10R) gems -> +15% + 15%
Maximum +35% inc blue shield and 35% inc red shield
## Upgraded Attack Spec Combos
# Increased Strike Damage (Combine Strike + Red Damage Spec x 2)
Construct Requires `8 red gems`
Adds `6 red gems`
`Base` -> 15% increased strike damage
`Player Bonus` 15 red gems -> +15% // 20 red gems -> +20% // 30 red gems -> +30%
Maximum 80% increased strike damage
# Improved Heal (Combine Heal + Healing Spec x 2)
Construct Requires `8 green gems`
`Base` -> 15% increased heal healing
`Player Bonus` 15 green gems -> +15% // 20 green gems -> +20% // 30 green gems -> +30%
Maximum 80% increased heal healing
# Increased Blast Damage (Combine Blast + Blue Spec x 2)
Construct Requires `8 blue gems`
`Base` -> 15% increased blast damage
`Player Bonus` 15 blue gems -> +15% // 20 blue gems -> +20% // 30 blue gems -> +30%
Maximum 80% increased blast damage
# Increased Slay Damage (Combine Slay + Red Damage Spec + Healing Spec)
Construct Requires `4 red 4 green gems`
`Base` -> 15% increased slay damage
`Player Bonus` (8R + 8G) gems -> +15% // (10R + 10G) gems -> +20% // (15R + 15G) gems -> +30%
Maximum 80% increased slay damage
# Increased Banish Damage (Combine Slay + Red Damage Spec + Blue Damage Spec)
Construct Requires `4 red 4 blue gems`
`Base` -> 15% increased slay damage
`Player Bonus` (8R + 8B) gems -> +15% // (10R + 10B) gems -> +20% // (15R + 15B) gems -> +30%
Maximum 80% increased banish damage
## Other Combos
# Increased % Red Speed (Basic Speed + 2R)
Add 2 red gems
`Base` -> 15% inc red speed
`Player Bonus` 5 red gems -> +15% // 10 red gems -> +20% // 20 red gems -> +25%
Maximum 80% inc red speed
# Nature Affinity (Basic Class spec + 2R)
`Base` -> Add 10 red gems
+1 -1
View File
@@ -1 +1 @@
1.10.0 1.11.0
+11 -25
View File
@@ -3,7 +3,6 @@
_ntr_ _ntr_
* can't reset password without knowing password =\ * can't reset password without knowing password =\
* skip faceoff on server side
* 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
@@ -30,14 +29,11 @@ Hexagon Set
- Increase intensity for each visit - Increase intensity for each visit
_mashy_ _mashy_
* floating combat text combat (start opposite hp and float towards it) to speed up animations * rebalance
* speed specs
* life specs
* represent construct colours during game phase (try %bar or dots) * represent construct colours during game phase (try %bar or dots)
* buy from preview if you have the required bases in vbox / inventory
- a "buy" becomes available under the current info / preview section
- clicking the buy automatically purchases / combine items
- could also be used to upgrade already equipped skills / specs
- e.g. an equipped white power spec could be upgraded by clicking under preview
- if this was added we could reduce inventory size to 3 and rearrange vbox (see mockup img)
_external_ _external_
* Graphics * Graphics
@@ -57,29 +53,19 @@ _tba_
## SOON ## SOON
* combo rework * Skill / Spec hybrids - SEE COMBOS.md
- reduce number of items for creating t2/t3 items from 3 -> 2
- add lost complexity by adding skill spec items
- Created by combining a skill with corresponding spec
e.g.
- Strike + PowerRR -> StrikePower (Will be the power symbol with strike text under)
- Construct does Y% more damage with Strike
- Strike + SpeedRR -> StrikeSpeed (strike has Y% more speed)
- Strike + LifeRR -> StrikeLife (Strike recharges X% of damage as red life)
- Can also work as module style passive keystones
* troll life -> dmg -> Invert life spec?
* prince of peace
* bonus healing / no damage -> Heal power spec?
* fuck magic -> Some sort of reflect spec?
* empower on ko -> Amplify + Power spec
* elo + leaderboards * elo + leaderboards
## LATER ## LATER
* Graphical status effects instead of text * Graphical status effects instead of text
* buy from preview if you have the required bases in vbox / inventory
- a "buy" becomes available under the current info / preview section
- clicking the buy automatically purchases / combine items
- could also be used to upgrade already equipped skills / specs
- e.g. an equipped white power spec could be upgraded by clicking under preview
* theme toasts * theme toasts
* rework vecs into sets * rework vecs into sets
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "mnml-client", "name": "mnml-client",
"version": "1.10.0", "version": "1.11.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
+1
View File
@@ -10,6 +10,7 @@ echo "Setting version to $VERSION"
echo $VERSION | tr -d '\n' > VERSION echo $VERSION | tr -d '\n' > VERSION
cd $MNML_PATH/server && sed -i "/^version/c\version = \"$VERSION\"" Cargo.toml cd $MNML_PATH/server && sed -i "/^version/c\version = \"$VERSION\"" Cargo.toml
cd $MNML_PATH/core && sed -i "/^version/c\version = \"$VERSION\"" Cargo.toml
cd $MNML_PATH/ops && npm --allow-same-version --no-git-tag-version version "$VERSION" cd $MNML_PATH/ops && npm --allow-same-version --no-git-tag-version version "$VERSION"
cd $MNML_PATH/client && npm --allow-same-version --no-git-tag-version version "$VERSION" cd $MNML_PATH/client && npm --allow-same-version --no-git-tag-version version "$VERSION"
cd $MNML_PATH/acp && npm --allow-same-version --no-git-tag-version version "$VERSION" cd $MNML_PATH/acp && npm --allow-same-version --no-git-tag-version version "$VERSION"
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 106 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 95 KiB

+10 -1
View File
@@ -3,16 +3,25 @@
div { div {
padding-right: 1em; padding-right: 1em;
// display: flex;
// flex-flow: column;
line-height: 2em;
}
h3 {
// text-transform: uppercase;
margin-bottom: 0.5em;
} }
button { button {
width: 100%; width: 100%;
height: 2.5em;
display: block; display: block;
} }
input { input {
width: 100%; width: 100%;
height: 3em; height: 2.5em;
display: block; display: block;
} }
+1 -1
View File
@@ -5,7 +5,7 @@
@white: #f5f5f5; // whitesmoke @white: #f5f5f5; // whitesmoke
@purple: #9355b5; // 6lack - that far cover @purple: #9355b5; // 6lack - that far cover
@yellow: #ffa100; @yellow: #ffa100;
@silver: #c0c0c0; @silver: #2c2c2c;
@black: black; @black: black;
@gray: #222; @gray: #222;
-4
View File
@@ -54,11 +54,7 @@
button { button {
&.highlight { &.highlight {
color: black;
background: @silver; background: @silver;
// border: 1px solid @white; (this bangs around the vbox)
// overwrite the classes on white svg elements
svg { svg {
stroke-width: 0.75em; stroke-width: 0.75em;
} }
+38 -69
View File
@@ -75,25 +75,11 @@
flex: 1; flex: 1;
border-top: 0; border-top: 0;
border: 0.1em solid #222; border: 0.1em solid #222;
&:not(:last-child) {
border-right: 0;
}
&:last-child { &:last-child {
float: right; float: right;
} }
} }
} }
.login {
display: flex;
flex-flow: column;
.terms {
display: inline;
margin: 0 1em;
}
}
} }
section { section {
@@ -108,7 +94,23 @@ section {
padding-right: 1em; padding-right: 1em;
} }
.panes {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
}
.list { .list {
margin-bottom: 2em;
figure {
letter-spacing: 0.25em;
text-transform: uppercase;
font-size: 125%;
display: flex;
flex-flow: column;
}
letter-spacing: 0.25em; letter-spacing: 0.25em;
text-transform: uppercase; text-transform: uppercase;
display: grid; display: grid;
@@ -117,10 +119,6 @@ section {
grid-gap: 1em; grid-gap: 1em;
flex-flow: row wrap; flex-flow: row wrap;
align-items: flex-end; align-items: flex-end;
button {
border-radius: 0.25em;
// height: 3em;
}
&.sub { &.sub {
grid-template-columns: 1fr; grid-template-columns: 1fr;
@@ -160,73 +158,44 @@ section {
} }
} }
.panes {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
figure { .login {
letter-spacing: 0.25em;
text-transform: uppercase;
font-size: 125%;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
}
}
.demo { .terms {
margin-top: 1em; display: inline;
margin: 0 1em;
display: block; }
button { button {
pointer-events: none; padding: 0 0.5em;
} margin-top: 1em;
section {
margin-bottom: 0.5em;
div:first-child {
padding-right: 1em;
} }
} }
.construct-section { .options {
.construct-list { grid-area: hdr;
height: 25em;
grid-area: unset;
.instance-construct {
// border: 0;
}
}
}
.colour-info {
grid-area: vinfo;
display: flex; display: flex;
align-items: center;
div { .logo {
display: flex; flex: 0 1 10%;
margin-right: 1em;
border: none;
} }
svg { button {
flex: 1; flex: 1;
height: 1em; border-top: 0;
} border: 0.1em solid #222;
} &:last-child {
float: right;
.game-demo {
.game {
height: 25em;
display: flex;
flex-flow: column;
.game-construct {
flex: 1;
}
} }
} }
} }
.intro {
text-align: center;
font-size: 0.8em;
}
+56 -38
View File
@@ -27,23 +27,6 @@ html body {
overflow-y: hidden; overflow-y: hidden;
} }
#mnml {
/* this is the sweet nectar to keep it full page*/
height: 100vh;
max-height: 100vh;
min-height: 100vh;
/* stops inspector going skitz*/
overflow-x: hidden;
// overflow-y: hidden;
}
// @media (min-width: 1921px) {
// html, body, #mnml {
// font-size: 16pt;
// }
// }
html { html {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
@@ -108,11 +91,37 @@ dl {
padding: 0.5em 1em; padding: 0.5em 1em;
/* this is the sweet nectar to keep it full page*/
height: 100vh;
max-height: 100vh;
min-height: 100vh;
/* stops inspector going skitz*/
overflow-x: hidden;
// overflow-y: hidden;
&.animations-test { &.animations-test {
aside button { aside button {
font-size: 50%; font-size: 50%;
} }
} }
&.front-page {
display: block;
main {
padding: 0 25%;
}
.logo {
margin: 2em 0;
}
.list {
margin-bottom: 0;
}
}
} }
main { main {
@@ -129,7 +138,7 @@ button, input {
box-sizing: border-box; box-sizing: border-box;
font-size: 1em; font-size: 1em;
flex: 1; flex: 1;
border-radius: 0.5em; border-radius: 0;
line-height: 2em; line-height: 2em;
padding-right: 0.1em; padding-right: 0.1em;
padding-left: 0.1em; padding-left: 0.1em;
@@ -150,9 +159,12 @@ button, input {
&:focus { &:focus {
/*colour necesary to bash skellington*/ /*colour necesary to bash skellington*/
outline: 0; outline: 0;
} }
// &:active {
// filter: url("#noiseFilter");
// }
} }
a { a {
@@ -261,28 +273,12 @@ figure.gray {
display: none; display: none;
} }
header {
.options {
font-size: 200%;
}
button {
height: 2em;
// border-radius: 0.1em;
border: none;
border-radius: 0;
}
}
.options { .options {
button { button {
&.highlight { &.highlight {
color: @white; color: @white;
box-shadow: inset 0px 5px 0px 0px @white; box-shadow: inset 0px 5px 0px 0px @white;
border: 0;
} }
border: none;
} }
} }
@@ -300,11 +296,20 @@ li {
} }
.logo { .logo {
height: 2em; height: 4em;
background-image: url("../../assets/mnml.logo.trim.svg"); filter: url("#noiseFilter");
background-image: url("../../assets/mnml.logo.text.svg");
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: left; background-position: center;
}
.awards {
height: 100%;
background-image: url("../../assets/mnml.awards.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
} }
.discord-btn { .discord-btn {
@@ -316,8 +321,13 @@ li {
.mnni { .mnni {
background-image: url("./../mnni.svg"); background-image: url("./../mnni.svg");
filter: url("#noiseFilter");
} }
// .highlight {
// filter: url("#noiseFilter");
// }
.avatar { .avatar {
grid-area: avatar; grid-area: avatar;
object-fit: contain; object-fit: contain;
@@ -328,6 +338,10 @@ li {
// pointer-events: none; // pointer-events: none;
} }
header {
// font-size: 1.2em;
}
#clipboard { #clipboard {
width: 1px; width: 1px;
height: 1px; height: 1px;
@@ -359,4 +373,8 @@ li {
} }
} }
#noise {
height: 0;
}
@import 'styles.mobile.less'; @import 'styles.mobile.less';
+20 -2
View File
@@ -7,6 +7,12 @@
font-size: 8pt; font-size: 8pt;
padding: 0; padding: 0;
&.front-page {
main {
padding: 0 0.5em;
}
}
.instance { .instance {
grid-template-areas: grid-template-areas:
"vbox vbox" "vbox vbox"
@@ -164,12 +170,21 @@
// portrait menu or small size vertical in landscape // portrait menu or small size vertical in landscape
@media (max-width: 550px) and (max-height: 800px) { @media (max-width: 550px) and (max-height: 800px) and (orientation: portrait) {
#mnml { #mnml {
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: 1fr; grid-template-rows: 1fr;
grid-template-areas: grid-template-areas:
"main" "main";
&.front-page {
display: block;
main {
padding: 0 0.5em;
}
}
} }
section { section {
@@ -264,6 +279,9 @@
} }
.info-combiner { .info-combiner {
max-height: 7em;
overflow-y: scroll;
.info { .info {
display: none; display: none;
} }
-5
View File
@@ -147,16 +147,11 @@
} }
&.highlight { &.highlight {
color: black;
background: @silver; background: @silver;
// overwrite the classes on white svg elements // overwrite the classes on white svg elements
svg { svg {
stroke-width: 0.75em; stroke-width: 0.75em;
} }
.white {
stroke: black;
}
} }
} }
+29 -13
View File
@@ -1,6 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><style>@font-face {
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><style>@font-face {
font-family: octicons-anchor; font-family: octicons-anchor;
src: url(https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octicons.woff) format('woff'); src: url(https://cdnjs.cloudflare.com/ajax/libs/octicons/4.4.0/font/octicons.woff) format('woff');
} }
@@ -733,6 +731,24 @@ pre {
} }
} }
</style><title>CHANGELOG</title></head><body><article class="markdown-body"><h2> </style><title>CHANGELOG</title></head><body><article class="markdown-body"><h2>
<a id="user-content-1101---2019-12-04" class="anchor" href="#1101---2019-12-04" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.10.1] - 2019-12-04</h2>
<h3>
<a id="user-content-changed" class="anchor" href="#changed" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul>
<li>Reduced the number of items to create + and ++ versions from 3 to 2 (need 4 items total to make a ++)</li>
<li>Reduced the power of ++ skills to be closer to the power of previous + versions</li>
<li>Tweaked all values of power specs to reduce their power bonuses</li>
</ul>
<h2>
<a id="user-content-1100---2019-11-29" class="anchor" href="#1100---2019-11-29" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.10.0] - 2019-11-29</h2>
<h3>
<a id="user-content-changed-1" class="anchor" href="#changed-1" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul>
<li>Reworked the vbox layout</li>
<li>Floating combat text and faster animations</li>
<li>Mobile UI fixes</li>
</ul>
<h2>
<a id="user-content-191---2019-11-21" class="anchor" href="#191---2019-11-21" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.9.1] - 2019-11-21</h2> <a id="user-content-191---2019-11-21" class="anchor" href="#191---2019-11-21" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.9.1] - 2019-11-21</h2>
<h3> <h3>
<a id="user-content-fixed" class="anchor" href="#fixed" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Fixed</h3> <a id="user-content-fixed" class="anchor" href="#fixed" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Fixed</h3>
@@ -742,7 +758,7 @@ pre {
<h2> <h2>
<a id="user-content-190---2019-11-21" class="anchor" href="#190---2019-11-21" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.9.0] - 2019-11-21</h2> <a id="user-content-190---2019-11-21" class="anchor" href="#190---2019-11-21" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.9.0] - 2019-11-21</h2>
<h3> <h3>
<a id="user-content-changed" class="anchor" href="#changed" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-2" class="anchor" href="#changed-2" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li>VBOX <li>VBOX
<ul> <ul>
@@ -794,7 +810,7 @@ pre {
<li>Resizing of vbox when you buy / create certain items</li> <li>Resizing of vbox when you buy / create certain items</li>
</ul> </ul>
<h3> <h3>
<a id="user-content-changed-1" class="anchor" href="#changed-1" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-3" class="anchor" href="#changed-3" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li>Automatically shows a preview of combo item when you have 3 items selected for combining</li> <li>Automatically shows a preview of combo item when you have 3 items selected for combining</li>
<li>Only highlight the first available item slot when equipping</li> <li>Only highlight the first available item slot when equipping</li>
@@ -826,7 +842,7 @@ pre {
<li>An issue where skills would not be put on cooldown after being used.</li> <li>An issue where skills would not be put on cooldown after being used.</li>
</ul> </ul>
<h3> <h3>
<a id="user-content-changed-2" class="anchor" href="#changed-2" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-4" class="anchor" href="#changed-4" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li> <li>
<p>Game phase</p> <p>Game phase</p>
@@ -893,7 +909,7 @@ pre {
</li> </li>
</ul> </ul>
<h3> <h3>
<a id="user-content-changed-3" class="anchor" href="#changed-3" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-5" class="anchor" href="#changed-5" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li> <li>
<p>Construct life changed</p> <p>Construct life changed</p>
@@ -979,7 +995,7 @@ pre {
</li> </li>
</ul> </ul>
<h3> <h3>
<a id="user-content-changed-4" class="anchor" href="#changed-4" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-6" class="anchor" href="#changed-6" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li> <li>
<p>Vbox phase</p> <p>Vbox phase</p>
@@ -1125,7 +1141,7 @@ pre {
<li>Player width styling</li> <li>Player width styling</li>
</ul> </ul>
<h3> <h3>
<a id="user-content-changed-5" class="anchor" href="#changed-5" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-7" class="anchor" href="#changed-7" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li>Improved wiggle animation</li> <li>Improved wiggle animation</li>
<li>Intercept is now considered defensive by bots</li> <li>Intercept is now considered defensive by bots</li>
@@ -1134,7 +1150,7 @@ pre {
<h2> <h2>
<a id="user-content-164---2019-10-24" class="anchor" href="#164---2019-10-24" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.6.4] - 2019-10-24</h2> <a id="user-content-164---2019-10-24" class="anchor" href="#164---2019-10-24" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.6.4] - 2019-10-24</h2>
<h3> <h3>
<a id="user-content-changed-6" class="anchor" href="#changed-6" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-8" class="anchor" href="#changed-8" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li>Animations processing on client side reduced.</li> <li>Animations processing on client side reduced.</li>
</ul> </ul>
@@ -1176,7 +1192,7 @@ pre {
<li>Subscriber chat!</li> <li>Subscriber chat!</li>
</ul> </ul>
<h3> <h3>
<a id="user-content-changed-7" class="anchor" href="#changed-7" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-9" class="anchor" href="#changed-9" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li> <li>
<p>Made available skill / effect information during the combat phase.</p> <p>Made available skill / effect information during the combat phase.</p>
@@ -1208,7 +1224,7 @@ pre {
<a id="user-content-156---2019-10-17" class="anchor" href="#156---2019-10-17" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.5.6] - 2019-10-17</h2> <a id="user-content-156---2019-10-17" class="anchor" href="#156---2019-10-17" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.5.6] - 2019-10-17</h2>
<p>We've updated the UI during the vbox / buy phase to give a better indication of valid actions.</p> <p>We've updated the UI during the vbox / buy phase to give a better indication of valid actions.</p>
<h3> <h3>
<a id="user-content-changed-8" class="anchor" href="#changed-8" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-10" class="anchor" href="#changed-10" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li> <li>
<p>Borders for skill combo's represent the base colours.</p> <p>Borders for skill combo's represent the base colours.</p>
@@ -1234,7 +1250,7 @@ pre {
<h2> <h2>
<a id="user-content-155---2019-10-15" class="anchor" href="#155---2019-10-15" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.5.5] - 2019-10-15</h2> <a id="user-content-155---2019-10-15" class="anchor" href="#155---2019-10-15" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>[1.5.5] - 2019-10-15</h2>
<h3> <h3>
<a id="user-content-changed-9" class="anchor" href="#changed-9" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3> <a id="user-content-changed-11" class="anchor" href="#changed-11" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Changed</h3>
<ul> <ul>
<li> <li>
<p>Purge</p> <p>Purge</p>
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "mnml-client", "name": "mnml-client",
"version": "1.10.0", "version": "1.11.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
+2 -4
View File
@@ -1,14 +1,12 @@
export const setAccount = value => ({ type: 'SET_ACCOUNT', value }); export const setAccount = value => ({ type: 'SET_ACCOUNT', value });
export const setAuthenticated = value => ({ type: 'SET_AUTHENTICATED', value });
export const setAnimating = value => ({ type: 'SET_ANIMATING', value }); export const setAnimating = value => ({ type: 'SET_ANIMATING', value });
export const setAnimCb = value => ({ type: 'SET_ANIM_CB', value });
export const setAnimFocus = value => ({ type: 'SET_ANIM_FOCUS', value }); export const setAnimFocus = value => ({ type: 'SET_ANIM_FOCUS', value });
export const setAnimSkill = value => ({ type: 'SET_ANIM_SKILL', value }); export const setAnimSkill = value => ({ type: 'SET_ANIM_SKILL', value });
export const setAnimSource = value => ({ type: 'SET_ANIM_SOURCE', value }); export const setAnimSource = value => ({ type: 'SET_ANIM_SOURCE', value });
export const setAnimTarget = value => ({ type: 'SET_ANIM_TARGET', value }); export const setAnimTarget = value => ({ type: 'SET_ANIM_TARGET', value });
export const setAnimText = value => ({ type: 'SET_ANIM_TEXT', value }); export const setResolution = value => ({ type: 'SET_RESOLUTION', value });
export const setDemo = value => ({ type: 'SET_DEMO', value });
export const setChatShow = value => ({ type: 'SET_CHAT_SHOW', value }); export const setChatShow = value => ({ type: 'SET_CHAT_SHOW', value });
export const setChatWheel = value => ({ type: 'SET_CHAT_WHEEL', value }); export const setChatWheel = value => ({ type: 'SET_CHAT_WHEEL', value });
+13 -56
View File
@@ -4,9 +4,7 @@ const toast = require('izitoast');
const eachSeries = require('async/eachSeries'); const eachSeries = require('async/eachSeries');
const actions = require('./actions'); const actions = require('./actions');
const { TIMES } = require('./constants'); const { setAnimations, clearAnimations } = require('./animations.utils');
const animations = require('./animations.utils');
const { removeTier } = require('./utils');
const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://mnml.gg/api/ws' : 'ws://localhost/api/ws'; const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://mnml.gg/api/ws' : 'ws://localhost/api/ws';
@@ -20,73 +18,32 @@ function createSocket(store) {
ws.send(cbor.encode(msg)); ws.send(cbor.encode(msg));
} }
function sendDevResolve(a, b, skill) { function sendDevResolve(skill) {
send(['DevResolve', { a, b, skill }]); send(['DevResolve', { skill }]);
} }
function onDevResolutions(newRes) { function setGame(game) {
const { game, account, animating } = store.getState(); store.dispatch(actions.setGame(game));
if (animating) return false;
store.dispatch(actions.setAnimating(true)); store.dispatch(actions.setAnimating(true));
store.dispatch(actions.setGameSkillInfo(null));
// stop fetching the game state til animations are done // stop fetching the game state til animations are done
const newRes = game.resolutions[game.resolutions.length - 1];
return eachSeries(newRes, (r, cb) => { return eachSeries(newRes, (r, cb) => {
if (!r.event) return cb(); // if (r.delay === 0) return cb(); // TargetKo etc
const timeout = animations.getTime(r.stages); setAnimations(r, store);
const anims = animations.getObjects(r, game, account); return setTimeout(cb, r.delay);
const text = animations.getText(r);
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
if (anims.animSkill) store.dispatch(actions.setAnimSkill(anims.animSkill));
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 (animations.isCbAnim(anims.animSkill)) store.dispatch(actions.setAnimCb(cb));
}
if (r.stages.includes('POST_SKILL') && text) {
// timeout to prevent text classes from being added too soon
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 (r.stages.includes('END_SKILL') && animations.isCbAnim(anims.animSkill)) return true;
return cb();
}, timeout);
}, err => { }, err => {
if (err) return console.error(err); if (err) return console.error(err);
// clear animation state clearAnimations(store);
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));
// set the game state so resolutions don't fire twice // set the game state so resolutions don't fire twice
store.dispatch(actions.setGame(game)); store.dispatch(actions.setGame(game));
// ws.sendGameState(game.id);
return true; return true;
}); });
} }
const handlers = { const handlers = {
DevResolutions: onDevResolutions, GameState: setGame,
}; };
// decodes the cbor and // decodes the cbor and
+5 -21
View File
@@ -5,18 +5,7 @@ const { createStore, combineReducers } = require('redux');
const reducers = require('./reducers'); const reducers = require('./reducers');
const actions = require('./actions'); const actions = require('./actions');
const createSocket = require('./animations.socket'); const createSocket = require('./animations.socket');
// const TrippyTriangle = require('./components/svgs/trippy.triangle');
// const Amplify = require('./components/svgs/amplify');
// const Hex = require('./components/svgs/hex');
const Game = require('./components/game'); const Game = require('./components/game');
const testGameBuilder = require('./test.game');
const testGame = testGameBuilder('8552e0bf-340d-4fc8-b6fc-cccccccccccc');
const testAccount = {
id: '8552e0bf-340d-4fc8-b6fc-cccccccccccc',
name: 'ntr',
};
// Redux Store // Redux Store
const store = createStore( const store = createStore(
@@ -24,20 +13,15 @@ const store = createStore(
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
); );
const testAccount = {
id: '8552e0bf-340d-4fc8-b6fc-cccccccccccc',
name: 'ntr',
};
store.dispatch(actions.setAccount(testAccount)); store.dispatch(actions.setAccount(testAccount));
store.dispatch(actions.setGame(testGame));
function animationsNav(ws) { function animationsNav(ws) {
function useSkill(skill) { function useSkill(skill) {
const ateam = Math.round(Math.random()); return ws.sendDevResolve(skill);
const bteam = Math.round(Math.random());
const acon = Math.floor(Math.random() * 3);
const bcon = Math.floor(Math.random() * 3);
const a = testGame.players[ateam].constructs[acon].id;
const b = testGame.players[bteam].constructs[bcon].id;
return ws.sendDevResolve(a, b, skill);
} }
return SKILLS.map((s, i) => ( return SKILLS.map((s, i) => (
+33 -191
View File
@@ -1,207 +1,49 @@
const actions = require('./actions');
const { TIMES } = require('./constants'); const { TIMES } = require('./constants');
const { removeTier } = require('./utils');
function none() { function setAnimations(r, store) {
return { const { focus, event: [type, variant] } = r;
animSource: null,
animTarget: null, if (type === 'HitAoe') {
}; const { construct } = variant;
const aoeFocus = focus.concat(construct);
store.dispatch(actions.setResolution(null));
store.dispatch(actions.setAnimFocus(aoeFocus));
store.dispatch(actions.setAnimTarget(r));
return setTimeout(() => store.dispatch(actions.setAnimSource(null)), TIMES.SOURCE_DURATION_MS);
} }
function getObjects(resolution, game, account) { store.dispatch(actions.setAnimFocus(focus));
if (!resolution) return none();
if (!resolution.target) return none();
const [type, event] = resolution.event; if (type === 'Cast') {
if (!event || !event.skill) return none(); store.dispatch(actions.setResolution(null));
store.dispatch(actions.setAnimSource(r));
const playerTeam = game.players.find(t => t.id === account.id); return setTimeout(() => store.dispatch(actions.setAnimSource(null)), TIMES.SOURCE_DURATION_MS);
const playerTeamIds = playerTeam.constructs.map(c => c.id);
const otherTeam = game.players.find(t => t.id !== account.id);
const otherTeamIds = otherTeam.constructs.map(c => c.id);
const sourceIsPlayer = playerTeamIds.includes(resolution.source.id);
const targetIsPlayer = playerTeamIds.includes(resolution.target.id);
const targetting = () => {
if (type === 'AoeSkill') {
if (targetIsPlayer) return playerTeamIds;
return otherTeamIds;
}
return [resolution.target.id];
};
const sameTeam = (sourceIsPlayer && targetIsPlayer) || (!sourceIsPlayer && !targetIsPlayer);
let y = 0;
if (!sameTeam) y = targetIsPlayer ? 1 : -1;
const i = sourceIsPlayer
? playerTeamIds.findIndex(c => c === resolution.source.id)
: otherTeamIds.findIndex(c => c === resolution.source.id);
const j = targetIsPlayer
? playerTeamIds.findIndex(c => c === resolution.target.id)
: otherTeamIds.findIndex(c => c === resolution.target.id);
const x = j - i;
const direction = { x, y };
// const targetTeam = targetIsPlayer ? playerTeamIds : otherTeamIds;
const createSourceAnim = () => {
return {
animation: 'sourceCast',
constructId: resolution.source.id,
direction,
};
};
const skipSource = !resolution.stages.includes('START_SKILL')
|| resolution.source.id === resolution.target.id;
const animSource = skipSource
? null
: createSourceAnim();
const animTarget = {
skill: event.skill,
constructId: targetting(),
player: playerTeamIds.includes(resolution.target.id),
direction,
};
return {
animSource,
animTarget,
animSkill: event.skill,
};
} }
function getTime(stages) { if (type === 'Hit') {
let time = 0; store.dispatch(actions.setResolution(null));
if (stages.includes('START_SKILL') && stages.includes('END_SKILL')) { store.dispatch(actions.setAnimTarget(r));
time += TIMES.SOURCE_AND_TARGET_TOTAL_DURATION; return setTimeout(() => store.dispatch(actions.setAnimTarget(null)), TIMES.TARGET_DURATION_MS);
} else {
if (stages.includes('START_SKILL')) time += TIMES.SOURCE_DURATION_MS;
if (stages.includes('END_SKILL')) time += TIMES.TARGET_DURATION_MS;
}
if (stages.includes('POST_SKILL')) time += TIMES.POST_SKILL_DURATION_MS;
return time;
} }
function getFocusTargets(resolution, game) { return store.dispatch(actions.setResolution(r));
if (!resolution) return [];
if (!resolution.event) return [];
const [type] = resolution.event;
const source = resolution.source.id;
const target = resolution.target.id;
if (type === 'AoeSkill') {
const targetTeam = game.players.find(t => t.constructs.find(c => c.id === target));
const targetTeamIds = targetTeam.constructs.map(c => c.id);
if (source !== target) targetTeamIds.push(source);
return targetTeamIds;
}
if (source !== target) return [source, target];
return [target];
} }
function getText(resolution) { function clearAnimations(store) {
const nullText = { text: null, constructId: null, life: null }; store.dispatch(actions.setAnimSkill(null));
if (!resolution) return nullText; store.dispatch(actions.setAnimSource(null));
if (!resolution.stages.includes('POST_SKILL')) return nullText; store.dispatch(actions.setAnimTarget(null));
store.dispatch(actions.setResolution(null));
function generatePostSkill() { store.dispatch(actions.setAnimating(false));
const [type, event] = resolution.event; store.dispatch(actions.setGameEffectInfo(null));
if (type === 'Ko') { store.dispatch(actions.setAnimFocus(null));
return { text: 'KO!', css: 'ko-transition' };
} }
if (type === 'Disable') {
const { disable } = event;
return { text: `${disable}`, css: '' };
}
if (type === 'Immunity') {
return { text: 'IMMUNE', css: '' };
}
if (type === 'Damage') {
const { mitigation, colour } = event;
let { amount } = event;
let css = '';
if (colour === 'Green') {
css = 'green';
amount *= -1;
}
if (colour === 'Red') css = 'red';
if (colour === 'Blue') css = 'blue';
const mitigationText = mitigation
? `(${mitigation})`
: '';
return { text: `${amount} ${mitigationText}`, css };
}
if (type === 'Healing') {
const { amount, overhealing } = event;
return { text: `${amount} (${overhealing} OH)`, css: 'green' };
}
if (type === 'Inversion') {
return { text: 'INVERT', css: '' };
}
if (type === 'Reflection') {
return { text: 'REFLECT', css: '' };
}
if (type === 'Effect') {
const { effect, duration, construct_effects: effects } = event;
return { text: `+ ${effect} ${duration}T`, css: '', effects };
}
if (type === 'Recharge') {
const { red, blue } = event;
if (red > 0 && blue > 0) return { text: `+${red}R +${blue}B`, css: 'rb' };
if (red > 0) return { text: `+${red}R`, css: 'red' };
if (blue > 0) return { text: `+${blue}B`, css: 'blue' };
return nullText;
}
if (type === 'Removal') {
const { effect, construct_effects: effects } = event;
if (!effect) {
return { text: 'Effect Removal', css: '', effects };
}
return { text: `-${effect}`, css: '', effects };
}
return false;
}
const { green, red, blue } = resolution.target;
const { text, css, effects } = generatePostSkill();
const skill = resolution.event[1] ? resolution.event[1].skill : null;
return {
css,
text,
effects,
life: { green, red, blue },
constructId: resolution.target.id,
skill,
};
}
function isCbAnim(skill) {
return ['Attack', 'Blast', 'Siphon', 'SiphonTick', 'Strike', 'Chaos', 'Slay', 'Heal',
'Buff', 'Amplify', 'Haste', 'Triage', 'TriageTick', 'Link', 'Hybrid', 'Intercept',
'Debuff', 'Curse', 'Decay', 'DecayTick', 'Purge', 'Silence', 'Restrict',
'Stun', 'Bash', 'Absorb', 'Sleep', 'Break', 'Ruin',
'Block', 'Sustain', 'Electrify', 'Electrocute', 'ElectrocuteTick',
'Counter', 'CounterAttack', 'Purify', 'Recharge', 'Reflect'].includes(removeTier(skill));
}
module.exports = { module.exports = {
getFocusTargets, setAnimations,
getObjects, clearAnimations,
getTime,
getText,
isCbAnim,
}; };
+4
View File
@@ -33,6 +33,10 @@ const ws = createSocket(events);
ws.connect(); ws.connect();
events.setWs(ws); events.setWs(ws);
if (process.env.NODE_ENV !== 'development') {
LogRocket.init('yh0dy3/mnml');
}
const App = () => ( const App = () => (
<Provider store={store}> <Provider store={store}>
{window.Stripe {window.Stripe
+3 -3
View File
@@ -152,11 +152,9 @@ class AccountStatus extends Component {
return ( return (
<section class='account top' onClick={tlClick}> <section class='account top' onClick={tlClick}>
<div>
{subInfo()} {subInfo()}
</div>
<div> <div>
<label for="email">Email Settings:</label> <h3>Email</h3>
<dl> <dl>
<dt>Recovery Email</dt> <dt>Recovery Email</dt>
<dd>{email ? email.email : 'No email set'}</dd> <dd>{email ? email.email : 'No email set'}</dd>
@@ -174,6 +172,7 @@ class AccountStatus extends Component {
<button onClick={() => sendSetEmail(emailState)}>Update</button> <button onClick={() => sendSetEmail(emailState)}>Update</button>
</div> </div>
<div> <div>
<h3>Password</h3>
<label for="current">Password:</label> <label for="current">Password:</label>
<input <input
class="login-input" class="login-input"
@@ -208,6 +207,7 @@ class AccountStatus extends Component {
</button> </button>
</div> </div>
<div> <div>
<h3>Other</h3>
<figure> <figure>
<figcaption>spawn new construct</figcaption> <figcaption>spawn new construct</figcaption>
<button onClick={() => sendConstructSpawn()} type="submit"> <button onClick={() => sendConstructSpawn()} type="submit">
+19 -16
View File
@@ -5,6 +5,7 @@ const { connect } = require('preact-redux');
const Amplify = require('./anims/amplify'); const Amplify = require('./anims/amplify');
const Attack = require('./anims/attack'); const Attack = require('./anims/attack');
const Absorb = require('./anims/absorb'); const Absorb = require('./anims/absorb');
const Absorption = require('./anims/absorption');
const Bash = require('./anims/bash'); const Bash = require('./anims/bash');
const Blast = require('./anims/blast'); const Blast = require('./anims/blast');
const Block = require('./anims/block'); const Block = require('./anims/block');
@@ -44,8 +45,8 @@ const { removeTier } = require('../utils');
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
const { animTarget } = state; const { animTarget, account } = state;
return { animTarget }; return { animTarget, account };
} }
); );
@@ -60,20 +61,21 @@ class ConstructAnimation extends Component {
const { const {
animTarget, animTarget,
construct, construct,
account,
} = props; } = props;
if (!animTarget) return false; if (!animTarget) return false;
const { const { skill, event: [, variant] } = animTarget;
skill, const { construct: constructId, player, direction: [x, y] } = variant;
player,
direction, const animY = y && player === account.id ? -1 : y;
constructId, const isPlayer = player === account.id;
} = animTarget; const direction = { x, y: animY };
const animSkill = removeTier(skill); const animSkill = removeTier(skill);
if (!constructId.includes(construct.id)) return false; if (!constructId.includes(construct.id)) return false;
// find target animation // find target animation
const chooseAnim = () => { const chooseAnim = () => {
switch (animSkill) { switch (animSkill) {
@@ -93,9 +95,9 @@ class ConstructAnimation extends Component {
case 'Haste': return <Haste />; case 'Haste': return <Haste />;
case 'Triage': return <Triage />; case 'Triage': return <Triage />;
case 'TriageTick': return <TriageTick />; case 'TriageTick': return <TriageTick />;
case 'Link': return <Link player={player} />; case 'Link': return <Link player={isPlayer} />;
case 'Hybrid': return <Hybrid />; case 'Hybrid': return <Hybrid />;
case 'Intercept': return <Intercept player={player} />; case 'Intercept': return <Intercept player={isPlayer} />;
// Debuff base // Debuff base
case 'Debuff': return <Debuff />; case 'Debuff': return <Debuff />;
@@ -112,21 +114,22 @@ class ConstructAnimation extends Component {
// case 'Banish': return setAvatarAnimation(true, true, resolution.id, construct.id, 'banish', null); // case 'Banish': return setAvatarAnimation(true, true, resolution.id, construct.id, 'banish', null);
case 'Bash': return <Bash />; case 'Bash': return <Bash />;
case 'Absorb': return <Absorb />; case 'Absorb': return <Absorb />;
case 'Absorption': return <Absorption />;
case 'Sleep': return <Sleep />; case 'Sleep': return <Sleep />;
case 'Break': return <Break />; case 'Break': return <Break />;
case 'Ruin': return <Ruin />; case 'Ruin': return <Ruin />;
// Block Base // Block Base
case 'Block': return <Block />; case 'Block': return <Block />;
case 'Sustain': return <Sustain player={player} />; case 'Sustain': return <Sustain player={isPlayer} />;
case 'Electrify': return <Electrify />; case 'Electrify': return <Electrify />;
case 'Electrocute': return <Electrocute />; case 'Electrocute': return <Electrocute />;
case 'ElectrocuteTick': return <Electrocute />; case 'ElectrocuteTick': return <Electrocute />;
case 'Counter': return <Counter player={player} />; case 'Counter': return <Counter player={isPlayer} />;
case 'CounterAttack': return <Attack direction={direction} />; case 'CounterAttack': return <Attack direction={direction} />;
case 'Purify': return <Purify player={player} />; case 'Purify': return <Purify player={isPlayer} />;
case 'Recharge': return <Recharge player={player} />; case 'Recharge': return <Recharge player={isPlayer} />;
case 'Reflect': return <Refl player={player} />; case 'Reflect': return <Refl player={isPlayer} />;
default: return false; default: return false;
} }
+2 -19
View File
@@ -1,21 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
// shamelessly lifted from teh anime docs
// https://animejs.com/documentation/#svgAttr
class Absorb extends Component { class Absorb extends Component {
constructor() { constructor() {
super(); super();
@@ -56,7 +44,7 @@ class Absorb extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#absorb'], targets: ['#absorb'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -66,8 +54,6 @@ class Absorb extends Component {
targets: ['#absorb polygon'], targets: ['#absorb polygon'],
points: '64 124.94732621931382 8.574 96.00354944613788 8.62269130211947 32.03166105954991 64 4 119.37730869788052 32.03166105954991 119.426 96.00354944613788', points: '64 124.94732621931382 8.574 96.00354944613788 8.62269130211947 32.03166105954991 64 4 119.37730869788052 32.03166105954991 119.426 96.00354944613788',
easing: 'easeOutExpo', easing: 'easeOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -76,8 +62,6 @@ class Absorb extends Component {
strokeWidth: [2, 1], strokeWidth: [2, 1],
easing: 'easeInOutSine', easing: 'easeInOutSine',
direction: 'alternate', direction: 'alternate',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -90,8 +74,7 @@ class Absorb extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Absorb); module.exports = Absorb;
@@ -0,0 +1,80 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
class Absorb extends Component {
constructor() {
super();
this.animations = [];
}
render() {
return (
<svg
class='skill-animation blue'
version="1.1"
id="absorb"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 128 128">
<defs>
<filter id="absorbFilter">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<polygon
class='blue'
points="64 124.94732621931382 8.574 96.00354944613788 8.62269130211947 32.03166105954991 64 4 119.37730869788052 32.03166105954991 119.426 96.00354944613788"
filter="url(#absorbFilter)">
</polygon>
<polygon
class='white'
points="64 124.94732621931382 8.574 96.00354944613788 8.62269130211947 32.03166105954991 64 4 119.37730869788052 32.03166105954991 119.426 96.00354944613788">
</polygon>
</svg>
);
}
componentDidMount() {
this.animations.push(anime({
targets: ['#absorb'],
opacity: [
{ value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
],
easing: 'easeInOutSine',
}));
this.animations.push(anime({
targets: ['#absorb polygon'],
points: '64 68.64 8.574 100 63.446 67.68 64 4 64.554 67.68 119.426 100',
easing: 'easeOutExpo',
duration: TIMES.TARGET_DURATION_MS,
}));
this.animations.push(anime({
targets: ['#absorb polygon.white'],
strokeWidth: [2, 1],
easing: 'easeInOutSine',
direction: 'alternate',
duration: TIMES.TARGET_DURATION_MS,
}));
}
// this is necessary because
// skipping / timing / unmounting race conditions
// can cause the animations to cut short, this will ensure the values are reset
// because preact will recycle all these components
componentWillUnmount() {
for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset();
}
}
}
module.exports = Absorb;
+2 -14
View File
@@ -5,13 +5,6 @@ const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Amplify extends Component { class Amplify extends Component {
constructor() { constructor() {
super(); super();
@@ -43,7 +36,7 @@ class Amplify extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#amplify'], targets: ['#amplify'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -54,8 +47,6 @@ class Amplify extends Component {
d: [{ value: altPath }], d: [{ value: altPath }],
stroke: ['#3050f8', '#a52a2a'], stroke: ['#3050f8', '#a52a2a'],
easing: 'easeInOutSine', easing: 'easeInOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -64,8 +55,6 @@ class Amplify extends Component {
baseFrequency: 0.15, baseFrequency: 0.15,
scale: 4, scale: 4,
easing: 'easeInOutExpo', easing: 'easeInOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -78,8 +67,7 @@ class Amplify extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Amplify); module.exports = Amplify;
+4 -13
View File
@@ -5,13 +5,6 @@ const anime = require('animejs').default;
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Attack extends Component { class Attack extends Component {
constructor(props) { constructor(props) {
super(); super();
@@ -46,8 +39,8 @@ class Attack extends Component {
y: [400, 200], y: [400, 200],
height: [100, 10, 0], height: [100, 10, 0],
width: [12, 5, 0], width: [12, 5, 0],
delay: () => anime.random(TIMES.TARGET_DELAY_MS, TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS / 2), delay: () => anime.random(0, TIMES.TARGET_DURATION_MS / 4),
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS * 5 / 4,
})); }));
} }
@@ -59,9 +52,7 @@ class Attack extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb(); }
} }
} module.exports = Attack;
module.exports = addState(Attack);
-1
View File
@@ -8,7 +8,6 @@ function Banish(id, idle) {
scaleY: 0, scaleY: 0,
fill: '#fff', fill: '#fff',
easing: 'easeOutElastic', easing: 'easeOutElastic',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS * 0.45, duration: TIMES.TARGET_DURATION_MS * 0.45,
direction: 'alternate', direction: 'alternate',
begin: idle.pause, begin: idle.pause,
+3 -15
View File
@@ -5,13 +5,6 @@ const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Bash extends Component { class Bash extends Component {
constructor() { constructor() {
super(); super();
@@ -54,7 +47,7 @@ class Bash extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#bash'], targets: ['#bash'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -64,7 +57,6 @@ class Bash extends Component {
targets: ['#bash'], targets: ['#bash'],
scale: { scale: {
value: 1, value: 1,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS * 0.2, duration: TIMES.TARGET_DURATION_MS * 0.2,
easing: 'easeInExpo', easing: 'easeInExpo',
}, },
@@ -72,10 +64,9 @@ class Bash extends Component {
value: 180, value: 180,
easing: 'linear', easing: 'linear',
loop: true, loop: true,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
}, },
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.1, delay: TIMES.TARGET_DURATION_MS * 0.1,
duration: TIMES.TARGET_DURATION_MS * 0.2, duration: TIMES.TARGET_DURATION_MS * 0.2,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -86,8 +77,6 @@ class Bash extends Component {
scale: 10, scale: 10,
numOctaves: 5, numOctaves: 5,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -100,8 +89,7 @@ class Bash extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Bash); module.exports = Bash;
+3 -14
View File
@@ -1,18 +1,10 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Bash extends Component { class Bash extends Component {
constructor() { constructor() {
super(); super();
@@ -47,7 +39,7 @@ class Bash extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#bash'], targets: ['#bash'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -57,7 +49,6 @@ class Bash extends Component {
targets: ['#bash'], targets: ['#bash'],
scale: { scale: {
value: 1, value: 1,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS * 0.2, duration: TIMES.TARGET_DURATION_MS * 0.2,
easing: 'easeInExpo', easing: 'easeInExpo',
}, },
@@ -74,7 +65,7 @@ class Bash extends Component {
{ translateX: 0, translateY: 2 }, { translateX: 0, translateY: 2 },
], ],
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.1, delay: TIMES.TARGET_DURATION_MS * 0.1,
duration: TIMES.TARGET_DURATION_MS * 0.2, duration: TIMES.TARGET_DURATION_MS * 0.2,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -90,7 +81,6 @@ class Bash extends Component {
], ],
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -103,8 +93,7 @@ class Bash extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Bash); module.exports = Bash;
+3 -27
View File
@@ -1,34 +1,12 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const times = require('lodash/times'); const times = require('lodash/times');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
function projectile(x, y, radius, colour) {
return (
<circle
cx={x}
cy={y}
r={radius}
fill="url(#grad1)"
stroke-width="2"
stroke={colour}
filter="url(#explosion)"
/>
);
}
class Blast extends Component { class Blast extends Component {
constructor(props) { constructor() {
super(); super();
this.animations = []; this.animations = [];
} }
@@ -62,7 +40,7 @@ class Blast extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#blast'], targets: ['#blast'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.5, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.5, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -75,7 +53,6 @@ class Blast extends Component {
`, `,
style: { rotate: anime.random(-180, 180) }, style: { rotate: anime.random(-180, 180) },
easing: 'easeOutCubic', easing: 'easeOutCubic',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -88,8 +65,7 @@ class Blast extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Blast); module.exports = Blast;
+2 -11
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Block extends Component { class Block extends Component {
constructor() { constructor() {
super(); super();
@@ -38,7 +30,7 @@ class Block extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#block'], targets: ['#block'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -53,8 +45,7 @@ class Block extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Block); module.exports = Block;
+2 -15
View File
@@ -1,20 +1,12 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
// logarithmic spiral lifted from // logarithmic spiral lifted from
// https://upload.wikimedia.org/wikipedia/commons/5/5b/Logarithmic_spiral_(1).svg // https://upload.wikimedia.org/wikipedia/commons/5/5b/Logarithmic_spiral_(1).svg
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Break extends Component { class Break extends Component {
constructor() { constructor() {
super(); super();
@@ -63,7 +55,7 @@ class Break extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#break'], targets: ['#break'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -74,7 +66,6 @@ class Break extends Component {
rotate: 180, rotate: 180,
easing: 'linear', easing: 'linear',
loop: true, loop: true,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -82,7 +73,6 @@ class Break extends Component {
targets: ['#break circle'], targets: ['#break circle'],
easing: 'easeInSine', easing: 'easeInSine',
r: 300, r: 300,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -93,10 +83,8 @@ class Break extends Component {
numOctaves: 5, numOctaves: 5,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
// this is necessary because // this is necessary because
@@ -107,8 +95,7 @@ class Break extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Break); module.exports = Break;
+2 -14
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Buff extends Component { class Buff extends Component {
constructor() { constructor() {
super(); super();
@@ -39,7 +31,7 @@ class Buff extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#buff'], targets: ['#buff'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -50,7 +42,6 @@ class Buff extends Component {
points: '0,190 100,10 190,190', points: '0,190 100,10 190,190',
easing: 'easeOutExpo', easing: 'easeOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -59,7 +50,6 @@ class Buff extends Component {
points: '40,170 100,50 160,170', points: '40,170 100,50 160,170',
easing: 'easeOutExpo', easing: 'easeOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -68,7 +58,6 @@ class Buff extends Component {
points: '70,150 100,90 130,150', points: '70,150 100,90 130,150',
easing: 'easeOutExpo', easing: 'easeOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -81,8 +70,7 @@ class Buff extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Buff); module.exports = Buff;
+12 -51
View File
@@ -1,26 +1,19 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const { randomPoints } = require('../../utils'); const { randomPoints } = require('../../utils');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
function projectile(x, y, radius, colour) { function projectile(x, y, radius, colour) {
return ( return (
<circle <circle
cx={x} cx={anime.random(0, 400)}
cy={y} cy={anime.random(0, 400)}
r={radius} r={radius}
fill={colour} fill={colour}
stroke="none" stroke={colour === '#a52a2a' ? 'none' : '#f5f5f5'}
stroke-width={colour === '#a52a2a' ? '0' : '0.05em'}
filter={colour === '#a52a2a' ? 'url(#chaosRedFilter)' : 'url(#chaosBlueFilter)'} filter={colour === '#a52a2a' ? 'url(#chaosRedFilter)' : 'url(#chaosBlueFilter)'}
/> />
); );
@@ -30,10 +23,10 @@ class Chaos extends Component {
constructor() { constructor() {
super(); super();
this.animations = []; this.animations = [];
const points = randomPoints(20, 30, { x: 0, y: 0, width: 300, height: 100 }); const points = randomPoints(20, 30, { x: 0, y: 0, width: 1000, height: 1000 });
this.charges = points.map(coord => { this.charges = points.map(coord => {
const colour = Math.random() >= 0.5 ? '#a52a2a' : '#3050f8'; const colour = Math.random() >= 0.5 ? '#a52a2a' : '#3050f8';
return projectile(coord[0], coord[1], 14, colour); return projectile(coord[0], coord[1], '0.5em', colour);
}); });
} }
@@ -65,39 +58,12 @@ class Chaos extends Component {
componentDidMount() { componentDidMount() {
const projectiles = document.querySelectorAll('.skill-anim circle'); const projectiles = document.querySelectorAll('.skill-anim circle');
anime.set('.skill-anim', {
translateY: -(window.innerHeight) * 0.35 * this.props.direction.y,
translateX: -(window.innerWidth) * 0.15 * this.props.direction.x,
opacity: 0,
});
this.animations.push(anime({
targets: '.skill-anim',
opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.3 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.7, duration: TIMES.POST_SKILL_DURATION_MS },
],
}));
this.animations.push(anime({
targets: '.skill-anim',
translateY: 0,
translateX: 0,
loop: false,
delay: TIMES.TARGET_DELAY_MS,
duration: (TIMES.TARGET_DURATION_MS * 1 / 2),
easing: 'easeInQuad',
}));
this.animations.push(anime({ this.animations.push(anime({
targets: ['#chaosRedFilter feTurbulence', '#chaosRedFilter feDisplacementMap'], targets: ['#chaosRedFilter feTurbulence', '#chaosRedFilter feDisplacementMap'],
baseFrequency: 2, baseFrequency: 2,
scale: 20, scale: 5,
numOctaves: 5, numOctaves: 3,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -105,13 +71,9 @@ class Chaos extends Component {
targets: proj, targets: proj,
cx: 150 + (Math.random() * 50 * (Math.random() < 0.5 ? -1 : 1)), cx: 150 + (Math.random() * 50 * (Math.random() < 0.5 ? -1 : 1)),
cy: 200 + (Math.random() * 50 * (Math.random() < 0.5 ? -1 : 1)), cy: 200 + (Math.random() * 50 * (Math.random() < 0.5 ? -1 : 1)),
// cx: 150, duration: anime.random(TIMES.TARGET_DURATION_MS * 2 / 3, TIMES.TARGET_DURATION_MS),
// cy: 200, opacity: 0,
// opacity: 0, easing: 'easeInExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: (TIMES.TARGET_DURATION_MS * 2 / 3),
easing: 'easeInQuad',
}))); })));
} }
@@ -119,8 +81,7 @@ class Chaos extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Chaos); module.exports = Chaos;
+3 -14
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Counter extends Component { class Counter extends Component {
constructor() { constructor() {
super(); super();
@@ -51,7 +43,7 @@ class Counter extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#counter'], targets: ['#counter'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -60,7 +52,7 @@ class Counter extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#counter'], targets: ['#counter'],
rotateX: 180, rotateX: 180,
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS / 3, delay: TIMES.TARGET_DURATION_MS / 3,
duration: TIMES.TARGET_DURATION_MS / 2, duration: TIMES.TARGET_DURATION_MS / 2,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -71,8 +63,6 @@ class Counter extends Component {
scale: 10, scale: 10,
numOctaves: 5, numOctaves: 5,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -85,8 +75,7 @@ class Counter extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Counter); module.exports = Counter;
+2 -14
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Curse extends Component { class Curse extends Component {
constructor() { constructor() {
super(); super();
@@ -54,7 +46,7 @@ class Curse extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#curse'], targets: ['#curse'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -64,7 +56,6 @@ class Curse extends Component {
targets: ['#curseCircleOne', '#curseFilterOne'], targets: ['#curseCircleOne', '#curseFilterOne'],
r: 30, r: 30,
easing: 'easeInOutSine', easing: 'easeInOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -72,7 +63,6 @@ class Curse extends Component {
targets: ['#curseCircleTwo', '#curseFilterTwo'], targets: ['#curseCircleTwo', '#curseFilterTwo'],
r: 60, r: 60,
easing: 'easeInOutSine', easing: 'easeInOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -80,7 +70,6 @@ class Curse extends Component {
targets: ['#curseCircleThree', '#curseFilterThree'], targets: ['#curseCircleThree', '#curseFilterThree'],
r: 90, r: 90,
easing: 'easeInOutSine', easing: 'easeInOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -93,8 +82,7 @@ class Curse extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Curse); module.exports = Curse;
+2 -14
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Debuff extends Component { class Debuff extends Component {
constructor() { constructor() {
super(); super();
@@ -41,7 +33,7 @@ class Debuff extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#debuff'], targets: ['#debuff'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -52,7 +44,6 @@ class Debuff extends Component {
points: '0,190 100,10 190,190', points: '0,190 100,10 190,190',
easing: 'easeOutExpo', easing: 'easeOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -61,7 +52,6 @@ class Debuff extends Component {
points: '40,170 100,50 160,170', points: '40,170 100,50 160,170',
easing: 'easeOutExpo', easing: 'easeOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -70,7 +60,6 @@ class Debuff extends Component {
points: '70,150 100,90 130,150', points: '70,150 100,90 130,150',
easing: 'easeOutExpo', easing: 'easeOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -83,8 +72,7 @@ class Debuff extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Debuff); module.exports = Debuff;
+2 -12
View File
@@ -2,17 +2,9 @@ const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const times = require('lodash/times'); const times = require('lodash/times');
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Decay extends Component { class Decay extends Component {
constructor() { constructor() {
super(); super();
@@ -40,7 +32,7 @@ class Decay extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#decay'], targets: ['#decay'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -55,7 +47,6 @@ class Decay extends Component {
rotate(${anime.random(-15, 15)}) rotate(${anime.random(-15, 15)})
`, `,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -68,8 +59,7 @@ class Decay extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Decay); module.exports = Decay;
+2 -12
View File
@@ -1,19 +1,10 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const times = require('lodash/times'); const times = require('lodash/times');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Electrify extends Component { class Electrify extends Component {
constructor() { constructor() {
super(); super();
@@ -57,7 +48,7 @@ class Electrify extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#electrify'], targets: ['#electrify'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -109,8 +100,7 @@ class Electrify extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Electrify); module.exports = Electrify;
+2 -11
View File
@@ -1,19 +1,11 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const times = require('lodash/times') const times = require('lodash/times')
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Electrocute extends Component { class Electrocute extends Component {
constructor() { constructor() {
super(); super();
@@ -55,7 +47,7 @@ class Electrocute extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#electrify'], targets: ['#electrify'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -101,8 +93,7 @@ class Electrocute extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Electrocute); module.exports = Electrocute;
+2 -14
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Haste extends Component { class Haste extends Component {
constructor() { constructor() {
super(); super();
@@ -49,7 +41,7 @@ class Haste extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#haste'], targets: ['#haste'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -59,7 +51,6 @@ class Haste extends Component {
targets: ['#haste g'], targets: ['#haste g'],
stroke: [COLOURS.GREEN, COLOURS.RED], stroke: [COLOURS.GREEN, COLOURS.RED],
easing: 'easeInOutSine', easing: 'easeInOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS * 0.75, duration: TIMES.TARGET_DURATION_MS * 0.75,
})); }));
@@ -69,8 +60,6 @@ class Haste extends Component {
scale: 10, scale: 10,
numOctaves: 5, numOctaves: 5,
easing: 'easeInOutSine', easing: 'easeInOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -83,8 +72,7 @@ class Haste extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Haste); module.exports = Haste;
+3 -13
View File
@@ -1,18 +1,10 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const { randomPoints } = require('../../utils'); const { randomPoints } = require('../../utils');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
function projectile(x, y, radius, colour) { function projectile(x, y, radius, colour) {
return ( return (
<circle <circle
@@ -60,7 +52,7 @@ class Heal extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#heal'], targets: ['#heal'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS / 4, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS / 4, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -70,8 +62,7 @@ class Heal extends Component {
targets: ['#heal circle'], targets: ['#heal circle'],
cx: 150, cx: 150,
cy: 200, cy: 200,
delay: TIMES.TARGET_DELAY_MS * 4, duration: TIMES.TARGET_DURATION_MS * 0.6,
duration: TIMES.TARGET_DURATION_MS * 0.9,
easing: 'easeOutCirc', easing: 'easeOutCirc',
direction: 'reverse', direction: 'reverse',
})); }));
@@ -81,8 +72,7 @@ class Heal extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Heal); module.exports = Heal;
+2 -12
View File
@@ -1,16 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
// shamelessly lifted from teh anime docs // shamelessly lifted from teh anime docs
// https://animejs.com/documentation/#svgAttr // https://animejs.com/documentation/#svgAttr
@@ -54,7 +47,7 @@ class Hex extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#hex'], targets: ['#hex'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -65,7 +58,6 @@ class Hex extends Component {
points: '64 124.94732621931382 8.574 96.00354944613788 8.62269130211947 32.03166105954991 64 4 119.37730869788052 32.03166105954991 119.426 96.00354944613788', points: '64 124.94732621931382 8.574 96.00354944613788 8.62269130211947 32.03166105954991 64 4 119.37730869788052 32.03166105954991 119.426 96.00354944613788',
easing: 'easeOutExpo', easing: 'easeOutExpo',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -75,7 +67,6 @@ class Hex extends Component {
easing: 'easeInOutSine', easing: 'easeInOutSine',
direction: 'alternate', direction: 'alternate',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -88,8 +79,7 @@ class Hex extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Hex); module.exports = Hex;
+2 -16
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Hybrid extends Component { class Hybrid extends Component {
constructor() { constructor() {
super(); super();
@@ -94,12 +86,11 @@ class Hybrid extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#hybrid'], targets: ['#hybrid'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
transform: { transform: {
value: ['rotate(0)', 'rotate(360)'], value: ['rotate(0)', 'rotate(360)'],
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
direction: 'alternate', direction: 'alternate',
}, },
@@ -110,7 +101,6 @@ class Hybrid extends Component {
r: [10, anime.random(10, 30)], r: [10, anime.random(10, 30)],
targets: ['#hybrid circle.green-one'], targets: ['#hybrid circle.green-one'],
cx: [50, 250, 50, 250], cx: [50, 250, 50, 250],
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInOutSine', easing: 'easeInOutSine',
loop: true, loop: true,
@@ -120,7 +110,6 @@ class Hybrid extends Component {
r: [10, anime.random(10, 30)], r: [10, anime.random(10, 30)],
targets: ['#hybrid circle.green-two'], targets: ['#hybrid circle.green-two'],
cy: [250, 50, 250, 50], cy: [250, 50, 250, 50],
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInOutSine', easing: 'easeInOutSine',
loop: true, loop: true,
@@ -131,7 +120,6 @@ class Hybrid extends Component {
targets: ['#hybrid circle.bluewhite-one'], targets: ['#hybrid circle.bluewhite-one'],
fill: [COLOURS.WHITE, COLOURS.BLUE], fill: [COLOURS.WHITE, COLOURS.BLUE],
cy: [50, 250, 50, 250], cy: [50, 250, 50, 250],
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInOutSine', easing: 'easeInOutSine',
loop: true, loop: true,
@@ -142,7 +130,6 @@ class Hybrid extends Component {
targets: ['#hybrid circle.bluewhite-two'], targets: ['#hybrid circle.bluewhite-two'],
cx: [250, 50, 250, 50], cx: [250, 50, 250, 50],
fill: [COLOURS.WHITE, COLOURS.BLUE], fill: [COLOURS.WHITE, COLOURS.BLUE],
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInOutSine', easing: 'easeInOutSine',
loop: true, loop: true,
@@ -153,8 +140,7 @@ class Hybrid extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Hybrid); module.exports = Hybrid;
+2 -16
View File
@@ -1,7 +1,5 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { const {
@@ -9,13 +7,6 @@ const {
COLOURS, COLOURS,
} = require('../../constants'); } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Intercept extends Component { class Intercept extends Component {
constructor() { constructor() {
super(); super();
@@ -47,7 +38,7 @@ class Intercept extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#intercept'], targets: ['#intercept'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -61,7 +52,6 @@ class Intercept extends Component {
], ],
strokeWidth: 0, strokeWidth: 0,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInCubic', easing: 'easeInCubic',
// direction: 'reverse', // direction: 'reverse',
@@ -70,7 +60,6 @@ class Intercept extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#intercept rect'], targets: ['#intercept rect'],
y: 96, y: 96,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInCubic', easing: 'easeInCubic',
// direction: 'reverse', // direction: 'reverse',
@@ -82,8 +71,6 @@ class Intercept extends Component {
scale: 10, scale: 10,
numOctaves: 5, numOctaves: 5,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -96,8 +83,7 @@ class Intercept extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Intercept); module.exports = Intercept;
-1
View File
@@ -6,7 +6,6 @@ function Invert(id, idle) {
return anime({ return anime({
targets: [document.getElementById(id)], targets: [document.getElementById(id)],
rotate: 180, rotate: 180,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS * 0.45, duration: TIMES.TARGET_DURATION_MS * 0.45,
easing: 'easeInOutElastic', easing: 'easeInOutElastic',
direction: 'alternate', direction: 'alternate',
+2 -12
View File
@@ -1,18 +1,10 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Link extends Component { class Link extends Component {
constructor() { constructor() {
super(); super();
@@ -68,7 +60,7 @@ class Link extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#link'], targets: ['#link'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: 1000 }, { value: 1, duration: 1000 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.7, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.7, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -78,7 +70,6 @@ class Link extends Component {
targets: ['#link path'], targets: ['#link path'],
strokeDashoffset: [anime.setDashoffset, 0], strokeDashoffset: [anime.setDashoffset, 0],
duration: TIMES.TARGET_DURATION_MS * 0.8, duration: TIMES.TARGET_DURATION_MS * 0.8,
delay: TIMES.TARGET_DELAY_MS,
easing: 'easeInOutSine', easing: 'easeInOutSine',
}); });
} }
@@ -91,8 +82,7 @@ class Link extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Link); module.exports = Link;
+2 -13
View File
@@ -1,17 +1,8 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Purge extends Component { class Purge extends Component {
constructor() { constructor() {
super(); super();
@@ -51,7 +42,7 @@ class Purge extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#purge'], targets: ['#purge'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -60,7 +51,6 @@ class Purge extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#purge g'], targets: ['#purge g'],
strokeWidth: [15, 0], strokeWidth: [15, 0],
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInSine', easing: 'easeInSine',
})); }));
@@ -81,8 +71,7 @@ class Purge extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Purge); module.exports = Purge;
+3 -13
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
function projectile(x, y, radius, colour) { function projectile(x, y, radius, colour) {
return ( return (
<circle <circle
@@ -80,7 +72,7 @@ class Purify extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#purify'], targets: ['#purify'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -89,7 +81,7 @@ class Purify extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#block'], targets: ['#block'],
opacity: [ opacity: [
{ value: 0, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS }, { value: 0, duration: TIMES.TARGET_DURATION_MS },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
})); }));
@@ -102,7 +94,6 @@ class Purify extends Component {
easing: 'easeInOutSine', easing: 'easeInOutSine',
cx: 128, cx: 128,
cy: 24, cy: 24,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -115,8 +106,7 @@ class Purify extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Purify); module.exports = Purify;
+2 -13
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Recharge extends Component { class Recharge extends Component {
constructor() { constructor() {
super(); super();
@@ -59,7 +51,7 @@ class Recharge extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#recharge'], targets: ['#recharge'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -71,7 +63,6 @@ class Recharge extends Component {
easing: 'easeInOutSine', easing: 'easeInOutSine',
direction: 'alternate', direction: 'alternate',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -82,7 +73,6 @@ class Recharge extends Component {
numOctaves: 5, numOctaves: 5,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -95,8 +85,7 @@ class Recharge extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Recharge); module.exports = Recharge;
-83
View File
@@ -1,83 +0,0 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Block extends Component {
constructor() {
super();
this.animations = [];
}
render() {
return (
<svg
class="skill-animation red"
version="1.1"
id="block"
xmlns="http://www.w3.org/2000/svg"
style={{ transform: 'rotate(180deg)' }}
viewBox="0 0 256 256">
<filter id='blockFilter'>
<feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" result="turbulence"></feTurbulence>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="15" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
</filter>
<polygon
points='128,168 80,240 176,240'
style={{ filter: 'url("#blockFilter")' }}
id="charge"/>
<polyline
points='176,240 212,216 128,96 44,216 80,240'
style={{ filter: 'url("#blockFilter")' }}
id="charge"/>
<polyline
points='212,216 248,192 128,24 8,192 44,216'
style={{ filter: 'url("#blockFilter")' }}
id="charge"/>
</svg>
);
}
componentDidMount() {
this.animations.push(anime({
targets: ['#block'],
opacity: [
{ value: 1, delay: TIMES.TARGET_FADE_IN_DELAY, duration: TIMES.TARGET_FADE_IN_DURATION },
{ value: 0, delay: TIMES.TARGET_FADE_OUT_DELAY, duration: TIMES.FADE_OUT_DURATION },
],
easing: 'easeInOutSine',
}));
this.animations.push(anime({
targets: ['#blockFilter feTurbulence', ' #blockFilter feDisplacementMap'],
baseFrequency: 0,
scale: 1,
easing: 'easeOutSine',
delay: TIMES.TARGET_FADE_IN_DELAY,
duration: TIMES.TARGET_DURATION_MS,
}));
}
// this is necessary because
// skipping / timing / unmounting race conditions
// can cause the animations to cut short, this will ensure the values are reset
// because preact will recycle all these components
componentWillUnmount() {
for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset();
}
this.props.animCb && this.props.animCb();
}
}
module.exports = addState(Block);
@@ -1,89 +0,0 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Intercept extends Component {
constructor() {
super();
this.animations = [];
}
render({ player }) {
return (
<svg
class="skill-animation red"
version="1.1"
id="intercept"
xmlns="http://www.w3.org/2000/svg"
style={{
transform: player ? 'rotate3d(1, 0, 0, 180deg)' : '',
}}
viewBox="0 0 128 128">
<filter id='interceptFilter'>
<feTurbulence type="turbulence" baseFrequency="0" numOctaves="1" result="turbulence"></feTurbulence>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
</filter>
<g filter="url(#interceptFilter)">
<circle cx="64" cy="128" r="48" />
<circle cx="64" cy="128" r="32" />
<circle cx="64" cy="128" r="16" />
</g>
</svg>
);
}
componentDidMount() {
this.animations.push(anime({
targets: ['#intercept'],
opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
],
easing: 'easeInOutSine',
}));
this.animations.push(anime({
targets: ['#intercept'],
scale: 3,
strokeWidth: 0,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInOutCubic',
}));
this.animations.push(anime({
targets: ['#interceptFilter feTurbulence', '#interceptFilter feDisplacementMap'],
baseFrequency: 2,
scale: 10,
numOctaves: 5,
easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS,
}));
}
// this is necessary because
// skipping / timing / unmounting race conditions
// can cause the animations to cut short, this will ensure the values are reset
// because preact will recycle all these components
componentWillUnmount() {
for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset();
}
this.props.animCb && this.props.animCb();
}
}
module.exports = addState(Intercept);
+2 -11
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Refl extends Component { class Refl extends Component {
constructor() { constructor() {
super(); super();
@@ -54,7 +46,7 @@ class Refl extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#reflect'], targets: ['#reflect'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -77,8 +69,7 @@ class Refl extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Refl); module.exports = Refl;
+5 -16
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Restrict extends Component { class Restrict extends Component {
constructor() { constructor() {
super(); super();
@@ -42,7 +34,7 @@ class Restrict extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#restrict'], targets: ['#restrict'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -52,12 +44,11 @@ class Restrict extends Component {
targets: ['#restrict'], targets: ['#restrict'],
scale: { scale: {
value: 1, value: 1,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS * 0.2, duration: TIMES.TARGET_DURATION_MS * 0.2,
easing: 'easeInExpo', easing: 'easeInExpo',
}, },
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.1, delay: TIMES.TARGET_DURATION_MS * 0.1,
duration: TIMES.TARGET_DURATION_MS * 0.2, duration: TIMES.TARGET_DURATION_MS * 0.2,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -69,7 +60,6 @@ class Restrict extends Component {
numOctaves: 5, numOctaves: 5,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -78,7 +68,7 @@ class Restrict extends Component {
d: 'M 124 8 L 8 124', d: 'M 124 8 L 8 124',
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.4, delay: TIMES.TARGET_DURATION_MS * 0.4,
duration: TIMES.TARGET_DURATION_MS * 0.4, duration: TIMES.TARGET_DURATION_MS * 0.4,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -88,7 +78,7 @@ class Restrict extends Component {
d: 'M 124 124 L 8 8', d: 'M 124 124 L 8 8',
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.4, delay: TIMES.TARGET_DURATION_MS * 0.4,
duration: TIMES.TARGET_DURATION_MS * 0.4, duration: TIMES.TARGET_DURATION_MS * 0.4,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -102,8 +92,7 @@ class Restrict extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Restrict); module.exports = Restrict;
+2 -16
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Ruin extends Component { class Ruin extends Component {
constructor() { constructor() {
super(); super();
@@ -75,7 +67,7 @@ class Ruin extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#ruin'], targets: ['#ruin'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -86,7 +78,6 @@ class Ruin extends Component {
rotate: 180, rotate: 180,
easing: 'linear', easing: 'linear',
loop: true, loop: true,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -99,12 +90,7 @@ class Ruin extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
try {
this.props.animCb && this.props.animCb();
} catch (e) {
console.log(e);
}
} }
} }
module.exports = addState(Ruin); module.exports = Ruin;
+5 -15
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Silence extends Component { class Silence extends Component {
constructor() { constructor() {
super(); super();
@@ -47,7 +39,7 @@ class Silence extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#silence'], targets: ['#silence'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -58,12 +50,11 @@ class Silence extends Component {
rotate: [90, 90], rotate: [90, 90],
scale: { scale: {
value: 1, value: 1,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS * 0.2, duration: TIMES.TARGET_DURATION_MS * 0.2,
easing: 'easeInExpo', easing: 'easeInExpo',
}, },
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.1, delay: TIMES.TARGET_DURATION_MS * 0.1,
duration: TIMES.TARGET_DURATION_MS * 0.2, duration: TIMES.TARGET_DURATION_MS * 0.2,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -73,7 +64,7 @@ class Silence extends Component {
d: 'M 124 8 L 8 124', d: 'M 124 8 L 8 124',
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.4, delay: TIMES.TARGET_DURATION_MS * 0.4,
duration: TIMES.TARGET_DURATION_MS * 0.4, duration: TIMES.TARGET_DURATION_MS * 0.4,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -83,7 +74,7 @@ class Silence extends Component {
d: 'M 124 124 L 8 8', d: 'M 124 124 L 8 8',
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.4, delay: TIMES.TARGET_DURATION_MS * 0.4,
duration: TIMES.TARGET_DURATION_MS * 0.4, duration: TIMES.TARGET_DURATION_MS * 0.4,
easing: 'easeOutSine', easing: 'easeOutSine',
})); }));
@@ -97,8 +88,7 @@ class Silence extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Silence); module.exports = Silence;
+3 -15
View File
@@ -1,19 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const duration = TIMES.TARGET_DURATION_MS;
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Siphon extends Component { class Siphon extends Component {
constructor() { constructor() {
super(); super();
@@ -43,7 +33,7 @@ class Siphon extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: '.skill-anim', targets: '.skill-anim',
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.3 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.3 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.7, duration: TIMES.POST_SKILL_DURATION_MS }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.7, duration: TIMES.POST_SKILL_DURATION_MS },
], ],
})); }));
@@ -51,8 +41,7 @@ class Siphon extends Component {
anime({ anime({
targets: '#siphon', targets: '#siphon',
r: 0, r: 0,
delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS,
duration,
easing: 'easeInSine', easing: 'easeInSine',
}); });
} }
@@ -61,8 +50,7 @@ class Siphon extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Siphon); module.exports = Siphon;
+4 -14
View File
@@ -1,18 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const duration = TIMES.TARGET_DURATION_MS;
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
function projectile(x, y, radius, colour) { function projectile(x, y, radius, colour) {
return ( return (
@@ -82,7 +73,7 @@ class SiphonTick extends Component {
anime({ anime({
targets: '#siphon', targets: '#siphon',
r: 600, r: 600,
duration: duration * 2 / 3, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeInSine', easing: 'easeInSine',
}); });
@@ -92,8 +83,8 @@ class SiphonTick extends Component {
targets: proj, targets: proj,
cx: 150 + (Math.random() * 300 * (Math.random() < 0.5 ? -1 : 1)), cx: 150 + (Math.random() * 300 * (Math.random() < 0.5 ? -1 : 1)),
cy: 150 + (Math.random() * 300 * (Math.random() < 0.5 ? -1 : 1)), cy: 150 + (Math.random() * 300 * (Math.random() < 0.5 ? -1 : 1)),
delay: (Math.random() * duration * 1 / 2), delay: (Math.random() * TIMES.TARGET_DURATION_MS * 1 / 2),
duration, duration: TIMES.TARGET_DURATION_MS,
easing: 'easeOutQuad', easing: 'easeOutQuad',
}); });
}); });
@@ -103,8 +94,7 @@ class SiphonTick extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(SiphonTick); module.exports = SiphonTick;
+27 -89
View File
@@ -1,42 +1,17 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux'); const times = require('lodash/times');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
const addState = connect( const GREEN = '#1FF01F';
function receiveState(state) { const RED = '#a52a2a';
const { animCb } = state;
return { animCb };
}
);
function projectile(x, y, radius, colour) {
return (
<circle
cx={x}
cy={y}
stroke="none"
r={radius}
fill={colour}
/>
);
}
function sword(colour) {
return (
<polygon points='150,150 100,75, 150,300, 200,75' stroke="none" fill={colour} id="sword" filter="url(#slayFilter)"></polygon>
);
}
class Slay extends Component { class Slay extends Component {
constructor() { constructor() {
super(); super();
this.animations = []; this.animations = [];
this.colour = '#a52a2a';
const points = new Array(30).fill(0);
this.charges = points.map(() => projectile(150, 420, 7, '#1FF01F'));
} }
render() { render() {
@@ -47,13 +22,16 @@ class Slay extends Component {
id="slay" id="slay"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 300"> viewBox="0 0 300 300">
<filter id="slayFilter"> {times(10, () => (
<feGaussianBlur stdDeviation="4"/> <ellipse
<feTurbulence type="turbulence" baseFrequency="0.001" numOctaves="3" result="turbulence"/> cx={anime.random(100, 200)}
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/> cy={anime.random(-60, -30)}
</filter> stroke="none"
{sword(this.colour)} rx={anime.random(5, 10)}
{this.charges} ry={10}
fill={RED}
/>
))}
</svg> </svg>
); );
} }
@@ -73,73 +51,33 @@ class Slay extends Component {
anime.set('#slay', { anime.set('#slay', {
rotate, rotate,
});
anime.set('#slay', {
translateY: -1 * (window.innerHeight) * 0.35,
translateX: 0,
});
anime.set('#slayFilter feDisplacementMap', {
scale: 0,
});
anime.set('#sword', {
fill: this.colour,
opacity: 1, opacity: 1,
}); });
this.animations.push(anime({ anime.set('#slay ellipse',{
targets: '#slay', fill: RED,
opacity: [ })
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{
value: 0,
delay: TIMES.TARGET_DURATION_MS + TIMES.POST_SKILL_DURATION_MS * 0.2,
duration: TIMES.POST_SKILL_DURATION_MS * 0.3,
}],
translateY: 0,
translateX: 0,
loop: false,
delay: TIMES.TARGET_DELAY_MS,
duration: (TIMES.TARGET_DURATION_MS * 0.5),
easing: 'easeInQuad',
}));
this.animations.push(anime({ this.animations.push(anime({
targets: ['#slayFilter feTurbulence', '#slayFilter feDisplacementMap'], targets: ['#slay ellipse'],
baseFrequency: 10, cx: 150,
scale: 100, cy: 325,
delay: (TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.5), duration: TIMES.TARGET_DURATION_MS * 0.2,
duration: (TIMES.TARGET_DURATION_MS * 0.5), duration: TIMES.TARGET_DURATION_MS * 0.4,
easing: 'easeInQuad', easing: 'easeOutQuad',
direction: 'alternate',
})); }));
this.animations.push(anime({ setTimeout(() => anime.set('#slay ellipse',{
targets: '#sword', fill: GREEN,
opacity: 0, }), TIMES.TARGET_DURATION_MS * 0.5);
delay: (TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS),
}));
const projectiles = document.querySelectorAll('#slay circle');
projectiles.forEach(proj => {
this.animations.push(anime({
targets: proj,
cx: Math.random() * 250 + 25,
cy: Math.random() * 200 - 100,
delay: (TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS + TIMES.POST_SKILL_DURATION_MS * 0.2),
duration: (TIMES.POST_SKILL_DURATION_MS * 0.3),
easing: 'easeInQuad',
}));
});
} }
componentWillUnmount() { componentWillUnmount() {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Slay); module.exports = Slay;
+6 -27
View File
@@ -1,7 +1,6 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const { randomPoints } = require('../../utils'); const { randomPoints } = require('../../utils');
@@ -9,13 +8,6 @@ const { randomPoints } = require('../../utils');
// logarithmic spiral lifted from // logarithmic spiral lifted from
// https://upload.wikimedia.org/wikipedia/commons/5/5b/Logarithmic_spiral_(1).svg // https://upload.wikimedia.org/wikipedia/commons/5/5b/Logarithmic_spiral_(1).svg
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
function projectile(x, y, radius, colour) { function projectile(x, y, radius, colour) {
return ( return (
<circle <circle
@@ -81,7 +73,7 @@ class Sleep extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#sleep'], targets: ['#sleep'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
})); }));
@@ -91,32 +83,21 @@ class Sleep extends Component {
rotate: 180, rotate: 180,
easing: 'linear', easing: 'linear',
loop: true, loop: true,
delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS,
duration: TIMES.TARGET_DURATION_MS + TIMES.POST_SKILL_DURATION_MS,
})); }));
this.animations.push(anime({ this.animations.push(anime({
targets: ['#stun'], targets: ['#stun'],
opacity: [ opacity: [
{ value: 0, delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.8, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.8, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
})); }));
/* this.animations.push(anime({
targets: ['#heal'],
opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.7, duration: TIMES.TARGET_DURATION_MS * 0.3 },
{ value: 0, delay: TIMES.POST_SKILL_DURATION_MS * 0.6, duration: TIMES.POST_SKILL_DURATION_MS * 0.4 },
],
easing: 'easeInOutSine',
}));
*/
this.animations.push(anime({ this.animations.push(anime({
targets: ['#charges'], targets: ['#charges'],
opacity: 1, opacity: 1,
delay: anime.stagger(TIMES.TARGET_DURATION_MS * 0.015, { start: TIMES.TARGET_DELAY_MS }), delay: anime.stagger(TIMES.TARGET_DURATION_MS * 0.01),
easing: 'easeInOutSine', easing: 'easeInOutSine',
})); }));
@@ -125,11 +106,10 @@ class Sleep extends Component {
targets: ['#charges'], targets: ['#charges'],
cx: 0, cx: 0,
cy: 0, cy: 0,
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS + TIMES.POST_SKILL_DURATION_MS * 0.75, delay: TIMES.TARGET_DURATION_MS + TIMES.POST_SKILL_DURATION_MS * 0.75,
duration: TIMES.POST_SKILL_DURATION_MS * 0.25, duration: TIMES.POST_SKILL_DURATION_MS * 0.25,
easing: 'easeInOutSine', easing: 'easeInOutSine',
})); }));
} }
// this is necessary because // this is necessary because
@@ -140,8 +120,7 @@ class Sleep extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Sleep); module.exports = Sleep;
+5 -22
View File
@@ -1,31 +1,16 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default; const anime = require('animejs').default;
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Strike extends Component { class Strike extends Component {
constructor(props) { constructor() {
super(); super();
this.props = props;
this.animations = []; this.animations = [];
} }
render() { render() {
// const { x, y } = (this.props && this.props.direction) || { x: 0, y: 0 };
// const angle = (Math.atan(y / x) * (180 / Math.PI)) + 90;
// console.log(x, -y);
// console.log(angle);
// can't get this shit to work
return ( return (
<svg <svg
class="strike-anim" class="strike-anim"
@@ -54,8 +39,8 @@ class Strike extends Component {
x: [200, 0, 200], x: [200, 0, 200],
height: [200, 10, 0], height: [200, 10, 0],
width: [20, 400, 0], width: [20, 400, 0],
delay: TIMES.TARGET_DELAY_MS * 0.5,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
delay: TIMES.TARGET_DURATION_MS * 0.2,
})); }));
this.animations.push(anime({ this.animations.push(anime({
@@ -64,7 +49,7 @@ class Strike extends Component {
scale: 50, scale: 50,
numOctaves: 5, numOctaves: 5,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS + (TIMES.TARGET_DURATION_MS / 3), delay: TIMES.TARGET_DURATION_MS / 3,
duration: TIMES.TARGET_DURATION_MS / 2, duration: TIMES.TARGET_DURATION_MS / 2,
})); }));
} }
@@ -77,9 +62,7 @@ class Strike extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb(); }
} }
} module.exports = Strike;
module.exports = addState(Strike);
+2 -13
View File
@@ -1,20 +1,11 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
// logarithmic spiral lifted from
// https://upload.wikimedia.org/wikipedia/commons/5/5b/Logarithmic_spiral_(1).svg // https://upload.wikimedia.org/wikipedia/commons/5/5b/Logarithmic_spiral_(1).svg
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Stun extends Component { class Stun extends Component {
constructor() { constructor() {
super(); super();
@@ -54,7 +45,7 @@ class Stun extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#stun'], targets: ['#stun'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -65,7 +56,6 @@ class Stun extends Component {
rotate: 180, rotate: 180,
easing: 'linear', easing: 'linear',
loop: true, loop: true,
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
} }
@@ -78,8 +68,7 @@ class Stun extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Stun); module.exports = Stun;
+12 -27
View File
@@ -1,17 +1,9 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class Sustain extends Component { class Sustain extends Component {
constructor() { constructor() {
super(); super();
@@ -33,17 +25,17 @@ class Sustain extends Component {
</filter> </filter>
<polyline <polyline
id="stageOne" id="stageOne"
points='0 0' points='128,168 80,240 176,240 128,168'
style={{ filter: 'url("#sustainFilter")' }} style={{ filter: 'url("#sustainFilter")' }}
/> />
<polyline <polyline
id="stageTwo" id="stageTwo"
points='0 0' points='176,240 212,216 128,96 44,216 80,240'
style={{ filter: 'url("#sustainFilter")' }} style={{ filter: 'url("#sustainFilter")' }}
/> />
<polyline <polyline
id="stageThree" id="stageThree"
points='0 0' points='212,216 248,192 128,24 8,192 44,216'
style={{ filter: 'url("#sustainFilter")' }} style={{ filter: 'url("#sustainFilter")' }}
/> />
</svg> </svg>
@@ -54,7 +46,7 @@ class Sustain extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#sustain'], targets: ['#sustain'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.8, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.8, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -62,17 +54,15 @@ class Sustain extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#stageOne'], targets: ['#stageOne'],
points: '128,168 80,240 176,240 128,168',
keyframes: [ keyframes: [
{ {
stroke: [COLOURS.GREEN, COLOURS.RED], stroke: [COLOURS.GREEN, COLOURS.RED],
fill: [null, COLOURS.RED], fill: [null, COLOURS.RED],
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.2, delay: TIMES.TARGET_DURATION_MS * 0.2,
duration: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.15,
easing: 'easeInOutSine', easing: 'easeInOutSine',
}, },
], ],
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -82,13 +72,11 @@ class Sustain extends Component {
{ {
stroke: [COLOURS.GREEN, COLOURS.RED], stroke: [COLOURS.GREEN, COLOURS.RED],
fill: [null, COLOURS.RED], fill: [null, COLOURS.RED],
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.5, delay: TIMES.TARGET_DURATION_MS * 0.35,
duration: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.15,
easing: 'easeInOutSine', easing: 'easeInOutSine',
}, },
], ],
points: '176,240 212,216 128,96 44,216 80,240',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -98,13 +86,11 @@ class Sustain extends Component {
{ {
stroke: [COLOURS.GREEN, COLOURS.RED], stroke: [COLOURS.GREEN, COLOURS.RED],
fill: [null, COLOURS.RED], fill: [null, COLOURS.RED],
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.8, delay: TIMES.TARGET_DURATION_MS * 0.5,
duration: TIMES.TARGET_DURATION_MS * 0.4, duration: TIMES.TARGET_DURATION_MS * 0.15,
easing: 'easeInOutSine', easing: 'easeInOutSine',
}, },
], ],
points: '212,216 248,192 128,24 8,192 44,216',
delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS, duration: TIMES.TARGET_DURATION_MS,
})); }));
@@ -114,7 +100,7 @@ class Sustain extends Component {
scale: 10, scale: 10,
numOctaves: 5, numOctaves: 5,
easing: 'easeOutSine', easing: 'easeOutSine',
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.4, delay: TIMES.TARGET_DURATION_MS * 0.55,
duration: TIMES.TARGET_DURATION_MS * 0.3 + TIMES.POST_SKILL_DURATION_MS, duration: TIMES.TARGET_DURATION_MS * 0.3 + TIMES.POST_SKILL_DURATION_MS,
})); }));
} }
@@ -127,8 +113,7 @@ class Sustain extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Sustain); module.exports = Sustain;
+2 -10
View File
@@ -6,13 +6,6 @@ const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const { randomPoints } = require('../../utils'); const { randomPoints } = require('../../utils');
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
function projectile(x, y, radius, colour) { function projectile(x, y, radius, colour) {
return ( return (
<circle <circle
@@ -60,7 +53,7 @@ class Triage extends Component {
this.animations.push(anime({ this.animations.push(anime({
targets: ['#triage'], targets: ['#triage'],
opacity: [ opacity: [
{ value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 1, duration: TIMES.TARGET_DURATION_MS * 0.2 },
{ value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 }, { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.6, duration: TIMES.TARGET_DURATION_MS * 0.2 },
], ],
easing: 'easeInOutSine', easing: 'easeInOutSine',
@@ -80,8 +73,7 @@ class Triage extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Triage); module.exports = Triage;
+1 -11
View File
@@ -1,7 +1,6 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact'); const { Component } = require('preact');
const anime = require('animejs').default; const anime = require('animejs').default;
const { connect } = require('preact-redux');
const { TIMES, COLOURS } = require('../../constants'); const { TIMES, COLOURS } = require('../../constants');
const { randomPoints } = require('../../utils'); const { randomPoints } = require('../../utils');
@@ -18,17 +17,9 @@ function projectile(x, y, radius, colour) {
); );
} }
const addState = connect(
function receiveState(state) {
const { animCb } = state;
return { animCb };
}
);
class TriageTick extends Component { class TriageTick extends Component {
constructor(props) { constructor(props) {
super(); super();
this.team = props.team;
this.animations = []; this.animations = [];
const points = randomPoints(15, 10, { x: 0, y: 0, width: 300, height: 400 }); const points = randomPoints(15, 10, { x: 0, y: 0, width: 300, height: 400 });
this.charges = points.map(coord => projectile(coord[0], coord[1], 15, COLOURS.GREEN)); this.charges = points.map(coord => projectile(coord[0], coord[1], 15, COLOURS.GREEN));
@@ -79,8 +70,7 @@ class TriageTick extends Component {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(TriageTick); module.exports = TriageTick;
+19 -16
View File
@@ -13,8 +13,8 @@ const { ConstructAnimation } = require('./animations');
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
const { animSource, animTarget, animText } = state; const { animating, animSource, animTarget, resolution, account } = state;
return { animSource, animTarget, animText }; return { animating, animSource, animTarget, resolution, account };
} }
); );
@@ -43,6 +43,7 @@ class ConstructAvatar extends Component {
} }
onClick() { onClick() {
if (this.props.animating) return false;
return this.animations.push(wiggle(this.props.construct.id, this.idle)); return this.animations.push(wiggle(this.props.construct.id, this.idle));
} }
@@ -62,19 +63,24 @@ class ConstructAvatar extends Component {
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { animSource, animTarget, animText, construct } = this.props; const { animSource, animTarget, resolution, construct, account } = this.props;
// a different text object and text construct // a different text object and text construct
if (animText && animText !== prevProps.animText && animText.constructId === construct.id) { if (resolution && resolution !== prevProps.resolution && resolution.event[1].construct === construct.id) {
return wiggle(construct.id, this.idle); const type = resolution.event[0];
// only trigger the wiggle on damage and ko events rather than spam it on everything
// also stops wiggle triggering when invert effect is applied
if (['Damage', 'Ko'].includes(type)) return wiggle(construct.id, this.idle);
} }
// different source object and source construct // different source object and source construct
if (animSource && animSource !== prevProps.animSource && animSource.constructId === construct.id) { if (animSource && animSource !== prevProps.animSource && animSource.event[1].construct === construct.id) {
return sourceCast(animSource.constructId, animSource.direction, this.idle); const { construct: constructId, player, direction: [x, y] } = animSource.event[1];
const animY = y && player === account.id ? -1 : y;
return sourceCast(constructId, { x, y: animY }, this.idle);
} }
// different target object and target construct // different target object and target construct
if (animTarget && animTarget !== prevProps.animTarget && animTarget.constructId.includes(construct.id)) { if (animTarget && animTarget !== prevProps.animTarget && animTarget.event[1].construct.includes(construct.id)) {
switch (animTarget.skill) { switch (animTarget.skill) {
case 'Banish': return banish(construct.id, this.idle); case 'Banish': return banish(construct.id, this.idle);
case 'Invert': return invert(construct.id, this.idle); case 'Invert': return invert(construct.id, this.idle);
@@ -86,26 +92,23 @@ class ConstructAvatar extends Component {
shouldComponentUpdate(newProps) { shouldComponentUpdate(newProps) {
const { animSource, animTarget, animText, construct, mouseOver } = newProps; const { animSource, animTarget, resolution, construct, mouseOver } = newProps;
if (animSource !== this.props.animSource) return true; if (animSource !== this.props.animSource) return true;
if (animTarget !== this.props.animTarget) return true; if (animTarget !== this.props.animTarget) return true;
if (animText !== this.props.animText) return true; if (resolution !== this.props.resolution) return true;
if (construct !== this.props.construct) return true; if (construct !== this.props.construct) return true;
if (mouseOver !== this.props.mouseOver) return true; if (mouseOver !== this.props.mouseOver) return true;
return false; return false;
} }
} }
function ConstructText(props) { function ConstructName(props) {
const { construct } = props; const { construct } = props;
if (!construct) return false; if (!construct) return false;
return <h3 class={'name'}><span>{construct.name}</span></h3>;
const text = construct.name;
return <h3 class={'name'}><span>{text}</span></h3>;
} }
module.exports = { module.exports = {
ConstructAvatar: addState(ConstructAvatar), ConstructAvatar: addState(ConstructAvatar),
ConstructText, ConstructName,
}; };
+4
View File
@@ -10,6 +10,7 @@ const addState = connect(
function receiveState(state) { function receiveState(state) {
const { const {
ws, ws,
authenticated,
account, account,
game, game,
instance, instance,
@@ -17,6 +18,7 @@ const addState = connect(
} = state; } = state;
return { return {
authenticated,
account, account,
game, game,
instance, instance,
@@ -28,6 +30,7 @@ const addState = connect(
function Controls(args) { function Controls(args) {
const { const {
game, game,
authenticated,
account, account,
instance, instance,
nav, nav,
@@ -38,6 +41,7 @@ function Controls(args) {
if (game) return <GameCtrl />; if (game) return <GameCtrl />;
if (instance) return <InstanceCtrl />; if (instance) return <InstanceCtrl />;
if (!authenticated) return false;
if (nav === 'play' || nav === 'shop' || nav === 'reshape' || !nav) return <PlayCtrl /> if (nav === 'play' || nav === 'shop' || nav === 'reshape' || !nav) return <PlayCtrl />
if (nav === 'team' || nav === 'account') return <TeamCtrl /> if (nav === 'team' || nav === 'account') return <TeamCtrl />
-190
View File
@@ -1,190 +0,0 @@
const { connect } = require('preact-redux');
const preact = require('preact');
// const actions = require('../actions');
const shapes = require('./shapes');
const { ConstructAvatar } = require('./construct');
// const { ConstructAnimation } = require('./animations');
const addState = connect(
function receiveState(state) {
const {
account,
itemInfo,
demo,
} = state;
return {
account,
itemInfo,
demo,
};
}
/* function receiveDispatch(dispatch) {
function setAnimTarget(anim) {
dispatch(actions.setAnimTarget(anim));
}
return { setAnimTarget };
} */
);
function Demo(args) {
const {
demo,
itemInfo,
account,
// setAnimTarget,
} = args;
if (!demo || !itemInfo.items.length || account) return false;
const { combiner, items, equipping, equipped, players, combo } = demo;
const vboxDemo = () => {
function stashBtn(i, j) {
if (!i) return <button disabled class='empty' >&nbsp;</button>;
const highlighted = combiner.indexOf(j) > -1;
const classes = `${highlighted ? 'highlight' : ''}`;
if (shapes[i]) {
return <button class={classes} key={j}>{shapes[i]()}</button>;
}
return <button class={classes}>{i}</button>;
}
function combinerBtn() {
let text = '';
if (combiner.length < 3) {
for (let i = 0; i < 3; i++) {
if (combiner.length > i) {
text += '■ ';
} else {
text += '▫ ';
}
}
} else {
text = 'combine';
}
return (
<button
class='vbox-btn'
disabled={combiner.length !== 3}>
{text}
</button>
);
}
function stashElement() {
return (
<div class="vbox">
<div class='vbox-section'>
<h2 class='colour-info'>
VBOX PHASE {shapes.Red()} {shapes.Green()} {shapes.Blue()}
</h2>
<p>
Combine colours with base skills and specialisations to build an array of powerful variants.
</p>
</div>
<div>&nbsp;</div>
<div class='vbox-section'>
<div class='vbox-items'>
{items.map((i, j) => stashBtn(i, j))}
</div>
{combinerBtn()}
</div>
</div>
);
}
return (
<div class="news vbox-demo">
{stashElement()}
</div>
);
};
const vboxConstructs = () => {
const btnClass = equipping
? 'equipping empty gray'
: 'empty gray';
const constructEl = c => (
<div class="instance-construct">
<h2 class="name" >{c.name}</h2>
<ConstructAvatar construct={c} />
<div class="skills">
{equipped
? <button>{combo}</button>
: <button disabled={!equipping} class={btnClass}>SKILL</button>
}
<button disabled={!equipping} class={btnClass}>SKILL</button>
<button disabled={!equipping} class={btnClass}>SKILL</button>
</div>
<div class="specs">
</div>
<div class="stats">
</div>
</div>
);
return (
<section class="construct-section">
<div>
<h2>CONSTRUCTS</h2>
<p><b>Constructs</b> are the units you control. They are reset every game and their initial appearance is randomly generated.</p>
<p><b>Skills</b> and <b>Specs</b> you create in the <b>VBOX Phase</b> are equipped to your constructs to create a build.</p>
</div>
<div class='construct-list'>
{constructEl(players[0].constructs[0])}
</div>
</section>
);
};
const gameDemo = () => {
return (
<section class="game-demo">
<div>
<h2>COMBAT PHASE</h2>
<p>Battle your opponent using dynamic team builds from the VBOX phase.</p>
<p>The skills crafted can be used to damage the opponent or support your team.</p>
<p>Simultaneous turn based combat: each team picks targets for their skills during this phase.</p>
<p>The damage dealt by skills, cast order and construct life depend on your decisions in the VBOX phase.</p>
</div>
<div class="game">
<div class="game-construct">
<div class="left"></div>
<div class="right">
<ConstructAvatar construct={players[1].constructs[0]} />
</div>
</div>
<div></div>
<div class="game-construct">
<div class="left"></div>
<div class="right">
<ConstructAvatar construct={players[1].constructs[1]} />
</div>
</div>
</div>
</section>
);
};
return (
<section class='demo news top'>
{gameDemo()}
{vboxDemo()}
{vboxConstructs()}
</section>
);
}
module.exports = addState(Demo);
+85
View File
@@ -0,0 +1,85 @@
// const { connect } = require('preact-redux');
const preact = require('preact');
const { connect } = require('preact-redux');
const { errorToast, infoToast } = require('../utils');
const actions = require('./../actions');
const VERSION = process.env.npm_package_version;
const Welcome = require('./welcome');
const addState = connect(
function receiveState(state) {
const {
ws,
account,
} = state;
function sendInstancePractice() {
ws.sendInstancePractice();
}
return {
account,
sendInstancePractice,
};
},
);
function Play(args) {
const {
account,
sendInstancePractice,
} = args;
const news = (
<div class="list">
<div class="intro">
<p> MNML is a turn-based 1v1 strategy game in an abstract setting. </p>
<p>
Build a unique team of 3 constructs from a range of skills and specialisations.<br />
Outplay your opponent across multiple rounds by adapting to an always shifting meta. <br />
</p>
</div>
<div class="awards"></div>
</div>
);
const list = () => {
return (
<div class='list play'>
<figure>
<button
class="ready"
onClick={() => sendInstancePractice()}>
Play
</button>
<figcaption>Learn MNML</figcaption>
</figure>
<figure>
<button
class='discord-btn'
onClick={() => window.open('https://discord.gg/YJJgurM') }>
&nbsp;
</button>
<figcaption>Join the Community</figcaption>
</figure>
</div>
);
};
return (
<main>
<div class="logo"/>
<hr />
{list()}
<hr />
<Welcome />
<hr />
{news}
</main>
);
}
module.exports = addState(Play);
@@ -0,0 +1,89 @@
const preact = require('preact');
const { connect } = require('preact-redux');
const anime = require('animejs').default;
const reactStringReplace = require('react-string-replace');
const shapes = require('./shapes');
const { removeTier } = require('../utils');
const { TIMES } = require('./../constants');
const addState = connect(({ resolution, itemInfo }) => ({ resolution, itemInfo }));
class AnimText extends preact.Component {
shouldComponentUpdate(newProps) {
if (newProps.resolution !== this.props.resolution) return true;
return false;
}
componentDidUpdate(prevProps) {
const { resolution, construct } = this.props;
if (resolution && resolution !== prevProps.resolution && resolution.event[1].construct === construct.id) {
anime({
targets: '.combat-text',
top: '40%',
duration: TIMES.POST_SKILL_DURATION_MS - 500,
easing: 'easeOutQuad',
});
}
}
render() {
const { construct, resolution, itemInfo } = this.props;
if (resolution && resolution.event[1].construct === construct.id) {
const itemSourceDescription = () => {
const itemSource = itemInfo.combos.filter(c => c.item === removeTier(resolution.skill));
const itemSourceInfo = itemSource.length
? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}`
: false;
const itemRegEx = /(Red|Blue|Green)/;
return reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]());
};
const generateAnimText = () => {
const [type, event] = resolution.event;
switch (type) {
case 'Damage': {
const { amount, mitigation, colour } = event;
const mitigationText = mitigation ? `(${mitigation})` : '';
return <h1><span class={colour.toLowerCase()}>-{amount} {mitigationText} </span></h1>;
}
case 'Healing': {
const { amount, overhealing, colour } = event;
const overHealingText = overhealing ? `(${overhealing} OH)` : '';
return <h1><span class={colour.toLowerCase()}>+{amount} {overHealingText}</span></h1>;
}
case 'Effect': {
const { effect, duration } = event;
return <h1><span>+{effect} {duration}T</span></h1>;
}
case 'Removal': {
const { effect } = event;
if (!effect) return <h1><span>Effect Removal</span></h1>;
return <h1><span>-{effect}</span></h1>;
}
case 'Ko': return <h1><span>KO!</span></h1>;
case 'Reflection': return <h1><span>REFLECT</span></h1>;
default: return false;
}
};
// We don't send inversion / disable / immune event text
/* case 'Inversion': return <h1><span>INVERT</span></h1>;
case 'Disable': {
const { disable } = event;
return <h1><span>{disable}</span></h1>;
}
case 'Immunity': return <h1><span>IMMUNE</span></h1>; */
return (
<div class="combat-text">
<h2><span>{resolution.skill}</span></h2>
<span>{itemSourceDescription()}</span>
{generateAnimText()}
</div>
);
}
return null;
}
}
module.exports = addState(AnimText);
@@ -0,0 +1,73 @@
const preact = require('preact');
const { connect } = require('preact-redux');
const reactStringReplace = require('react-string-replace');
const actions = require('../actions');
const shapes = require('./shapes');
const { INFO } = require('./../constants');
const addState = connect(
({ resolution, itemInfo, gameSkillInfo }) => ({ resolution, itemInfo, gameSkillInfo }),
function receiveDispatch(dispatch) {
function setGameEffectInfo(info) {
dispatch(actions.setGameEffectInfo(info));
}
return { setGameEffectInfo };
}
);
class GameConstructEffects extends preact.Component {
shouldComponentUpdate(newProps) {
if (newProps.resolution && newProps.resolution !== this.props.resolution) {
const [type, info] = newProps.resolution.event;
if (info.construct === this.props.construct.id
&& (type === 'Effect' || type === 'Removal')) return true;
}
if (newProps.construct !== this.props.construct) return true;
return false;
}
render() {
const {
resolution,
construct,
gameSkillInfo,
setGameEffectInfo,
itemInfo,
} = this.props;
function hoverInfo(e, info) {
e.stopPropagation();
return setGameEffectInfo(info);
}
if (gameSkillInfo && gameSkillInfo.constructId === construct.id) {
const fullInfo = itemInfo.items.find(k => k.item === gameSkillInfo.skill) || INFO[gameSkillInfo.skill];
const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat)/;
const infoDescription = reactStringReplace(fullInfo.description, regEx, match => shapes[match]());
const speed = <span> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </span>;
return (
<div class="skill-description">
<h2><span> {gameSkillInfo.skill} </span></h2>
<span>{infoDescription} </span> <br />
{speed}
</div>);
}
const effects = resolution ? resolution.event[1].display.effects : construct.effects;
const renderEffects = effects.length
? effects.map(c =>
<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"> {renderEffects} </div>);
}
}
module.exports = addState(GameConstructEffects);
+37 -189
View File
@@ -1,95 +1,23 @@
const preact = require('preact'); const preact = require('preact');
const { connect } = require('preact-redux'); const { connect } = require('preact-redux');
const anime = require('animejs').default;
const range = require('lodash/range'); const range = require('lodash/range');
const reactStringReplace = require('react-string-replace');
const { STATS, removeTier } = require('../utils'); const { ConstructAvatar, ConstructName } = require('./construct');
const { ConstructAvatar, ConstructText } = require('./construct');
const shapes = require('./shapes');
const { INFO, TIMES } = require('./../constants');
const actions = require('../actions');
const SkillBtn = require('./skill.btn'); const SkillBtn = require('./game.construct.skill.btn');
const ConstructAnimationText = require('./game.construct.anim.text');
const addStateText = connect(({ animText, itemInfo }) => ({ animText, itemInfo })); const ConstructLife = require('./game.construct.life');
const ConstructEffectBox = require('./game.construct.effect.box');
class combatText extends preact.Component {
shouldComponentUpdate(newProps) {
if (newProps.animText !== this.props.animText) return true;
return false;
}
componentDidUpdate(prevProps) {
const { animText, construct } = this.props;
if (animText && animText !== prevProps.animText && animText.constructId === construct.id) {
anime({
targets: '.combat-text',
top: '40%',
duration: TIMES.POST_SKILL_DURATION_MS - 500,
easing: 'easeOutQuad',
});
}
}
render(props) {
const { construct, animText, itemInfo } = props;
if (animText && animText.constructId === construct.id) {
const itemSourceDescription = () => {
const itemSource = itemInfo.combos.filter(c => c.item === removeTier(animText.skill));
const itemSourceInfo = itemSource.length
? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}`
: false;
const itemRegEx = /(Red|Blue|Green)/;
return reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]());
};
const animationTextHtml = () => {
// monkaW hack to make red / blue recharge work nicely
if (animText.css === 'rb') {
const text = animText.text.split(' ');
return (
<h1>
<span class="red">{text[0]}</span>&nbsp;
<span class="blue">{text[1]}</span>
</h1>
);
}
return (
<h1 class={animText.css}>
<span>{animText.text}</span>
</h1>
);
};
return (
<div class="combat-text">
<h2><span>{animText.skill}</span></h2>
<span>{itemSourceDescription()}</span>
{animationTextHtml()}
</div>
);
}
return null;
}
}
const ConstructAnimationText = addStateText(combatText);
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
const { const {
ws, ws,
game, game,
account,
activeSkill, activeSkill,
animFocus, animFocus,
animating, resolution,
animText,
gameSkillInfo,
itemInfo,
tutorialGame,
} = state; } = state;
function selectSkillTarget(targetConstructId) { function selectSkillTarget(targetConstructId) {
@@ -100,65 +28,23 @@ const addState = connect(
} }
return { return {
game,
account,
animating,
animFocus,
animText,
activeSkill, activeSkill,
animFocus,
resolution,
selectSkillTarget, selectSkillTarget,
gameSkillInfo,
itemInfo,
tutorialGame,
}; };
},
function receiveDispatch(dispatch) {
function setGameEffectInfo(info) {
dispatch(actions.setGameEffectInfo(info));
} }
function setTutorialGameClear(activeSkill, tutorialGame) {
if (activeSkill && tutorialGame) dispatch(actions.setTutorialGame(null));
}
return { setGameEffectInfo, setTutorialGameClear };
}
); );
const eventClasses = (animating, animFocus, construct, postSkill) => {
if (!animating) return '';
if (!postSkill) {
if (animFocus.includes(construct.id)) return '';
return 'unfocus';
}
if (postSkill.constructId !== construct.id) {
if (animFocus.includes(construct.id)) return '';
return 'unfocus';
}
if (postSkill.effects) construct.effects = postSkill.effects;
construct.green_life.value = postSkill.life.green;
construct.red_life.value = postSkill.life.red;
construct.blue_life.value = postSkill.life.blue;
return postSkill.css;
};
class GameConstruct extends preact.Component { class GameConstruct extends preact.Component {
constructor() {
super();
this.resolvedLength = 0;
}
shouldComponentUpdate(newProps) { shouldComponentUpdate(newProps) {
if (newProps.activeSkill !== this.props.activeSkill) return true; if (newProps.activeSkill !== this.props.activeSkill) return true;
if (newProps.animFocus !== this.props.animFocus) return true; if (newProps.animFocus !== this.props.animFocus) return true;
if (newProps.animText !== this.props.animText) return true;
if (newProps.animating !== this.props.animating) return true;
if (newProps.construct !== this.props.construct) return true; if (newProps.construct !== this.props.construct) return true;
if (newProps.player !== this.props.player) return true; if (newProps.resolution && newProps.resolution !== this.props.resolution) {
if (newProps.tutorialGame !== this.props.tutorialGame) return true; const [type, variant] = newProps.resolution.event;
if (newProps.gameSkillInfo !== this.props.gameSkillInfo) return true; if (variant.construct === this.props.construct.id && type === 'Ko') return true;
}
return false; return false;
} }
@@ -167,84 +53,46 @@ class GameConstruct extends preact.Component {
// Changing state variables // Changing state variables
activeSkill, activeSkill,
animFocus, animFocus,
animText, resolution,
animating, selectSkillTarget,
construct, construct,
player, player,
tutorialGame,
gameSkillInfo,
// Constants
i,
itemInfo,
// Functions
selectSkillTarget,
setTutorialGameClear,
setGameEffectInfo,
} = this.props; } = this.props;
const koEvent = animText ? animText.text === 'KO!' && animText.constructId === construct.id : false; // construct green_life comes from game state and won't update during animations
const ko = construct.green_life.value === 0 && !koEvent ? 'ko' : ''; // treat the construct as ko for the remainder of the anims if ko event occurs
const classes = eventClasses(animating, animFocus, construct, animText); const ko = construct.green_life.value === 0 || this.ko ? 'ko' : '';
const cssClass = ['ko-transition', 'unfocus'].includes(classes) ? classes : null; const koEvent = () => {
if (resolution) {
const stats = ['RedLife', 'GreenLife', 'BlueLife'].map((s, j) => ( const [type, variant] = resolution.event;
<div key={j} alt={STATS[s].stat}> if (variant.construct === construct.id && type === 'Ko') {
{shapes[s]()} this.ko = true;
<div class="max" >{construct[STATS[s].stat].value} / {construct[STATS[s].stat].max}</div> return 'ko-transition';
<div class="value" >{construct[STATS[s].stat].value}</div>
</div>
));
const skills = range(0, 3)
.map(j => <SkillBtn key={j} construct={construct} i={j} j={i} animating={animating} />);
let crypSkills = <div></div>;
if (player) crypSkills = (<div class="skills"> {skills} </div>);
function hoverInfo(e, info) {
e.stopPropagation();
return setGameEffectInfo(info);
} }
const effectBox = () => {
if (gameSkillInfo && gameSkillInfo.constructId === construct.id) {
const fullInfo = itemInfo.items.find(k => k.item === gameSkillInfo.skill) || INFO[gameSkillInfo.skill];
const regEx = /(RedPower|BluePower|GreenPower|RedLife|BlueLife|GreenLife|SpeedStat)/;
const infoDescription = reactStringReplace(fullInfo.description, regEx, match => shapes[match]());
const speed = <span> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </span>;
return (
<div class="skill-description">
<h2><span> {gameSkillInfo.skill} </span></h2>
<span>{infoDescription} </span> <br />
{speed}
</div>);
} }
const effects = construct.effects.length return '';
? construct.effects.map(c =>
<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>);
}; };
const unfocus = animFocus && !animFocus.includes(construct.id) ? 'unfocus' : '';
const crypSkills = player
? <div class="skills"> {range(0, 3).map(j => <SkillBtn key={j} construct={construct} i={j} />)} </div>
: <div></div>;
return ( return (
<div <div
onClick={() => { onClick={() => selectSkillTarget(construct.id)}
selectSkillTarget(construct.id);
setTutorialGameClear(activeSkill, tutorialGame);
}}
style={ activeSkill ? { cursor: 'pointer' } : {}} style={ activeSkill ? { cursor: 'pointer' } : {}}
class={`game-construct ${ko} ${cssClass}`}> class={`game-construct ${ko} ${koEvent()} ${unfocus}`}>
<div class="left"> <div class="left">
{crypSkills} {crypSkills}
{effectBox()} <ConstructEffectBox construct={construct} />
</div> </div>
<div class="right"> <div class="right">
<div class="stats"> {stats} </div> <ConstructLife construct={construct} />
<ConstructAvatar construct={construct} /> <ConstructAvatar construct={construct} />
<ConstructText construct={construct} /> <ConstructName construct={construct} />
<ConstructAnimationText construct={construct} /> <ConstructAnimationText construct={construct} />
</div> </div>
</div> </div>
@@ -0,0 +1,49 @@
const preact = require('preact');
const { connect } = require('preact-redux');
const shapes = require('./shapes');
const addState = connect(({ resolution }) => ({ resolution }));
class GameConstructLife extends preact.Component {
shouldComponentUpdate(newProps) {
if (newProps.resolution && newProps.resolution !== this.props.resolution) {
const [type, variant] = newProps.resolution.event;
if (variant.construct === this.props.construct.id
&& (type === 'Damage' || type === 'Healing')) return true;
}
if (newProps.construct !== this.props.construct) return true;
return false;
}
render() {
const { construct, resolution } = this.props;
const lifeBars = (redLife, greenLife, blueLife) => {
return (
<div class="stats">
<div>
{shapes.RedLife()}
<div class="max" >{redLife} / {construct.red_life.max}</div>
</div>
<div>
{shapes.GreenLife()}
<div class="max" >{greenLife} / {construct.green_life.max}</div>
</div>
<div>
{shapes.BlueLife()}
<div class="max" >{blueLife} / {construct.blue_life.max}</div>
</div>
</div>
);
};
if (resolution) {
const { red, blue, green } = resolution.event[1].display;
return lifeBars(red, green, blue);
}
return lifeBars(construct.red_life.value, construct.green_life.value, construct.blue_life.value);
}
}
module.exports = addState(GameConstructLife);
@@ -10,11 +10,13 @@ const addState = connect(
const { const {
activeSkill, activeSkill,
game, game,
animating,
} = state; } = state;
return { return {
activeSkill, activeSkill,
game, game,
animating,
}; };
}, },
+5 -5
View File
@@ -8,6 +8,7 @@ const addState = connect(
const { const {
ws, ws,
account, account,
authenticated,
nav, nav,
} = state; } = state;
@@ -22,6 +23,7 @@ const addState = connect(
return { return {
account, account,
authenticated,
nav, nav,
sendInstanceState, sendInstanceState,
@@ -48,6 +50,7 @@ const addState = connect(
function Header(args) { function Header(args) {
const { const {
account, account,
authenticated,
nav, nav,
sendAccountStates, sendAccountStates,
@@ -56,6 +59,8 @@ function Header(args) {
if (!account) return false; if (!account) return false;
if (!authenticated) return false;
function navTo(p) { function navTo(p) {
return setNav(p); return setNav(p);
} }
@@ -68,11 +73,6 @@ function Header(args) {
return ( return (
<header> <header>
<div class="options"> <div class="options">
<button
onClick={() => navTo('play')}
class='logo login-btn'>
&nbsp;
</button>
<button <button
onClick={() => navTo('play')} onClick={() => navTo('play')}
class={`login-btn ${nav === 'play' ? 'highlight' : ''}`}> class={`login-btn ${nav === 'play' ? 'highlight' : ''}`}>
+1
View File
@@ -23,6 +23,7 @@ const addState = connect(
function Top(args) { function Top(args) {
const { const {
nav, nav,
authenticated,
} = args; } = args;
if (nav === 'account') return <AccountTop />; if (nav === 'account') return <AccountTop />;
+14 -3
View File
@@ -4,27 +4,38 @@ 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 FrontPage = require('./front.page');
const Noise = require('./noise');
const addState = connect( const addState = connect(
({ game, instance }) => ({ game, instance }) ({ game, instance, authenticated }) => ({ game, instance, authenticated })
); );
function Mnml(args) { function Mnml(args) {
const { const {
game, game,
instance, instance,
authenticated,
} = args; } = args;
const rotateClass = (game || instance) && window.innerHeight < 900 && window.innerWidth < window.innerHeight const rotateClass = (game || instance) && window.innerHeight < 900 && window.innerWidth < window.innerHeight
? 'show' ? 'show'
: ''; : '';
if (!authenticated && !instance && !game) return (
<div id="mnml" class='front-page'>
<Noise />
<FrontPage />
<div id="rotate" class={rotateClass} ></div>
</div>
);
return ( return (
<div id="mnml"> <div id="mnml">
<Main /> <Main />
<Controls /> <Controls />
<div id="rotate" class={rotateClass} > <Noise />
</div> <div id="rotate" class={rotateClass} ></div>
</div> </div>
); );
} }
+75
View File
@@ -0,0 +1,75 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
class Noise extends Component {
constructor() {
super();
this.animations = [];
}
render() {
return (
<svg
version="1.1"
id="noise"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 400 400">
<filter id='noiseFilter'>
<feTurbulence type="turbulence" baseFrequency="0.2" numOctaves="2" result="turbulence"></feTurbulence>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="2" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
</filter>
</svg>
);
}
componentDidMount() {
this.animations.push(anime({
targets: ['#noiseFilter feTurbulence', '#noiseFilter feDisplacementMap'],
easing: 'linear',
loop: true,
keyframes: [
{
baseFrequency: 0.5,
duration: () => anime.random(1000, 2000),
},
],
}));
this.animations.push(anime({
targets: ['#noiseFilter feDisplacementMap'],
easing: 'linear',
loop: true,
keyframes: [
{
scale: 2,
duration: () => anime.random(2000, 5000),
},
{
scale: 4,
duration: () => anime.random(150, 250),
},
{
scale: 2,
duration: () => anime.random(100, 150),
},
{
scale: 4,
duration: () => anime.random(150, 250),
},
],
}));
}
// this is necessary because
// skipping / timing / unmounting race conditions
// can cause the animations to cut short, this will ensure the values are reset
// because preact will recycle all these components
componentWillUnmount() {
for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset();
}
}
}
module.exports = Noise;
+8 -12
View File
@@ -6,11 +6,11 @@ const reactStringReplace = require('react-string-replace');
const throttle = require('lodash/throttle'); const throttle = require('lodash/throttle');
const shapes = require('./shapes'); const shapes = require('./shapes');
const { effectInfo, removeTier } = require('../utils'); const { effectInfo } = require('../utils');
const addState = connect( const addState = connect(
({ game, account, animSkill, animating, itemInfo, gameEffectInfo, tutorialGame }) => ({ game, account, animating, itemInfo, gameEffectInfo, tutorialGame }) =>
({ game, account, animSkill, animating, itemInfo, gameEffectInfo, tutorialGame }) ({ game, account, animating, itemInfo, gameEffectInfo, tutorialGame })
); );
class TargetSvg extends Component { class TargetSvg extends Component {
@@ -31,7 +31,6 @@ class TargetSvg extends Component {
if (newProps.game !== this.props.game) return true; if (newProps.game !== this.props.game) return true;
if (newProps.account !== this.props.account) return true; if (newProps.account !== this.props.account) return true;
if (newProps.animating !== this.props.animating) return true; if (newProps.animating !== this.props.animating) return true;
if (newProps.animSkill !== this.props.animSkill) return true;
if (newProps.gameEffectInfo !== this.props.gameEffectInfo) return true; if (newProps.gameEffectInfo !== this.props.gameEffectInfo) return true;
if (newProps.tutorialGame !== this.props.tutorialGame) return true; if (newProps.tutorialGame !== this.props.tutorialGame) return true;
if (newState.width !== this.state.width) return true; if (newState.width !== this.state.width) return true;
@@ -44,12 +43,9 @@ class TargetSvg extends Component {
// Changing State Variables // Changing State Variables
account, account,
animating, animating,
animSkill,
game, game,
gameEffectInfo, gameEffectInfo,
tutorialGame, tutorialGame,
// Static
itemInfo,
} = props; } = props;
const { width, height } = state; const { width, height } = state;
@@ -87,14 +83,14 @@ class TargetSvg extends Component {
const otherTeam = game.players.find(t => t.id !== account.id); const otherTeam = game.players.find(t => t.id !== account.id);
const playerTeamIds = playerTeam.constructs.map(c => c.id); const playerTeamIds = playerTeam.constructs.map(c => c.id);
const outgoing = game.stack.filter(stack => playerTeamIds.includes(stack.source_construct_id)); const outgoing = game.stack.filter(stack => stack.player === account.id);
function getPath(cast) { function getPath(cast) {
const source = playerTeam.constructs.findIndex(c => c.id === cast.source_construct_id); const source = playerTeam.constructs.findIndex(c => c.id === cast.source);
const defensive = playerTeamIds.includes(cast.target_construct_id); const defensive = playerTeamIds.includes(cast.target);
const target = defensive const target = defensive
? playerTeam.constructs.findIndex(c => c.id === cast.target_construct_id) ? playerTeam.constructs.findIndex(c => c.id === cast.target)
: otherTeam.constructs.findIndex(c => c.id === cast.target_construct_id); : otherTeam.constructs.findIndex(c => c.id === cast.target);
const skillNumber = window.innerWidth <= 800 // mobile styling trigger const skillNumber = window.innerWidth <= 800 // mobile styling trigger
? playerTeam.constructs[source].skills.findIndex(s => s.skill === cast.skill) ? playerTeam.constructs[source].skills.findIndex(s => s.skill === cast.skill)
-1
View File
@@ -71,7 +71,6 @@ class Combos extends preact.Component {
<div class="combos"> <div class="combos">
<div class="combo-header"> <div class="combo-header">
<h2>COMBOS</h2> <h2>COMBOS</h2>
Combine colours and items.
</div> </div>
<div class="combo-list" <div class="combo-list"
onMouseOver={e => e.stopPropagation()} onMouseOver={e => e.stopPropagation()}
+3 -37
View File
@@ -5,10 +5,9 @@ const Login = require('./welcome.login');
const Register = require('./welcome.register'); const Register = require('./welcome.register');
const Help = require('./welcome.help'); const Help = require('./welcome.help');
// const About = require('./welcome.about'); // const About = require('./welcome.about');
const Demo = require('./demo');
function Welcome() { function Welcome() {
const page = this.state.page || 'register'; const page = this.state.page || 'login';
const pageEl = () => { const pageEl = () => {
if (page === 'login') return <Login />; if (page === 'login') return <Login />;
@@ -17,35 +16,11 @@ function Welcome() {
return false; return false;
}; };
const news = ( const form = <div>{pageEl()}</div>;
<div class="news">
<p> Welcome to mnml.</p>
<p> MNML is a turn-based 1v1 strategy game in an abstract setting. </p>
<p>
Build a unique team of 3 constructs from a range of skills and specialisations.<br />
Outplay your opponent in multiple rounds by adapting to an always shifting meta. <br />
Simple rules, complex interactions and unique mechanics.<br />
</p>
<p> Free to play, no pay to win. Register to start playing.<br /></p>
<a href='https://www.youtube.com/watch?v=VtZLlkpJuS8'>Tutorial Playthrough on YouTube</a>
</div>
);
const main = (['login', 'register', 'help'].includes(page))
? <section>{news}{pageEl()}</section>
: <Demo />;
return ( return (
<main class="menu welcome">
<header> <header>
<div class="options"> <div class="options">
<button
onClick={() => this.setState({ page: 'login' })}
class='logo login-btn'>
&nbsp;
</button>
<button <button
class={`login-btn ${page === 'login' ? 'highlight' : ''}`} class={`login-btn ${page === 'login' ? 'highlight' : ''}`}
disabled={page === 'login'} disabled={page === 'login'}
@@ -58,12 +33,6 @@ function Welcome() {
onClick={() => this.setState({ page: 'register' })}> onClick={() => this.setState({ page: 'register' })}>
Register Register
</button> </button>
<button
class={`login-btn ${page === 'info' ? 'highlight' : ''}`}
disabled={page === 'info'}
onClick={() => this.setState({ page: 'info' })}>
Info
</button>
<button <button
class={`login-btn ${page === 'help' ? 'highlight' : ''}`} class={`login-btn ${page === 'help' ? 'highlight' : ''}`}
disabled={page === 'help'} disabled={page === 'help'}
@@ -71,11 +40,8 @@ function Welcome() {
Help Help
</button> </button>
</div> </div>
{form}
</header> </header>
<div class="top">
{main}
</div>
</main>
); );
} }
+32 -158
View File
@@ -5,8 +5,7 @@ const eachSeries = require('async/eachSeries');
const sample = require('lodash/sample'); const sample = require('lodash/sample');
const actions = require('./actions'); const actions = require('./actions');
const { TIMES } = require('./constants'); const { setAnimations, clearAnimations } = require('./animations.utils');
const animations = require('./animations.utils');
const { infoToast, errorToast } = require('./utils'); const { infoToast, errorToast } = require('./utils');
const { tutorialVbox } = require('./tutorial.utils'); const { tutorialVbox } = require('./tutorial.utils');
@@ -28,10 +27,13 @@ function registerEvents(store) {
function clearTutorial() { function clearTutorial() {
store.dispatch(actions.setTutorial(null)); store.dispatch(actions.setTutorial(null));
localStorage.setItem('tutorial-complete', true);
} }
function clearTutorialGame() {
store.dispatch(actions.setTutorialGame(null));
}
function setPing(ping) { function setPing(ping) {
store.dispatch(actions.setPing(ping)); store.dispatch(actions.setPing(ping));
} }
@@ -73,67 +75,23 @@ function registerEvents(store) {
} }
function setGame(game) { function setGame(game) {
const { game: currentGame, account, ws, animating } = store.getState(); const { game: currentGame, ws, animating } = store.getState();
if (animating) return false; if (animating) return false;
if (game && currentGame) { if (game && currentGame) {
if (game.resolved.length !== currentGame.resolved.length) { if (game.resolutions.length !== currentGame.resolutions.length) {
store.dispatch(actions.setAnimating(true)); store.dispatch(actions.setAnimating(true));
store.dispatch(actions.setGameSkillInfo(null)); store.dispatch(actions.setGameSkillInfo(null));
// 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.resolutions[game.resolutions.length - 1];
return eachSeries(newRes, (r, cb) => { return eachSeries(newRes, (r, cb) => {
if (!r.event) return cb(); if (r.delay === 0) return cb(); // TargetKo etc
let timeout = animations.getTime(r.stages); setAnimations(r, store);
const anims = animations.getObjects(r, game, account); return setTimeout(cb, r.delay);
const text = animations.getText(r);
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
if (anims.animSkill) store.dispatch(actions.setAnimSkill(anims.animSkill));
if (r.stages.includes('START_SKILL') && anims.animSource) {
store.dispatch(actions.setAnimSource(anims.animSource));
store.dispatch(actions.setAnimText(null));
}
if (r.stages.includes('END_SKILL') && anims.animTarget) {
store.dispatch(actions.setAnimTarget(anims.animTarget));
store.dispatch(actions.setAnimText(null));
if (animations.isCbAnim(anims.animSkill)) store.dispatch(actions.setAnimCb(cb));
}
if (r.stages.includes('POST_SKILL') && text) {
// timeout to prevent text classes from being added too soon
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 - 700
);
timeout -= 700;
}
}
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 (r.stages.includes('END_SKILL') && animations.isCbAnim(anims.animSkill)) return true;
return cb();
}, timeout);
}, err => { }, err => {
if (err) return console.error(err); if (err) return console.error(err);
// clear animation state clearAnimations(store);
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));
// set the game state so resolutions don't fire twice // set the game state so resolutions don't fire twice
store.dispatch(actions.setGame(game)); store.dispatch(actions.setGame(game));
ws.sendGameState(game.id); ws.sendGameState(game.id);
@@ -146,16 +104,21 @@ function registerEvents(store) {
} }
function setAccount(account) { function setAccount(account) {
if (account && process.env.NODE_ENV !== 'development') { store.dispatch(actions.setAccount(account));
LogRocket.init('yh0dy3/mnml'); }
LogRocket.identify(account.id, account);
if (window.Notification) { function setAuthenticated(account) {
if (account && window.Notification) {
window.Notification.requestPermission(); window.Notification.requestPermission();
} }
if (process.env.NODE_ENV !== 'development') {
LogRocket.identify(account.id, account);
} }
store.dispatch(actions.setAccount(account)); store.dispatch(actions.setAccount(account));
store.dispatch(actions.setTutorial(null));
store.dispatch(actions.setAuthenticated(true));
} }
function setEmail(email) { function setEmail(email) {
@@ -220,22 +183,13 @@ function registerEvents(store) {
const player = v.players.find(p => p.id === account.id); const player = v.players.find(p => p.id === account.id);
store.dispatch(actions.setPlayer(player)); store.dispatch(actions.setPlayer(player));
const skip = v.time_control === 'Practice' && v.phase === 'Lobby'; if (tutorial) tutorialVbox(player, store, tutorial);
if (skip) {
ws.sendInstanceReady(v.id);
}
if (v.phase === 'Finished') { if (v.phase === 'Finished') {
ws.sendAccountInstances(); ws.sendAccountInstances();
} }
}
// instance.mobile.less hides info at @media 1000
if (localStorage.getItem('tutorial-complete') || window.innerWidth <= 1100) {
store.dispatch(actions.setTutorial(null));
} else if (v.time_control === 'Practice' && v.rounds.length === 1 && tutorial) {
tutorialVbox(player, store, tutorial);
}
}
return store.dispatch(actions.setInstance(v)); return store.dispatch(actions.setInstance(v));
} }
@@ -252,94 +206,6 @@ function registerEvents(store) {
return store.dispatch(actions.setItemInfo(v)); return store.dispatch(actions.setItemInfo(v));
} }
function setDemo(d) {
const vboxDemo = {
players: d,
combiner: [],
equipped: false,
equipping: false,
};
const startDemo = () => {
const { account, itemInfo } = store.getState();
if (account) return false;
if (!itemInfo || itemInfo.items.length === 0) return setTimeout(startDemo, 500);
store.dispatch(actions.setAnimTarget(null));
const bases = ['Attack', 'Stun', 'Buff', 'Debuff', 'Block'];
const combo = sample(itemInfo.combos.filter(i => bases.some(b => i.components.includes(b))));
vboxDemo.combo = combo.item;
vboxDemo.items = combo.components;
store.dispatch(actions.setDemo(vboxDemo));
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, vboxDemo, { combiner: [0] }))), 500);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, vboxDemo, { combiner: [0, 1] }))), 1000);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, vboxDemo, { combiner: [0, 1, 2] }))), 1500);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, vboxDemo, { combiner: [], items: [vboxDemo.combo, '', ''] }))), 2500);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, vboxDemo, { combiner: [0], items: [vboxDemo.combo, '', ''], equipping: true }))), 3000);
setTimeout(() => store.dispatch(actions.setDemo(Object.assign({}, vboxDemo, { combiner: [], items: ['', '', ''], equipped: true, equipping: false }))), 4000);
setTimeout(() => {
return store.dispatch(actions.setAnimTarget({
skill: sample(itemInfo.items.filter(i => i.skill)).item,
constructId: d[1].constructs[0].id,
player: false,
direction: 0,
}));
}, 500);
setTimeout(() => {
return store.dispatch(actions.setAnimTarget({
skill: sample(itemInfo.items.filter(i => i.skill)).item,
constructId: d[1].constructs[1].id,
player: true,
direction: 0,
}));
}, 3000);
return setTimeout(startDemo, 5000);
};
startDemo();
}
// store.subscribe(setInfo);
// store.on('SET_INFO', setInfo);
// events.on('SET_PLAYER', setInstance);
// events.on('SEND_SKILL', function skillActive(gameId, constructId, targetConstructId, skill) {
// ws.sendGameSkill(gameId, constructId, targetConstructId, skill);
// setConstructStatusUpdate(constructId, skill, targetConstructId);
// });
// events.on('CONSTRUCT_ACTIVE', function constructActiveCb(construct) {
// for (let i = 0; i < constructs.length; i += 1) {
// if (constructs[i].id === construct.id) constructs[i].active = !constructs[i].active;
// }
// return setConstructs(constructs);
// });
/* function errorPrompt(type) {
const message = errMessages[type];
const OK_BUTTON = '<button type="submit">OK</button>';
toast.error({
theme: 'dark',
color: 'black',
timeout: false,
drag: false,
position: 'center',
maxWidth: window.innerWidth / 2,
close: false,
buttons: [
[OK_BUTTON, (instance, thisToast) => instance.hide({ transitionOut: 'fadeOut' }, thisToast)],
],
message,
});
} */
// setup / localstorage
function urlHashChange() { function urlHashChange() {
const { ws } = store.getState(); const { ws } = store.getState();
const cmds = querystring.parse(location.hash); const cmds = querystring.parse(location.hash);
@@ -348,6 +214,11 @@ function registerEvents(store) {
return true; return true;
} }
function startTutorial() {
store.dispatch(actions.setTutorial(1));
}
window.addEventListener('hashchange', urlHashChange, false); window.addEventListener('hashchange', urlHashChange, false);
return { return {
@@ -356,12 +227,13 @@ function registerEvents(store) {
clearInstance, clearInstance,
clearMtxActive, clearMtxActive,
clearTutorial, clearTutorial,
clearTutorialGame,
setAccount, setAccount,
setAuthenticated,
setAccountInstances, setAccountInstances,
setActiveItem, setActiveItem,
setActiveSkill, setActiveSkill,
setChatWheel, setChatWheel,
setDemo,
setConstructList, setConstructList,
setNewConstruct, setNewConstruct,
setGame, setGame,
@@ -377,6 +249,8 @@ function registerEvents(store) {
setSubscription, setSubscription,
setWs, setWs,
startTutorial,
urlHashChange, urlHashChange,
notify, notify,
+2 -4
View File
@@ -10,18 +10,16 @@ function createReducer(defaultState, actionType) {
/* eslint-disable key-spacing */ /* eslint-disable key-spacing */
module.exports = { module.exports = {
account: createReducer(null, 'SET_ACCOUNT'), account: createReducer(null, 'SET_ACCOUNT'),
authenticated: createReducer(null, 'SET_AUTHENTICATED'),
activeItem: createReducer(null, 'SET_ACTIVE_VAR'), activeItem: createReducer(null, 'SET_ACTIVE_VAR'),
activeSkill: createReducer(null, 'SET_ACTIVE_SKILL'), activeSkill: createReducer(null, 'SET_ACTIVE_SKILL'),
animating: createReducer(false, 'SET_ANIMATING'), animating: createReducer(false, 'SET_ANIMATING'),
animCb: createReducer(null, 'SET_ANIM_CB'),
animSkill: createReducer(null, 'SET_ANIM_SKILL'),
animSource: createReducer(null, 'SET_ANIM_SOURCE'), animSource: createReducer(null, 'SET_ANIM_SOURCE'),
animFocus: createReducer(null, 'SET_ANIM_FOCUS'), animFocus: createReducer(null, 'SET_ANIM_FOCUS'),
animTarget: createReducer(null, 'SET_ANIM_TARGET'), animTarget: createReducer(null, 'SET_ANIM_TARGET'),
animText: createReducer(null, 'SET_ANIM_TEXT'),
demo: createReducer(null, 'SET_DEMO'), resolution: createReducer(null, 'SET_RESOLUTION'),
chatShow: createReducer(null, 'SET_CHAT_SHOW'), chatShow: createReducer(null, 'SET_CHAT_SHOW'),
chatWheel: createReducer([], 'SET_CHAT_WHEEL'), chatWheel: createReducer([], 'SET_CHAT_WHEEL'),
+4 -5
View File
@@ -129,6 +129,7 @@ function createSocket(events) {
{ game_id: gameId, construct_id: constructId, target_construct_id: targetConstructId, skill }, { game_id: gameId, construct_id: constructId, target_construct_id: targetConstructId, skill },
]); ]);
events.setActiveSkill(null); events.setActiveSkill(null);
events.clearTutorialGame();
} }
function sendGameSkillClear(gameId) { function sendGameSkillClear(gameId) {
@@ -255,10 +256,6 @@ function createSocket(events) {
events.setItemInfo(info); events.setItemInfo(info);
} }
function onDemo(v) {
events.setDemo(v);
}
let pongTimeout; let pongTimeout;
function onPong() { function onPong() {
events.setPing(Date.now() - ping); events.setPing(Date.now() - ping);
@@ -273,6 +270,7 @@ function createSocket(events) {
// this object wraps the reply types to a function // this object wraps the reply types to a function
const handlers = { const handlers = {
AccountState: onAccount, AccountState: onAccount,
AccountAuthenticated: account => events.setAuthenticated(account),
AccountConstructs: onAccountConstructs, AccountConstructs: onAccountConstructs,
AccountTeam: onAccountTeam, AccountTeam: onAccountTeam,
AccountInstances: onAccountInstances, AccountInstances: onAccountInstances,
@@ -284,7 +282,6 @@ function createSocket(events) {
InstanceState: onInstanceState, InstanceState: onInstanceState,
ItemInfo: onItemInfo, ItemInfo: onItemInfo,
Pong: onPong, Pong: onPong,
Demo: onDemo,
// QueueRequested: () => events.notify('PVP queue request received.'), // QueueRequested: () => events.notify('PVP queue request received.'),
QueueRequested: () => true, QueueRequested: () => true,
@@ -303,6 +300,8 @@ function createSocket(events) {
ChatWheel: wheel => events.setChatWheel(wheel), ChatWheel: wheel => events.setChatWheel(wheel),
// Joining: () => events.notify('Searching for instance...'), // Joining: () => events.notify('Searching for instance...'),
StartTutorial: () => events.startTutorial(),
Processing: () => true, Processing: () => true,
Error: errHandler, Error: errHandler,
}; };
+21 -18
View File
@@ -115,8 +115,9 @@ function tutorialStage(tutorial, clearTutorial, instance) {
if (tutorial === 1) { if (tutorial === 1) {
return ( return (
<div class='info-item'> <div class='info-item'>
<h2>Tutorial</h2> <h1>Welcome to MNML</h1>
<p> Welcome to the vbox phase tutorial.</p> <p> This is the <b>VBOX Phase</b> tutorial.</p>
<p> In the <b>VBOX Phase</b> you customise your constructs' skills and specialisations. </p>
<p> Colours are used to create powerful combinations with base items. </p> <p> Colours are used to create powerful combinations with base items. </p>
<p> Buy the two colours from the store to continue. </p> <p> Buy the two colours from the store to continue. </p>
</div> </div>
@@ -126,9 +127,9 @@ function tutorialStage(tutorial, clearTutorial, instance) {
if (tutorial === 2) { if (tutorial === 2) {
return ( return (
<div class='info-item'> <div class='info-item'>
<h2>Tutorial</h2> <h2>Combining Items</h2>
<p> You start the game with the base <b>Attack</b> skill item. </p> <p> You start the game with the base <b>Attack</b> skill item. </p>
<p> Highlight all three items then click combine.</p> <p> Highlight the <b>Attack</b> and the two <b> colours</b> then click <b> combine</b> </p>
</div> </div>
); );
} }
@@ -137,11 +138,11 @@ function tutorialStage(tutorial, clearTutorial, instance) {
const constructOne = instance.players[0].constructs[0].name; const constructOne = instance.players[0].constructs[0].name;
return ( return (
<div class='info-item'> <div class='info-item'>
<h2>Tutorial</h2> <h2>Equipping Items</h2>
<p> The first construct on your team is <b>{constructOne}</b>. </p> <p> The first construct on your team is <b>{constructOne}</b>. </p>
<p> Skill items can be equipped to your constructs to be used in the combat phase. </p> <p> Skill items can be equipped to your constructs to be used in the combat phase. </p>
<p> Click your new skill from the stash. <br /> <p> Click your new skill from the stash. <br />
Once selected click the flashing <b>SKILL</b> slot to equip the skill. </p> Once selected click the flashing <b>SKILL</b> slot or the construct img to equip the skill. </p>
</div> </div>
); );
} }
@@ -149,7 +150,7 @@ function tutorialStage(tutorial, clearTutorial, instance) {
if (tutorial === 4) { if (tutorial === 4) {
return ( return (
<div class='info-item'> <div class='info-item'>
<h2>Tutorial</h2> <h2>Specialisations</h2>
<p> You can also buy specialisation items for your constructs. <br /> <p> You can also buy specialisation items for your constructs. <br />
Specialisation items increase stats including power, speed and life. </p> Specialisation items increase stats including power, speed and life. </p>
<p> Buy the specialisation item from the store to continue. </p> <p> Buy the specialisation item from the store to continue. </p>
@@ -160,11 +161,12 @@ function tutorialStage(tutorial, clearTutorial, instance) {
if (tutorial === 5) { if (tutorial === 5) {
return ( return (
<div class='info-item'> <div class='info-item'>
<h2>Tutorial</h2> <h2>Specialisations</h2>
<p> Equipping specialisation items will increase the stats of your constructs.</p> <p> Equipping specialisation items will increase the stats of your constructs.</p>
<p> These can also be combined with colours for further specialisation. </p> <p> These can also be combined with colours for further specialisation. </p>
<p> Click the specialisation item in the stash.<br /> <p> Click the specialisation item in the stash.<br />
Once selected click the flashing <b>SPEC</b> slot to equip the specialisation. </p> Once selected click the flashing <b>SPEC</b> slot to equip the specialisation. </p>
<p> <b>PRO TIP:</b> while selecting an item in the shop, click on your construct to buy and equip in one step. </p>
</div> </div>
); );
} }
@@ -174,11 +176,12 @@ function tutorialStage(tutorial, clearTutorial, instance) {
const constructThree = instance.players[0].constructs[2].name; const constructThree = instance.players[0].constructs[2].name;
return ( return (
<div class='info-item'> <div class='info-item'>
<h2>Tutorial</h2> <h2>Skills</h2>
<p> You have now created a construct with an upgraded skill and base spec. </p> <p> You have now created a construct with an upgraded skill and base spec. </p>
<p> The goal is to create three powerful constructs for combat. </p> <p> The goal is to create three powerful constructs for combat. </p>
<p> Equip your other constructs <b>{constructTwo}</b> and <b>{constructThree}</b> with the Attack skill. <br /> <p> Equip your other constructs <b>{constructTwo}</b> and <b>{constructThree}</b> with the Attack skill. <br />
Ensure each construct has a single skill to continue. </p> Ensure each construct has a single skill to continue. </p>
<p> <b>PRO TIP:</b> Select a skill or spec on a construct and click another construct to swap it. </p>
</div> </div>
); );
} }
@@ -186,7 +189,7 @@ function tutorialStage(tutorial, clearTutorial, instance) {
if (tutorial === 7) { if (tutorial === 7) {
return ( return (
<div class='info-item'> <div class='info-item'>
<h2>Tutorial</h2> <h2>Economy</h2>
<p> Each round you start with 30 bits and a store full of different skills, specs and colours. </p> <p> Each round you start with 30 bits and a store full of different skills, specs and colours. </p>
<p> Bits are your currency for buying items. <br /> <p> Bits are your currency for buying items. <br />
You can refill the store by pressing the refill button for 2b. <br /> You can refill the store by pressing the refill button for 2b. <br />
@@ -203,22 +206,22 @@ function tutorialStage(tutorial, clearTutorial, instance) {
return ( return (
<div class='info-item'> <div class='info-item'>
<h2>Tutorial</h2> <h2>GLHF</h2>
<p>That completes the VBOX Tutorial.</p> <p>That completes the VBOX Tutorial.</p>
<p>Press <b>READY</b> to progress to the <b>GAME PHASE</b> <br /> <p>Press the green <b>READY</b> button in the bottom right to progress to the <b>GAME PHASE</b> <br />
You can continue creating new items to upgrade your constructs further. </p> or continue creating new items to upgrade your constructs further. </p>
</div> </div>
); );
} }
return false; return false;
}; };
const classes = tutorial === 8 ? 'focus' : ''; const exitTutorial = tutorial === 8 ?
const text = tutorial === 8 ? 'Continue' : 'Skip Tutorial' <button
const exitTutorial = <button class='focus'
class={classes}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
onMouseDown={exit}> {text} </button>; onMouseDown={exit}> Continue </button>
: null;
return ( return (
<div class='tutorial'> <div class='tutorial'>
+11 -23
View File
@@ -240,28 +240,16 @@ function convertItem(v) {
} }
function effectInfo(i) { function effectInfo(i) {
// FIX ME
const hybridBlast = 25; const hybridBlast = 25;
const hasteStrike = 30; 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 115;
if (s === 'CounterAttack+') return 160; if (s === 'CounterAttack+') return 130;
if (s === 'CounterAttack++') return 230; if (s === 'CounterAttack++') return 160;
if (s === 'Electrocute') return 80;
if (s === 'DecayTick') return 33; if (s === 'Electrocute+') return 90;
if (s === 'DecayTick+') return 45; if (s === 'Electrocute++') return 100;
if (s === 'DecayTick++') return 70;
if (s === 'SiphonTick') return 25;
if (s === 'SiphonTick+') return 30;
if (s === 'SiphonTick++') return 40;
if (s === 'TriageTick') return 75;
if (s === 'TriageTick+') return 110;
if (s === 'TriageTick++') return 140;
if (s === 'Electrocute' || s === 'ElectrocuteTick') return 80;
if (s === 'Electrocute+' || s === 'ElectrocuteTick+') return 100;
if (s === 'Electrocute++' || s === 'ElectrocuteTick++') return 130;
return 0; return 0;
} }
@@ -286,13 +274,13 @@ function effectInfo(i) {
case 'Vulnerable': return `Construct will take ${i.meta[1] - 100}% increased red damage`; case 'Vulnerable': return `Construct will take ${i.meta[1] - 100}% increased red damage`;
case 'Silence': return 'Disable construct from casting any blue skills'; case 'Silence': return 'Disable construct from casting any blue skills';
case 'Wither': return `Construct will take ${100 - i.meta[1]}% reduced healing`; // case 'Wither': return `Construct will take ${100 - i.meta[1]}% reduced healing`; //
case 'Decay': return `Construct will take ${multiplier(i.tick.skill)}% of caster's BluePower as blue damage each turn.`; // case 'Decay': return `Construct will take ${i.meta[1].amount} blue damage each turn.`; //
case 'Electric': return `Attacks against this construct will apply Electrocute dealing ${multiplier(i.meta[1])}% of construct BluePower as blue damage each turn.`; case 'Electric': return `Attacks against this construct will apply Electrocute dealing ${multiplier(i.meta[1])}% of construct BluePower as blue damage each turn.`;
case 'Electrocute': return `Construct will take ${multiplier(i.tick.skill)}% of caster's BluePower as blue damage each turn.`; case 'Electrocute': return `Construct will take ${i.meta[1].amount} blue damage each turn.`;
case 'Absorb': return 'If construct takes damage, Absorption will be applied increasing RedPower and BluePower based on damage taken.'; case 'Absorb': return 'If construct takes damage, Absorption will be applied increasing RedPower and BluePower based on damage taken.';
case 'Absorption': return `Increasing construct RedPower and BluePower by ${i.meta[1]}`; case 'Absorption': return `Increasing construct RedPower and BluePower by ${i.meta[1]}`;
case 'Triage': return `Construct will be healed for ${multiplier(i.tick.skill)}% of caster's GreenPower each turn.`; case 'Triage': return `Construct will be healed for ${i.meta[1].amount} green life each turn.`;
case 'Siphon': return `Construct will take ${multiplier(i.tick.skill)}% of caster's BluePower + GreenPower as blue damage each turn, healing the caster.`; case 'Siphon': return `Construct will take ${i.meta[1].amount} blue damage each turn, healing the caster.`;
default: return 'Missing Effect Text'; default: return 'Missing Effect Text';
} }
+3
View File
@@ -0,0 +1,3 @@
[target.x86_64-pc-windows-msvc.gnu]
rustc-link-search = ["C:\\Program Files\\PostgreSQL\\pg96\\lib"]
+4
View File
@@ -0,0 +1,4 @@
target/
Cargo.lock
log/
.env
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mnml_core"
version = "1.11.0"
authors = ["ntr <ntr@smokestack.io>", "mashy <mashy@mnml.gg>"]
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
failure = "0.1"
log = "0.4"
rand = "0.6"
serde = "1"
serde_derive = "1"
uuid = { version = "0.5", features = ["serde", "v4"] }
+5
View File
@@ -0,0 +1,5 @@
# FIXME
game ready not auto starting resolve phase
remove big header and move to rhs of news pane
add big logo w/ noise when you mouseover stuff etc
File diff suppressed because it is too large Load Diff
+62 -31
View File
@@ -1,8 +1,9 @@
use construct::{Stat, EffectMeta}; use construct::{Stat, EffectMeta};
use game::{Colour};
use skill::{Skill}; use skill::{Skill};
use util::{IntPct}; use util::{IntPct};
pub type Cooldown = Option<u8>; pub type Cooldown = Option<usize>;
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Effect { pub enum Effect {
@@ -41,10 +42,17 @@ pub enum Effect {
// effects over time // effects over time
Triage, Triage,
Decay, Triaged, // immunity
Regen,
Siphon,
Decay,
Decayed, // immunity
Siphon,
Siphoned, // immunity
Countered,
// Regen,
// Airborne, // Airborne,
// Boost // Boost
// Bleed, // Bleed,
@@ -66,18 +74,42 @@ impl Effect {
pub fn immune(&self, skill: Skill) -> bool { pub fn immune(&self, skill: Skill) -> bool {
match self { match self {
Effect::Banish => true, Effect::Banish => true,
Effect::Sustain => [
Skill::Stun, // these provide immunity for the ticks
Skill::Silence, // the base skills will still resolve
Skill::SilencePlus, // but they have early return checks
Skill::SilencePlusPlus, // to ensure the effect is reapplied but damage is not
Skill::Ruin, Effect::Siphoned => [
Skill::RuinPlus, Skill::SiphonTick,
Skill::RuinPlusPlus,
Skill::Restrict,
Skill::RestrictPlus,
Skill::RestrictPlusPlus
].contains(&skill), ].contains(&skill),
Effect::Decayed => [
Skill::DecayTick,
].contains(&skill),
Effect::Triaged => [
Skill::TriageTick,
].contains(&skill),
Effect::Countered => [
Skill::CounterAttack,
Skill::CounterAttackPlus,
Skill::CounterAttackPlusPlus,
].contains(&skill),
_ => false,
}
}
// hidden effects are used generally for immunities
// they are not displayed on client
// and not included in counts
pub fn hidden(&self) -> bool {
match self {
Effect::Siphoned => true,
Effect::Decayed => true,
Effect::Triaged => true,
Effect::Countered => true,
_ => false, _ => false,
} }
} }
@@ -101,7 +133,7 @@ impl Effect {
pub fn modifications(&self) -> Vec<Stat> { pub fn modifications(&self) -> Vec<Stat> {
match self { match self {
// Bases // Bases
Effect::Block => vec![Stat::RedDamageTaken, Stat::BlueDamageTaken], Effect::Block => vec![Stat::RedDamageReceived, Stat::BlueDamageReceived],
Effect::Buff => vec![Stat::BluePower, Stat::RedPower, Stat::Speed], Effect::Buff => vec![Stat::BluePower, Stat::RedPower, Stat::Speed],
Effect::Slow => vec![Stat::Speed], Effect::Slow => vec![Stat::Speed],
@@ -111,10 +143,10 @@ impl Effect {
Effect::Hybrid => vec![Stat::GreenPower], Effect::Hybrid => vec![Stat::GreenPower],
// Damage taken changes // Damage taken changes
Effect::Curse => vec![Stat::RedDamageTaken, Stat::BlueDamageTaken], Effect::Curse => vec![Stat::RedDamageReceived, Stat::BlueDamageReceived],
Effect::Pure => vec![Stat::GreenDamageTaken], // increased green taken Effect::Pure => vec![Stat::GreenHealingReceived], // increased green taken
Effect::Vulnerable => vec![Stat::RedDamageTaken], Effect::Vulnerable => vec![Stat::RedDamageReceived],
Effect::Wither => vec![Stat::GreenDamageTaken], // reduced green taken Effect::Wither => vec![Stat::GreenHealingReceived], // reduced green taken
// Speed // Speed
Effect::Haste => vec![Stat::Speed], Effect::Haste => vec![Stat::Speed],
@@ -123,7 +155,7 @@ impl Effect {
} }
} }
pub fn apply(&self, value: u64, meta: Option<EffectMeta>) -> u64 { pub fn apply(&self, value: usize, meta: Option<EffectMeta>) -> usize {
match self { match self {
Effect::Amplify | Effect::Amplify |
Effect::Vulnerable | Effect::Vulnerable |
@@ -154,7 +186,10 @@ impl Effect {
} }
} }
pub fn colour(&self) -> Option<Colour> { // Old colour matching system for buffs / debuffs
// Had issues as some effects will be considered as both a buff and debuff e.g. invert,
// Ended up being confusing with mismatch skills that have red / blue e.g. amplify, haste, hybrid
/* pub fn colour(&self) -> Option<Colour> {
match self { match self {
// physical // physical
Effect::Stun => Some(Colour::Red), Effect::Stun => Some(Colour::Red),
@@ -192,18 +227,14 @@ impl Effect {
// effects over time // effects over time
Effect::Triage => Some(Colour::Green), Effect::Triage => Some(Colour::Green),
Effect::Decay => Some(Colour::Blue), Effect::Decay => Some(Colour::Blue),
Effect::Regen => Some(Colour::Green),
Effect::Siphon => Some(Colour::Blue), Effect::Siphon => Some(Colour::Blue),
Effect::Pure => Some(Colour::Green), Effect::Pure => Some(Colour::Green),
Effect::Triaged => None,
Effect::Decayed => None,
Effect::Siphoned => None,
Effect::Countered => None,
Effect::Ko => None, Effect::Ko => None,
} }
} }*/
}
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Colour {
Red,
Blue,
Green,
} }
+2514
View File
File diff suppressed because it is too large Load Diff
+27 -352
View File
@@ -1,12 +1,8 @@
use std::fs::File;
use std::collections::{HashMap}; use std::collections::{HashMap};
use uuid::Uuid; use uuid::Uuid;
use serde_cbor::{from_slice, to_vec};
use postgres::transaction::Transaction;
use failure::Error; use failure::Error;
use failure::err_msg; use failure::err_msg;
@@ -14,16 +10,11 @@ use failure::err_msg;
use chrono::prelude::*; use chrono::prelude::*;
use chrono::Duration; use chrono::Duration;
use account::Account; use player::{Player, Score};
use game::{Game};
use item::{Item};
use vbox; use vbox;
use player::{Player, Score, player_create};
use mob::{bot_player, instance_mobs};
use game::{Game, Phase, game_get, game_write, game_update};
use item::{Item};
use rpc::{RpcMessage};
use img;
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
enum InstancePhase { enum InstancePhase {
@@ -107,14 +98,14 @@ pub struct Instance {
time_control: TimeControl, time_control: TimeControl,
phase: InstancePhase, phase: InstancePhase,
phase_end: Option<DateTime<Utc>>, pub phase_end: Option<DateTime<Utc>>,
phase_start: DateTime<Utc>, pub phase_start: DateTime<Utc>,
winner: Option<Uuid>, winner: Option<Uuid>,
} }
impl Instance { impl Instance {
fn new() -> Instance { pub fn new() -> Instance {
Instance { Instance {
id: Uuid::new_v4(), id: Uuid::new_v4(),
players: vec![], players: vec![],
@@ -152,8 +143,8 @@ impl Instance {
.collect::<Vec<Uuid>>() .collect::<Vec<Uuid>>()
} }
pub fn upkeep(mut self) -> (Instance, Option<Game>) {
// time out lobbies that have been open too long // time out lobbies that have been open too long
pub fn upkeep(mut self) -> (Instance, Option<Game>) {
if self.phase == InstancePhase::Lobby && self.phase_timed_out() { if self.phase == InstancePhase::Lobby && self.phase_timed_out() {
self.finish(); self.finish();
return (self, None); return (self, None);
@@ -178,7 +169,7 @@ impl Instance {
(self, new_game) (self, new_game)
} }
fn set_name(mut self, name: String) -> Result<Instance, Error> { pub fn set_name(mut self, name: String) -> Result<Instance, Error> {
if name.len() == 0 { if name.len() == 0 {
return Err(err_msg("name must have a length")); return Err(err_msg("name must have a length"));
} }
@@ -187,12 +178,12 @@ impl Instance {
Ok(self) Ok(self)
} }
fn set_time_control(mut self, tc: TimeControl) -> Instance { pub fn set_time_control(mut self, tc: TimeControl) -> Instance {
self.time_control = tc; self.time_control = tc;
self self
} }
fn add_player(&mut self, player: Player) -> Result<&mut Instance, Error> { pub fn add_player(&mut self, player: Player) -> Result<&mut Instance, Error> {
if self.players.len() >= self.max_players { if self.players.len() >= self.max_players {
return Err(err_msg("game full")) return Err(err_msg("game full"))
} }
@@ -206,7 +197,7 @@ impl Instance {
Ok(self) Ok(self)
} }
fn player_ready(&mut self, player_id: Uuid) -> Result<Option<Game>, Error> { pub fn player_ready(&mut self, player_id: Uuid) -> Result<Option<Game>, Error> {
if ![InstancePhase::InProgress, InstancePhase::Lobby].contains(&self.phase) { if ![InstancePhase::InProgress, InstancePhase::Lobby].contains(&self.phase) {
return Err(err_msg("instance not in start or vbox phase")); return Err(err_msg("instance not in start or vbox phase"));
} }
@@ -308,7 +299,7 @@ impl Instance {
self.next_round() self.next_round()
} }
fn next_round(&mut self) -> &mut Instance { pub fn next_round(&mut self) -> &mut Instance {
if self.finish_condition() { if self.finish_condition() {
return self.finish(); return self.finish();
} }
@@ -350,7 +341,7 @@ impl Instance {
self self
} }
fn finished(&self) -> bool { pub fn finished(&self) -> bool {
self.phase == InstancePhase::Finished self.phase == InstancePhase::Finished
} }
@@ -378,7 +369,7 @@ impl Instance {
self self
} }
fn current_game_id(&self) -> Option<Uuid> { pub fn current_game_id(&self) -> Option<Uuid> {
if self.phase != InstancePhase::InProgress { if self.phase != InstancePhase::InProgress {
return None; return None;
} }
@@ -394,7 +385,7 @@ impl Instance {
return current_round.game_id; return current_round.game_id;
} }
fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> { pub fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> {
{ {
let current_round = self.rounds let current_round = self.rounds
.iter_mut() .iter_mut()
@@ -436,14 +427,14 @@ impl Instance {
} }
// PLAYER ACTIONS // PLAYER ACTIONS
fn account_player(&mut self, account: Uuid) -> Result<&mut Player, Error> { pub fn account_player(&mut self, account: Uuid) -> Result<&mut Player, Error> {
self.players self.players
.iter_mut() .iter_mut()
.find(|p| p.id == account) .find(|p| p.id == account)
.ok_or(err_msg("account not in instance")) .ok_or(err_msg("account not in instance"))
} }
fn account_opponent(&mut self, account: Uuid) -> Result<&mut Player, Error> { pub fn account_opponent(&mut self, account: Uuid) -> Result<&mut Player, Error> {
self.players self.players
.iter_mut() .iter_mut()
.find(|p| p.id != account) .find(|p| p.id != account)
@@ -501,334 +492,18 @@ impl Instance {
Ok(self) Ok(self)
} }
pub fn vbox_unequip(mut self, account: Uuid, target: Item, construct_id: Uuid, target_construct_id: Option<Uuid>) -> Result<Instance, Error> { pub fn vbox_unequip(mut self, account: Uuid, target: Item, construct_id: Uuid, target_construct: Option<Uuid>) -> Result<Instance, Error> {
self.vbox_action_allowed(account)?; self.vbox_action_allowed(account)?;
self.account_player(account)? self.account_player(account)?
.vbox_unequip(target, construct_id, target_construct_id)?; .vbox_unequip(target, construct_id, target_construct)?;
Ok(self) Ok(self)
} }
} }
pub fn instance_create(tx: &mut Transaction, instance: Instance) -> Result<Instance, Error> {
let instance_bytes = to_vec(&instance)?;
let query = "
INSERT INTO instances (id, data, upkeep)
VALUES ($1, $2, $3)
RETURNING id;
";
let result = tx
.query(query, &[&instance.id, &instance_bytes, &instance.phase_end])?;
result.iter().next().ok_or(format_err!("no instances written"))?;
return Ok(instance);
}
pub fn instance_update(tx: &mut Transaction, instance: Instance) -> Result<Instance, Error> {
let instance_bytes = to_vec(&instance)?;
let query = "
UPDATE instances
SET data = $1, finished = $2, upkeep = $3, updated_at = now()
WHERE id = $4
RETURNING id, data;
";
let result = tx
.query(query, &[&instance_bytes, &instance.finished(), &instance.phase_end, &instance.id])?;
result.iter().next().ok_or(err_msg("no instance row returned"))?;
trace!("{:?} wrote instance", instance.id);
if instance.finished() {
info!("finished id={:?}", instance.id);
match instance_json_file_write(&instance) {
Ok(dest) => info!("wrote dest={:?}", dest),
Err(e) => error!("json write error={:?}", e),
};
}
return Ok(instance);
}
fn instance_json_file_write(g: &Instance) -> Result<String, Error> {
let dest = format!("/var/lib/mnml/data/instances/{}.mnml.instance.json", g.id);
serde_json::to_writer(File::create(&dest)?, g)?;
Ok(dest)
}
pub fn instance_get(tx: &mut Transaction, instance_id: Uuid) -> Result<Instance, Error> {
let query = "
SELECT *
FROM instances
WHERE id = $1
FOR UPDATE;
";
let result = tx
.query(query, &[&instance_id])?;
let returned = match result.iter().next() {
Some(row) => row,
None => return Err(err_msg("instance not found")),
};
let instance_bytes: Vec<u8> = returned.get("data");
let instance = from_slice::<Instance>(&instance_bytes)?;
return Ok(instance);
}
pub fn instance_delete(tx: &mut Transaction, id: Uuid) -> Result<(), Error> {
let query = "
DELETE
FROM instances
WHERE id = $1;
";
let result = tx
.execute(query, &[&id])?;
if result != 1 {
return Err(format_err!("unable to delete instance {:?}", id));
}
info!("instance deleted {:?}", id);
return Ok(());
}
pub fn _instance_list(tx: &mut Transaction) -> Result<Vec<Instance>, Error> {
let query = "
SELECT data, id
FROM instances
AND finished = false;
";
let result = tx
.query(query, &[])?;
let mut list = vec![];
for row in result.into_iter() {
let bytes: Vec<u8> = row.get(0);
let id = row.get(1);
match from_slice::<Instance>(&bytes) {
Ok(i) => list.push(i),
Err(_e) => {
instance_delete(tx, id)?;
}
};
}
return Ok(list);
}
pub fn instances_need_upkeep(tx: &mut Transaction) -> Result<Vec<Instance>, Error> {
let query = "
SELECT data, id
FROM instances
WHERE finished = false
AND upkeep < now()
FOR UPDATE;
";
let result = tx
.query(query, &[])?;
let mut list = vec![];
for row in result.into_iter() {
let bytes: Vec<u8> = row.get(0);
let id = row.get(1);
match from_slice::<Instance>(&bytes) {
Ok(i) => list.push(i),
Err(_e) => {
instance_delete(tx, id)?;
}
};
}
return Ok(list);
}
// timed out instances with no time control
pub fn instances_idle(tx: &mut Transaction) -> Result<Vec<Instance>, Error> {
let query = "
SELECT data, id
FROM instances
WHERE finished = false
AND updated_at < now() - interval '1 hour'
FOR UPDATE;
";
let result = tx
.query(query, &[])?;
let mut list = vec![];
for row in result.into_iter() {
let bytes: Vec<u8> = row.get(0);
let id = row.get(1);
match from_slice::<Instance>(&bytes) {
Ok(i) => list.push(i),
Err(_e) => {
instance_delete(tx, id)?;
}
};
}
return Ok(list);
}
pub fn instance_practice(tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let bot = bot_player();
let bot_id = bot.id;
// generate bot imgs for the client to see
for c in bot.constructs.iter() {
img::shapes_write(c.img)?;
}
let mut instance = Instance::new()
.set_time_control(TimeControl::Practice)
.set_name(bot.name.clone())?;
let player = Player::from_account(tx, account)?;
instance.add_player(player.clone())?;
instance.add_player(bot)?;
instance.player_ready(bot_id)?;
instance = instance_create(tx, instance)?;
player_create(tx, player, instance.id, account)?;
Ok(instance)
}
pub fn pvp(tx: &mut Transaction, a: &Account, b: &Account) -> Result<Instance, Error> {
let mut instance = Instance::new()
// TODO generate nice game names
.set_name("PVP".to_string())?;
instance = instance_create(tx, instance)?;
for account in [a, b].iter() {
let acc_p = Player::from_account(tx, &account)?;
let player = player_create(tx, acc_p, instance.id, account)?;
instance.add_player(player)?;
}
instance_update(tx, instance)
}
pub fn instance_abandon(tx: &mut Transaction, account: &Account, instance_id: Uuid) -> Result<RpcMessage, Error> {
let mut instance = instance_get(tx, instance_id)?;
if let Some(game_id) = instance.current_game_id() {
let mut game = game_get(tx, game_id)?;
game.player_by_id(account.id)?.forfeit();
game = game.start(); // actually finishes it...
game_update(tx, &game)?;
}
instance.account_player(account.id)?.set_lose();
instance.account_opponent(account.id)?.set_win();
instance.next_round();
Ok(RpcMessage::InstanceState(instance_update(tx, instance)?))
}
pub fn instance_ready(tx: &mut Transaction, account: &Account, instance_id: Uuid) -> Result<RpcMessage, Error> {
let mut instance = instance_get(tx, instance_id)?;
let player_id = instance.account_player(account.id)?.id;
if let Some(game) = instance.player_ready(player_id)? {
game_write(tx, &game)?;
// ensures cleanup for warden etc is done
game_update(tx, &game)?;
instance_update(tx, instance)?;
return Ok(RpcMessage::GameState(game));
}
Ok(RpcMessage::InstanceState(instance_update(tx, instance)?))
}
pub fn instance_state(tx: &mut Transaction, instance_id: Uuid) -> Result<RpcMessage, Error> {
let instance = instance_get(tx, instance_id)?;
if let Some(game_id) = instance.current_game_id() {
let game = game_get(tx, game_id)?;
// return the game until it's finished
if game.phase != Phase::Finished {
return Ok(RpcMessage::GameState(game))
}
}
Ok(RpcMessage::InstanceState(instance))
}
pub fn instance_game_finished(tx: &mut Transaction, game: &Game, instance_id: Uuid) -> Result<(), Error> {
let mut instance = instance_get(tx, instance_id)?;
instance.game_finished(game)?;
// info!("{:?}", instance_get(tx, instance_id)?);
instance_update(tx, instance)?;
Ok(())
}
pub fn bot_instance() -> Instance {
let mut instance = Instance::new();
let bot_player = bot_player();
let bot = bot_player.id;
instance.add_player(bot_player).unwrap();
let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account);
let player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true);
instance.add_player(player).expect("could not add player");
instance.player_ready(player_account).unwrap();
instance.player_ready(bot).unwrap();
return instance;
}
pub fn demo() -> Result<Vec<Player>, Error> {
let bot = bot_player();
// generate bot imgs for the client to see
for c in bot.constructs.iter() {
img::shapes_write(c.img)?;
};
let bot2 = bot_player();
// generate bot imgs for the client to see
for c in bot2.constructs.iter() {
img::shapes_write(c.img)?;
};
Ok(vec![bot, bot2])
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use mob::{bot_player, instance_mobs};
#[test] #[test]
fn instance_pve_test() { fn instance_pve_test() {
@@ -854,7 +529,7 @@ mod tests {
let _instance = Instance::new(); let _instance = Instance::new();
let player_account = Uuid::new_v4(); let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account); let constructs = instance_mobs(player_account);
let _player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true); let _player = Player::new(player_account, None, &"test".to_string(), constructs).set_bot(true);
} }
#[test] #[test]
@@ -865,7 +540,7 @@ mod tests {
let player_account = Uuid::new_v4(); let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account); let constructs = instance_mobs(player_account);
let player = Player::new(player_account, &"a".to_string(), constructs); let player = Player::new(player_account, None, &"a".to_string(), constructs);
let a_id = player.id; let a_id = player.id;
instance.add_player(player).expect("could not add player"); instance.add_player(player).expect("could not add player");
@@ -873,7 +548,7 @@ mod tests {
let player_account = Uuid::new_v4(); let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account); let constructs = instance_mobs(player_account);
let player = Player::new(player_account, &"b".to_string(), constructs); let player = Player::new(player_account, None, &"b".to_string(), constructs);
let b_id = player.id; let b_id = player.id;
instance.add_player(player).expect("could not add player"); instance.add_player(player).expect("could not add player");
@@ -902,7 +577,7 @@ mod tests {
let player_account = Uuid::new_v4(); let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account); let constructs = instance_mobs(player_account);
let player = Player::new(player_account, &"a".to_string(), constructs); let player = Player::new(player_account, None, &"a".to_string(), constructs);
let a_id = player.id; let a_id = player.id;
instance.add_player(player).expect("could not add player"); instance.add_player(player).expect("could not add player");
@@ -910,7 +585,7 @@ mod tests {
let player_account = Uuid::new_v4(); let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account); let constructs = instance_mobs(player_account);
let player = Player::new(player_account, &"b".to_string(), constructs); let player = Player::new(player_account, None, &"b".to_string(), constructs);
let b_id = player.id; let b_id = player.id;
instance.add_player(player).expect("could not add player"); instance.add_player(player).expect("could not add player");
@@ -942,7 +617,7 @@ mod tests {
let player_account = Uuid::new_v4(); let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account); let constructs = instance_mobs(player_account);
let player = Player::new(player_account, &"a".to_string(), constructs); let player = Player::new(player_account, None, &"a".to_string(), constructs);
let _a_id = player.id; let _a_id = player.id;
instance.add_player(player).expect("could not add player"); instance.add_player(player).expect("could not add player");

Some files were not shown because too many files have changed in this diff Show More