Merge branch 'release/1.11.0'
This commit is contained in:
commit
9cf81735f1
12
CHANGELOG.md
12
CHANGELOG.md
@ -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
|
||||
### Fixed
|
||||
- Fixed Item+ Purchasing Bug
|
||||
|
||||
266
COMBOS.md
266
COMBOS.md
@ -1,142 +1,200 @@
|
||||
# item_info ->
|
||||
# Spec / Skill hybrid specs #
|
||||
|
||||
combos [strike, [R R Attack]]
|
||||
specs [spec [bonus amount, [r g b]]
|
||||
Create skills specs by combining an upgraded skills with corresponding colour specs:
|
||||
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
|
||||
stats randomised
|
||||
## Why Skill Specs
|
||||
|
||||
initial stash drops
|
||||
6 skills
|
||||
6 colours
|
||||
6 specs
|
||||
- Give tools to players to make cool unique builds
|
||||
- Passive utility that you work towards
|
||||
- Specialise in a type of skill
|
||||
- Repurpose skills that you aren't using much for active use
|
||||
- More layers of complexity
|
||||
|
||||
play first round
|
||||
basically duke it out
|
||||
## Skill specs philosphy
|
||||
|
||||
# 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 ###
|
||||
Real world concepts
|
||||
Aggressive
|
||||
Apply Buffs
|
||||
Fast & Chaotic
|
||||
## Brainstorming on what kind of things skill specs could do
|
||||
|
||||
### Green ###
|
||||
Healing Specialisation
|
||||
Defensive
|
||||
Purge buffs & debuffs
|
||||
- Passive
|
||||
- (Abosrb Spec) when you take damage you permanently gain X Power/Speed/Life
|
||||
- Grant you Amplify with (X% multiplier) when you KO a target
|
||||
- Convert (X% of your red / blue power -> green power)
|
||||
- Reduce / Increase damage taken of a certain type (Block type upgrades maybe?)
|
||||
|
||||
### Blue ###
|
||||
Fantasy concepts (magical)
|
||||
Aggressive & Defensive
|
||||
Apply Debuffs
|
||||
Slow & Reliable
|
||||
- Active
|
||||
- Make a skill cast another skill (strike another guy)
|
||||
- Apply an effect when you cast a skill
|
||||
- Change damage / healing / speed of a skill
|
||||
|
||||
# Classes #
|
||||
## The BIG LIST
|
||||
|
||||
Class names to be changed
|
||||
====================
|
||||
Pure Red `Nature`
|
||||
Pure Green `Non-Violence`
|
||||
Pure Blue `Destruction`
|
||||
Hybrid Red / Blue `Chaos`
|
||||
Hybrid Red / Green `Purity`
|
||||
Hybrid Blue / Green `Technology`
|
||||
### Attack Base
|
||||
Strike
|
||||
Power
|
||||
Life
|
||||
Speed (Repeat skill at X% multiplier)
|
||||
|
||||
Chaos
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
Skills
|
||||
==========
|
||||
Heal
|
||||
Power (Convert X% Red/Blue Power to GreenPower)
|
||||
Life
|
||||
Speed
|
||||
|
||||
Basic Type
|
||||
-------------------------------------------------------------------------
|
||||
Attack `Basic offensive skill - deal damage`
|
||||
Buff `Base ally targetted skill - increase ally 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`
|
||||
Blast
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
# Attack Base #
|
||||
Slay
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
RR - Strike
|
||||
GG - Heal
|
||||
BB - Blast
|
||||
RG - Purify
|
||||
GB - Decay
|
||||
RB - Blast
|
||||
Siphon
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
# Stun Base #
|
||||
### Stun Base
|
||||
Bash
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
RR - Strangle
|
||||
GG - Break
|
||||
BB - Ruin
|
||||
RG - Banish
|
||||
GB - Silence
|
||||
RB - Hex
|
||||
Sleep
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
# Buff Base #
|
||||
Ruin
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
RR - Empower
|
||||
GR - Triage
|
||||
BB - Absorb
|
||||
RG - Sustain
|
||||
GB - Amplify
|
||||
RB - Haste
|
||||
Link
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
# Debuff Base #
|
||||
Banish
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
RR - Restrict
|
||||
GG - Purge
|
||||
BB - Curse
|
||||
RG - Slow
|
||||
GB - Siphon
|
||||
RB - Invert
|
||||
Break
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
# Block Base #
|
||||
### Block Base
|
||||
|
||||
RR - Counter
|
||||
GG - Reflect
|
||||
BB - Electrify
|
||||
RG - Intercept
|
||||
GB - Life `rename?`
|
||||
RB - Recharge
|
||||
Counter
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
Reflect
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
## Advanced combos ##
|
||||
Purify
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
Two ways of upgrading
|
||||
#1 -> combine more of the same for a stronger version of the same skill / spec (T2 / T3 Combos)
|
||||
#2 -> combine skill with two matching colour specs to change the way the skill works (Spec / Skill hybrid)
|
||||
Sustain
|
||||
Power
|
||||
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 #
|
||||
`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`
|
||||
### Buff Base
|
||||
|
||||
Upgraded skills will have a combination of higher damage / longer duration / reduced cooldown
|
||||
Upgraded skills use the same speed formula as previously
|
||||
Intercept
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
### Spec / Skill hybrid specs ###
|
||||
Triage
|
||||
Power
|
||||
Life
|
||||
Speed
|
||||
|
||||
# Strike #
|
||||
2 x Red Damage + Strike => Strike damage bonus (crit?)
|
||||
2 x Red Speed + Strike => Strike reduces enemy speed
|
||||
2 x Red Life + Strike => Strike reduces enemy healing (% reduction)
|
||||
Absorb
|
||||
Power (Gain X Power when you take damage)
|
||||
Life (Gain X Life when you take damage)
|
||||
Speed (Gain X Speed when you take damage)
|
||||
|
||||
# Heal #
|
||||
2 x Green Damage + Heal => Heal target for additional 20% of caster's maximum life
|
||||
2 x Green Speed + Heal => Heal target gets bonus speed
|
||||
2 x Green Life + Heal => Heal increases target's max hp for 2 turns
|
||||
Amplify
|
||||
Power
|
||||
Life
|
||||
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
ECONOMY.md
75
ECONOMY.md
@ -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
NODES.md
49
NODES.md
@ -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 #
|
||||
@ -34,8 +34,12 @@ Player Events e.g. chatwheel
|
||||
Matchmaking + ELO / Leaderboard
|
||||
Game skill private fields
|
||||
|
||||
# Phase 4 (Release -> Full Shill mode)
|
||||
|
||||
Refine artwork, icons, scaling etc
|
||||
Music
|
||||
Skill Specs
|
||||
Some sort viewable combat log
|
||||
|
||||
Marketing materials
|
||||
Videos
|
||||
|
||||
224
SPECS.md
224
SPECS.md
@ -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
|
||||
36
WORKLOG.md
36
WORKLOG.md
@ -3,7 +3,6 @@
|
||||
|
||||
_ntr_
|
||||
* can't reset password without knowing password =\
|
||||
* skip faceoff on server side
|
||||
* change cooldowns to delay & recharge
|
||||
- delay is cooldown before skill can first be used
|
||||
- recharge is cooldown after using skill
|
||||
@ -30,14 +29,11 @@ Hexagon Set
|
||||
- Increase intensity for each visit
|
||||
|
||||
_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)
|
||||
* 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_
|
||||
* Graphics
|
||||
@ -57,29 +53,19 @@ _tba_
|
||||
|
||||
## SOON
|
||||
|
||||
* combo rework
|
||||
- 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
|
||||
|
||||
* Skill / Spec hybrids - SEE COMBOS.md
|
||||
* elo + leaderboards
|
||||
|
||||
## LATER
|
||||
|
||||
* 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
|
||||
|
||||
* rework vecs into sets
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mnml-client",
|
||||
"version": "1.10.0",
|
||||
"version": "1.11.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -10,6 +10,7 @@ echo "Setting version to $VERSION"
|
||||
|
||||
echo $VERSION | tr -d '\n' > VERSION
|
||||
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/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"
|
||||
|
||||
2684
client/assets/mnml.awards.svg
Normal file
2684
client/assets/mnml.awards.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 106 KiB |
2568
client/assets/mnml.logo.text.svg
Normal file
2568
client/assets/mnml.logo.text.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 95 KiB |
@ -3,16 +3,25 @@
|
||||
|
||||
div {
|
||||
padding-right: 1em;
|
||||
// display: flex;
|
||||
// flex-flow: column;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
// text-transform: uppercase;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
height: 2.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 3em;
|
||||
height: 2.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
@white: #f5f5f5; // whitesmoke
|
||||
@purple: #9355b5; // 6lack - that far cover
|
||||
@yellow: #ffa100;
|
||||
@silver: #c0c0c0;
|
||||
@silver: #2c2c2c;
|
||||
|
||||
@black: black;
|
||||
@gray: #222;
|
||||
|
||||
@ -54,11 +54,7 @@
|
||||
|
||||
button {
|
||||
&.highlight {
|
||||
color: black;
|
||||
background: @silver;
|
||||
// border: 1px solid @white; (this bangs around the vbox)
|
||||
|
||||
// overwrite the classes on white svg elements
|
||||
svg {
|
||||
stroke-width: 0.75em;
|
||||
}
|
||||
|
||||
@ -75,25 +75,11 @@
|
||||
flex: 1;
|
||||
border-top: 0;
|
||||
border: 0.1em solid #222;
|
||||
&:not(:last-child) {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
.terms {
|
||||
display: inline;
|
||||
margin: 0 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
@ -108,62 +94,14 @@ section {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.list {
|
||||
letter-spacing: 0.25em;
|
||||
text-transform: uppercase;
|
||||
display: grid;
|
||||
// grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 1em;
|
||||
flex-flow: row wrap;
|
||||
align-items: flex-end;
|
||||
button {
|
||||
border-radius: 0.25em;
|
||||
// height: 3em;
|
||||
}
|
||||
|
||||
&.sub {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
&.play {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
align-items: flex-start;
|
||||
|
||||
&.rejoin {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
button.ready:enabled {
|
||||
color: forestgreen;
|
||||
border-color: forestgreen;
|
||||
|
||||
&:hover {
|
||||
background: forestgreen;
|
||||
color: black;
|
||||
border-color: forestgreen;
|
||||
}
|
||||
}
|
||||
|
||||
// // all green
|
||||
// button.ready:enabled {
|
||||
// background: forestgreen;
|
||||
// color: black;
|
||||
// border-color: forestgreen;
|
||||
|
||||
// &:hover {
|
||||
// color: forestgreen;
|
||||
// border-color: forestgreen;
|
||||
// background: 0;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
.panes {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-bottom: 2em;
|
||||
|
||||
figure {
|
||||
letter-spacing: 0.25em;
|
||||
@ -172,61 +110,92 @@ section {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
letter-spacing: 0.25em;
|
||||
text-transform: uppercase;
|
||||
display: grid;
|
||||
// grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 1em;
|
||||
flex-flow: row wrap;
|
||||
align-items: flex-end;
|
||||
|
||||
&.sub {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
&.play {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
align-items: flex-start;
|
||||
|
||||
&.rejoin {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
button.ready:enabled {
|
||||
color: forestgreen;
|
||||
border-color: forestgreen;
|
||||
|
||||
&:hover {
|
||||
background: forestgreen;
|
||||
color: black;
|
||||
border-color: forestgreen;
|
||||
}
|
||||
}
|
||||
|
||||
// // all green
|
||||
// button.ready:enabled {
|
||||
// background: forestgreen;
|
||||
// color: black;
|
||||
// border-color: forestgreen;
|
||||
|
||||
// &:hover {
|
||||
// color: forestgreen;
|
||||
// border-color: forestgreen;
|
||||
// background: 0;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
.demo {
|
||||
margin-top: 1em;
|
||||
|
||||
display: block;
|
||||
.login {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
.terms {
|
||||
display: inline;
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
button {
|
||||
pointer-events: none;
|
||||
padding: 0 0.5em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
grid-area: hdr;
|
||||
|
||||
display: flex;
|
||||
|
||||
.logo {
|
||||
flex: 0 1 10%;
|
||||
margin-right: 1em;
|
||||
border: none;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
div:first-child {
|
||||
padding-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.construct-section {
|
||||
.construct-list {
|
||||
height: 25em;
|
||||
grid-area: unset;
|
||||
|
||||
.instance-construct {
|
||||
// border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.colour-info {
|
||||
grid-area: vinfo;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
svg {
|
||||
flex: 1;
|
||||
height: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.game-demo {
|
||||
.game {
|
||||
height: 25em;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
.game-construct {
|
||||
flex: 1;
|
||||
}
|
||||
button {
|
||||
flex: 1;
|
||||
border-top: 0;
|
||||
border: 0.1em solid #222;
|
||||
&:last-child {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.intro {
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
@ -27,23 +27,6 @@ html body {
|
||||
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 {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
@ -108,11 +91,37 @@ dl {
|
||||
|
||||
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 {
|
||||
aside button {
|
||||
font-size: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
&.front-page {
|
||||
display: block;
|
||||
|
||||
main {
|
||||
padding: 0 25%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
@ -129,7 +138,7 @@ button, input {
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
flex: 1;
|
||||
border-radius: 0.5em;
|
||||
border-radius: 0;
|
||||
line-height: 2em;
|
||||
padding-right: 0.1em;
|
||||
padding-left: 0.1em;
|
||||
@ -150,9 +159,12 @@ button, input {
|
||||
|
||||
&:focus {
|
||||
/*colour necesary to bash skellington*/
|
||||
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
// &:active {
|
||||
// filter: url("#noiseFilter");
|
||||
// }
|
||||
}
|
||||
|
||||
a {
|
||||
@ -261,28 +273,12 @@ figure.gray {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header {
|
||||
.options {
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
button {
|
||||
height: 2em;
|
||||
// border-radius: 0.1em;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
button {
|
||||
&.highlight {
|
||||
color: @white;
|
||||
box-shadow: inset 0px 5px 0px 0px @white;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,11 +296,20 @@ li {
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 2em;
|
||||
background-image: url("../../assets/mnml.logo.trim.svg");
|
||||
height: 4em;
|
||||
filter: url("#noiseFilter");
|
||||
background-image: url("../../assets/mnml.logo.text.svg");
|
||||
background-size: contain;
|
||||
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 {
|
||||
@ -316,8 +321,13 @@ li {
|
||||
|
||||
.mnni {
|
||||
background-image: url("./../mnni.svg");
|
||||
filter: url("#noiseFilter");
|
||||
}
|
||||
|
||||
// .highlight {
|
||||
// filter: url("#noiseFilter");
|
||||
// }
|
||||
|
||||
.avatar {
|
||||
grid-area: avatar;
|
||||
object-fit: contain;
|
||||
@ -328,6 +338,10 @@ li {
|
||||
// pointer-events: none;
|
||||
}
|
||||
|
||||
header {
|
||||
// font-size: 1.2em;
|
||||
}
|
||||
|
||||
#clipboard {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
@ -359,4 +373,8 @@ li {
|
||||
}
|
||||
}
|
||||
|
||||
#noise {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
@import 'styles.mobile.less';
|
||||
|
||||
@ -7,6 +7,12 @@
|
||||
font-size: 8pt;
|
||||
padding: 0;
|
||||
|
||||
&.front-page {
|
||||
main {
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.instance {
|
||||
grid-template-areas:
|
||||
"vbox vbox"
|
||||
@ -164,12 +170,21 @@
|
||||
|
||||
|
||||
// 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 {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas:
|
||||
"main"
|
||||
"main";
|
||||
|
||||
&.front-page {
|
||||
display: block;
|
||||
|
||||
main {
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
section {
|
||||
@ -264,6 +279,9 @@
|
||||
}
|
||||
|
||||
.info-combiner {
|
||||
max-height: 7em;
|
||||
overflow-y: scroll;
|
||||
|
||||
.info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -147,16 +147,11 @@
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
color: black;
|
||||
background: @silver;
|
||||
// overwrite the classes on white svg elements
|
||||
svg {
|
||||
stroke-width: 0.75em;
|
||||
}
|
||||
|
||||
.white {
|
||||
stroke: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<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 {
|
||||
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><style>@font-face {
|
||||
font-family: octicons-anchor;
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<li>VBOX
|
||||
<ul>
|
||||
@ -794,7 +810,7 @@ pre {
|
||||
<li>Resizing of vbox when you buy / create certain items</li>
|
||||
</ul>
|
||||
<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>
|
||||
<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>
|
||||
@ -826,7 +842,7 @@ pre {
|
||||
<li>An issue where skills would not be put on cooldown after being used.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<li>
|
||||
<p>Game phase</p>
|
||||
@ -893,7 +909,7 @@ pre {
|
||||
</li>
|
||||
</ul>
|
||||
<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>
|
||||
<li>
|
||||
<p>Construct life changed</p>
|
||||
@ -979,7 +995,7 @@ pre {
|
||||
</li>
|
||||
</ul>
|
||||
<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>
|
||||
<li>
|
||||
<p>Vbox phase</p>
|
||||
@ -1125,7 +1141,7 @@ pre {
|
||||
<li>Player width styling</li>
|
||||
</ul>
|
||||
<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>
|
||||
<li>Improved wiggle animation</li>
|
||||
<li>Intercept is now considered defensive by bots</li>
|
||||
@ -1134,7 +1150,7 @@ pre {
|
||||
<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>
|
||||
<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>
|
||||
<li>Animations processing on client side reduced.</li>
|
||||
</ul>
|
||||
@ -1176,7 +1192,7 @@ pre {
|
||||
<li>Subscriber chat!</li>
|
||||
</ul>
|
||||
<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>
|
||||
<li>
|
||||
<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>
|
||||
<p>We've updated the UI during the vbox / buy phase to give a better indication of valid actions.</p>
|
||||
<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>
|
||||
<li>
|
||||
<p>Borders for skill combo's represent the base colours.</p>
|
||||
@ -1234,7 +1250,7 @@ pre {
|
||||
<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>
|
||||
<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>
|
||||
<li>
|
||||
<p>Purge</p>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mnml-client",
|
||||
"version": "1.10.0",
|
||||
"version": "1.11.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
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 setAnimCb = value => ({ type: 'SET_ANIM_CB', 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 setAnimTarget = value => ({ type: 'SET_ANIM_TARGET', value });
|
||||
export const setAnimText = value => ({ type: 'SET_ANIM_TEXT', value });
|
||||
|
||||
export const setDemo = value => ({ type: 'SET_DEMO', value });
|
||||
export const setResolution = value => ({ type: 'SET_RESOLUTION', value });
|
||||
|
||||
export const setChatShow = value => ({ type: 'SET_CHAT_SHOW', value });
|
||||
export const setChatWheel = value => ({ type: 'SET_CHAT_WHEEL', value });
|
||||
|
||||
@ -4,9 +4,7 @@ const toast = require('izitoast');
|
||||
const eachSeries = require('async/eachSeries');
|
||||
|
||||
const actions = require('./actions');
|
||||
const { TIMES } = require('./constants');
|
||||
const animations = require('./animations.utils');
|
||||
const { removeTier } = require('./utils');
|
||||
const { setAnimations, clearAnimations } = require('./animations.utils');
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
function sendDevResolve(a, b, skill) {
|
||||
send(['DevResolve', { a, b, skill }]);
|
||||
function sendDevResolve(skill) {
|
||||
send(['DevResolve', { skill }]);
|
||||
}
|
||||
|
||||
function onDevResolutions(newRes) {
|
||||
const { game, account, animating } = store.getState();
|
||||
|
||||
if (animating) return false;
|
||||
function setGame(game) {
|
||||
store.dispatch(actions.setGame(game));
|
||||
store.dispatch(actions.setAnimating(true));
|
||||
store.dispatch(actions.setGameSkillInfo(null));
|
||||
// stop fetching the game state til animations are done
|
||||
|
||||
const newRes = game.resolutions[game.resolutions.length - 1];
|
||||
return eachSeries(newRes, (r, cb) => {
|
||||
if (!r.event) return cb();
|
||||
const timeout = animations.getTime(r.stages);
|
||||
const anims = animations.getObjects(r, game, account);
|
||||
const text = animations.getText(r);
|
||||
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
|
||||
if (anims.animSkill) store.dispatch(actions.setAnimSkill(anims.animSkill));
|
||||
|
||||
if (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);
|
||||
// if (r.delay === 0) return cb(); // TargetKo etc
|
||||
setAnimations(r, store);
|
||||
return setTimeout(cb, r.delay);
|
||||
}, err => {
|
||||
if (err) return console.error(err);
|
||||
// clear animation state
|
||||
store.dispatch(actions.setAnimSkill(null));
|
||||
store.dispatch(actions.setAnimSource(null));
|
||||
store.dispatch(actions.setAnimTarget(null));
|
||||
store.dispatch(actions.setAnimText(null));
|
||||
store.dispatch(actions.setAnimating(false));
|
||||
store.dispatch(actions.setGameEffectInfo(null));
|
||||
|
||||
clearAnimations(store);
|
||||
// set the game state so resolutions don't fire twice
|
||||
store.dispatch(actions.setGame(game));
|
||||
// ws.sendGameState(game.id);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
const handlers = {
|
||||
DevResolutions: onDevResolutions,
|
||||
GameState: setGame,
|
||||
};
|
||||
|
||||
// decodes the cbor and
|
||||
|
||||
@ -5,18 +5,7 @@ const { createStore, combineReducers } = require('redux');
|
||||
const reducers = require('./reducers');
|
||||
const actions = require('./actions');
|
||||
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 testGameBuilder = require('./test.game');
|
||||
|
||||
const testGame = testGameBuilder('8552e0bf-340d-4fc8-b6fc-cccccccccccc');
|
||||
const testAccount = {
|
||||
id: '8552e0bf-340d-4fc8-b6fc-cccccccccccc',
|
||||
name: 'ntr',
|
||||
};
|
||||
|
||||
// Redux Store
|
||||
const store = createStore(
|
||||
@ -24,20 +13,15 @@ const store = createStore(
|
||||
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.setGame(testGame));
|
||||
|
||||
function animationsNav(ws) {
|
||||
function useSkill(skill) {
|
||||
const ateam = Math.round(Math.random());
|
||||
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 ws.sendDevResolve(skill);
|
||||
}
|
||||
|
||||
return SKILLS.map((s, i) => (
|
||||
|
||||
@ -1,207 +1,49 @@
|
||||
const actions = require('./actions');
|
||||
const { TIMES } = require('./constants');
|
||||
const { removeTier } = require('./utils');
|
||||
|
||||
function none() {
|
||||
return {
|
||||
animSource: null,
|
||||
animTarget: null,
|
||||
};
|
||||
}
|
||||
function setAnimations(r, store) {
|
||||
const { focus, event: [type, variant] } = r;
|
||||
|
||||
function getObjects(resolution, game, account) {
|
||||
if (!resolution) return none();
|
||||
if (!resolution.target) return none();
|
||||
if (type === 'HitAoe') {
|
||||
const { construct } = variant;
|
||||
const aoeFocus = focus.concat(construct);
|
||||
|
||||
const [type, event] = resolution.event;
|
||||
if (!event || !event.skill) return none();
|
||||
store.dispatch(actions.setResolution(null));
|
||||
store.dispatch(actions.setAnimFocus(aoeFocus));
|
||||
store.dispatch(actions.setAnimTarget(r));
|
||||
|
||||
const playerTeam = game.players.find(t => t.id === account.id);
|
||||
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) {
|
||||
let time = 0;
|
||||
if (stages.includes('START_SKILL') && stages.includes('END_SKILL')) {
|
||||
time += TIMES.SOURCE_AND_TARGET_TOTAL_DURATION;
|
||||
} 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) {
|
||||
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) {
|
||||
const nullText = { text: null, constructId: null, life: null };
|
||||
if (!resolution) return nullText;
|
||||
if (!resolution.stages.includes('POST_SKILL')) return nullText;
|
||||
|
||||
function generatePostSkill() {
|
||||
const [type, event] = resolution.event;
|
||||
if (type === 'Ko') {
|
||||
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;
|
||||
return setTimeout(() => store.dispatch(actions.setAnimSource(null)), TIMES.SOURCE_DURATION_MS);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
store.dispatch(actions.setAnimFocus(focus));
|
||||
|
||||
if (type === 'Cast') {
|
||||
store.dispatch(actions.setResolution(null));
|
||||
store.dispatch(actions.setAnimSource(r));
|
||||
return setTimeout(() => store.dispatch(actions.setAnimSource(null)), TIMES.SOURCE_DURATION_MS);
|
||||
}
|
||||
|
||||
if (type === 'Hit') {
|
||||
store.dispatch(actions.setResolution(null));
|
||||
store.dispatch(actions.setAnimTarget(r));
|
||||
return setTimeout(() => store.dispatch(actions.setAnimTarget(null)), TIMES.TARGET_DURATION_MS);
|
||||
}
|
||||
|
||||
return store.dispatch(actions.setResolution(r));
|
||||
}
|
||||
|
||||
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));
|
||||
function clearAnimations(store) {
|
||||
store.dispatch(actions.setAnimSkill(null));
|
||||
store.dispatch(actions.setAnimSource(null));
|
||||
store.dispatch(actions.setAnimTarget(null));
|
||||
store.dispatch(actions.setResolution(null));
|
||||
store.dispatch(actions.setAnimating(false));
|
||||
store.dispatch(actions.setGameEffectInfo(null));
|
||||
store.dispatch(actions.setAnimFocus(null));
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
getFocusTargets,
|
||||
getObjects,
|
||||
getTime,
|
||||
getText,
|
||||
isCbAnim,
|
||||
setAnimations,
|
||||
clearAnimations,
|
||||
};
|
||||
|
||||
@ -33,6 +33,10 @@ const ws = createSocket(events);
|
||||
ws.connect();
|
||||
events.setWs(ws);
|
||||
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
LogRocket.init('yh0dy3/mnml');
|
||||
}
|
||||
|
||||
const App = () => (
|
||||
<Provider store={store}>
|
||||
{window.Stripe
|
||||
|
||||
@ -152,11 +152,9 @@ class AccountStatus extends Component {
|
||||
|
||||
return (
|
||||
<section class='account top' onClick={tlClick}>
|
||||
{subInfo()}
|
||||
<div>
|
||||
{subInfo()}
|
||||
</div>
|
||||
<div>
|
||||
<label for="email">Email Settings:</label>
|
||||
<h3>Email</h3>
|
||||
<dl>
|
||||
<dt>Recovery Email</dt>
|
||||
<dd>{email ? email.email : 'No email set'}</dd>
|
||||
@ -174,6 +172,7 @@ class AccountStatus extends Component {
|
||||
<button onClick={() => sendSetEmail(emailState)}>Update</button>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Password</h3>
|
||||
<label for="current">Password:</label>
|
||||
<input
|
||||
class="login-input"
|
||||
@ -208,6 +207,7 @@ class AccountStatus extends Component {
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Other</h3>
|
||||
<figure>
|
||||
<figcaption>spawn new construct</figcaption>
|
||||
<button onClick={() => sendConstructSpawn()} type="submit">
|
||||
|
||||
@ -5,6 +5,7 @@ const { connect } = require('preact-redux');
|
||||
const Amplify = require('./anims/amplify');
|
||||
const Attack = require('./anims/attack');
|
||||
const Absorb = require('./anims/absorb');
|
||||
const Absorption = require('./anims/absorption');
|
||||
const Bash = require('./anims/bash');
|
||||
const Blast = require('./anims/blast');
|
||||
const Block = require('./anims/block');
|
||||
@ -44,8 +45,8 @@ const { removeTier } = require('../utils');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animTarget } = state;
|
||||
return { animTarget };
|
||||
const { animTarget, account } = state;
|
||||
return { animTarget, account };
|
||||
}
|
||||
);
|
||||
|
||||
@ -60,20 +61,21 @@ class ConstructAnimation extends Component {
|
||||
const {
|
||||
animTarget,
|
||||
construct,
|
||||
account,
|
||||
} = props;
|
||||
|
||||
if (!animTarget) return false;
|
||||
|
||||
const {
|
||||
skill,
|
||||
player,
|
||||
direction,
|
||||
constructId,
|
||||
} = animTarget;
|
||||
const { skill, event: [, variant] } = animTarget;
|
||||
const { construct: constructId, player, direction: [x, y] } = variant;
|
||||
|
||||
const animY = y && player === account.id ? -1 : y;
|
||||
const isPlayer = player === account.id;
|
||||
const direction = { x, y: animY };
|
||||
|
||||
const animSkill = removeTier(skill);
|
||||
if (!constructId.includes(construct.id)) return false;
|
||||
|
||||
|
||||
// find target animation
|
||||
const chooseAnim = () => {
|
||||
switch (animSkill) {
|
||||
@ -93,9 +95,9 @@ class ConstructAnimation extends Component {
|
||||
case 'Haste': return <Haste />;
|
||||
case 'Triage': return <Triage />;
|
||||
case 'TriageTick': return <TriageTick />;
|
||||
case 'Link': return <Link player={player} />;
|
||||
case 'Link': return <Link player={isPlayer} />;
|
||||
case 'Hybrid': return <Hybrid />;
|
||||
case 'Intercept': return <Intercept player={player} />;
|
||||
case 'Intercept': return <Intercept player={isPlayer} />;
|
||||
|
||||
// Debuff base
|
||||
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 'Bash': return <Bash />;
|
||||
case 'Absorb': return <Absorb />;
|
||||
case 'Absorption': return <Absorption />;
|
||||
case 'Sleep': return <Sleep />;
|
||||
case 'Break': return <Break />;
|
||||
case 'Ruin': return <Ruin />;
|
||||
|
||||
// Block Base
|
||||
case 'Block': return <Block />;
|
||||
case 'Sustain': return <Sustain player={player} />;
|
||||
case 'Sustain': return <Sustain player={isPlayer} />;
|
||||
case 'Electrify': return <Electrify />;
|
||||
case 'Electrocute': 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 'Purify': return <Purify player={player} />;
|
||||
case 'Recharge': return <Recharge player={player} />;
|
||||
case 'Reflect': return <Refl player={player} />;
|
||||
case 'Purify': return <Purify player={isPlayer} />;
|
||||
case 'Recharge': return <Recharge player={isPlayer} />;
|
||||
case 'Reflect': return <Refl player={isPlayer} />;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@ -1,21 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const anime = require('animejs').default;
|
||||
|
||||
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 {
|
||||
constructor() {
|
||||
super();
|
||||
@ -56,7 +44,7 @@ class Absorb extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#absorb'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -66,8 +54,6 @@ class Absorb extends Component {
|
||||
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',
|
||||
easing: 'easeOutExpo',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -76,8 +62,6 @@ class Absorb extends Component {
|
||||
strokeWidth: [2, 1],
|
||||
easing: 'easeInOutSine',
|
||||
direction: 'alternate',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -90,8 +74,7 @@ class Absorb extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Absorb);
|
||||
module.exports = Absorb;
|
||||
|
||||
80
client/src/components/anims/absorption.jsx
Normal file
80
client/src/components/anims/absorption.jsx
Normal file
@ -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;
|
||||
@ -5,13 +5,6 @@ const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Amplify extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -43,7 +36,7 @@ class Amplify extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#amplify'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -54,8 +47,6 @@ class Amplify extends Component {
|
||||
d: [{ value: altPath }],
|
||||
stroke: ['#3050f8', '#a52a2a'],
|
||||
easing: 'easeInOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -64,8 +55,6 @@ class Amplify extends Component {
|
||||
baseFrequency: 0.15,
|
||||
scale: 4,
|
||||
easing: 'easeInOutExpo',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -78,8 +67,7 @@ class Amplify extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Amplify);
|
||||
module.exports = Amplify;
|
||||
|
||||
@ -5,13 +5,6 @@ const anime = require('animejs').default;
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Attack extends Component {
|
||||
constructor(props) {
|
||||
super();
|
||||
@ -46,8 +39,8 @@ class Attack extends Component {
|
||||
y: [400, 200],
|
||||
height: [100, 10, 0],
|
||||
width: [12, 5, 0],
|
||||
delay: () => anime.random(TIMES.TARGET_DELAY_MS, TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS / 2),
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
delay: () => anime.random(0, TIMES.TARGET_DURATION_MS / 4),
|
||||
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--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = addState(Attack);
|
||||
module.exports = Attack;
|
||||
|
||||
@ -8,7 +8,6 @@ function Banish(id, idle) {
|
||||
scaleY: 0,
|
||||
fill: '#fff',
|
||||
easing: 'easeOutElastic',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.45,
|
||||
direction: 'alternate',
|
||||
begin: idle.pause,
|
||||
|
||||
@ -5,13 +5,6 @@ const anime = require('animejs').default;
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Bash extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -54,7 +47,7 @@ class Bash extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#bash'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -64,7 +57,6 @@ class Bash extends Component {
|
||||
targets: ['#bash'],
|
||||
scale: {
|
||||
value: 1,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.2,
|
||||
easing: 'easeInExpo',
|
||||
},
|
||||
@ -72,10 +64,9 @@ class Bash extends Component {
|
||||
value: 180,
|
||||
easing: 'linear',
|
||||
loop: true,
|
||||
delay: TIMES.TARGET_DELAY_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,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -86,8 +77,6 @@ class Bash extends Component {
|
||||
scale: 10,
|
||||
numOctaves: 5,
|
||||
easing: 'easeOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -100,8 +89,7 @@ class Bash extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Bash);
|
||||
module.exports = Bash;
|
||||
|
||||
@ -1,18 +1,10 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const anime = require('animejs').default;
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Bash extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -47,7 +39,7 @@ class Bash extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#bash'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -57,7 +49,6 @@ class Bash extends Component {
|
||||
targets: ['#bash'],
|
||||
scale: {
|
||||
value: 1,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.2,
|
||||
easing: 'easeInExpo',
|
||||
},
|
||||
@ -74,7 +65,7 @@ class Bash extends Component {
|
||||
{ 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,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -90,7 +81,6 @@ class Bash extends Component {
|
||||
],
|
||||
easing: 'easeOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -103,8 +93,7 @@ class Bash extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Bash);
|
||||
module.exports = Bash;
|
||||
|
||||
@ -1,34 +1,12 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
const anime = require('animejs').default;
|
||||
const times = require('lodash/times');
|
||||
|
||||
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 {
|
||||
constructor(props) {
|
||||
constructor() {
|
||||
super();
|
||||
this.animations = [];
|
||||
}
|
||||
@ -62,7 +40,7 @@ class Blast extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#blast'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -75,7 +53,6 @@ class Blast extends Component {
|
||||
`,
|
||||
style: { rotate: anime.random(-180, 180) },
|
||||
easing: 'easeOutCubic',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -88,8 +65,7 @@ class Blast extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Blast);
|
||||
module.exports = Blast;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
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();
|
||||
@ -38,7 +30,7 @@ class Block extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#block'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -53,8 +45,7 @@ class Block extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Block);
|
||||
module.exports = Block;
|
||||
|
||||
@ -1,20 +1,12 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
|
||||
// logarithmic spiral lifted from
|
||||
// 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 {
|
||||
constructor() {
|
||||
super();
|
||||
@ -63,7 +55,7 @@ class Break extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#break'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -74,7 +66,6 @@ class Break extends Component {
|
||||
rotate: 180,
|
||||
easing: 'linear',
|
||||
loop: true,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -82,7 +73,6 @@ class Break extends Component {
|
||||
targets: ['#break circle'],
|
||||
easing: 'easeInSine',
|
||||
r: 300,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -93,10 +83,8 @@ class Break extends Component {
|
||||
numOctaves: 5,
|
||||
easing: 'easeOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
// this is necessary because
|
||||
@ -107,8 +95,7 @@ class Break extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Break);
|
||||
module.exports = Break;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
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 Buff extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -39,7 +31,7 @@ class Buff extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#buff'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -50,7 +42,6 @@ class Buff extends Component {
|
||||
points: '0,190 100,10 190,190',
|
||||
|
||||
easing: 'easeOutExpo',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -59,7 +50,6 @@ class Buff extends Component {
|
||||
points: '40,170 100,50 160,170',
|
||||
|
||||
easing: 'easeOutExpo',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -68,7 +58,6 @@ class Buff extends Component {
|
||||
points: '70,150 100,90 130,150',
|
||||
|
||||
easing: 'easeOutExpo',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -81,8 +70,7 @@ class Buff extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Buff);
|
||||
module.exports = Buff;
|
||||
|
||||
@ -1,26 +1,19 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
const { randomPoints } = require('../../utils');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
function projectile(x, y, radius, colour) {
|
||||
return (
|
||||
<circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
cx={anime.random(0, 400)}
|
||||
cy={anime.random(0, 400)}
|
||||
r={radius}
|
||||
fill={colour}
|
||||
stroke="none"
|
||||
stroke={colour === '#a52a2a' ? 'none' : '#f5f5f5'}
|
||||
stroke-width={colour === '#a52a2a' ? '0' : '0.05em'}
|
||||
filter={colour === '#a52a2a' ? 'url(#chaosRedFilter)' : 'url(#chaosBlueFilter)'}
|
||||
/>
|
||||
);
|
||||
@ -30,10 +23,10 @@ class Chaos extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
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 => {
|
||||
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() {
|
||||
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({
|
||||
targets: ['#chaosRedFilter feTurbulence', '#chaosRedFilter feDisplacementMap'],
|
||||
baseFrequency: 2,
|
||||
scale: 20,
|
||||
numOctaves: 5,
|
||||
scale: 5,
|
||||
numOctaves: 3,
|
||||
easing: 'easeOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -105,13 +71,9 @@ class Chaos extends Component {
|
||||
targets: proj,
|
||||
cx: 150 + (Math.random() * 50 * (Math.random() < 0.5 ? -1 : 1)),
|
||||
cy: 200 + (Math.random() * 50 * (Math.random() < 0.5 ? -1 : 1)),
|
||||
// cx: 150,
|
||||
// cy: 200,
|
||||
// opacity: 0,
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: (TIMES.TARGET_DURATION_MS * 2 / 3),
|
||||
easing: 'easeInQuad',
|
||||
duration: anime.random(TIMES.TARGET_DURATION_MS * 2 / 3, TIMES.TARGET_DURATION_MS),
|
||||
opacity: 0,
|
||||
easing: 'easeInExpo',
|
||||
})));
|
||||
}
|
||||
|
||||
@ -119,8 +81,7 @@ class Chaos extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Chaos);
|
||||
module.exports = Chaos;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
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 Counter extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -51,7 +43,7 @@ class Counter extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#counter'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -60,7 +52,7 @@ class Counter extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#counter'],
|
||||
rotateX: 180,
|
||||
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS / 3,
|
||||
delay: TIMES.TARGET_DURATION_MS / 3,
|
||||
duration: TIMES.TARGET_DURATION_MS / 2,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -71,8 +63,6 @@ class Counter extends Component {
|
||||
scale: 10,
|
||||
numOctaves: 5,
|
||||
easing: 'easeOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -85,8 +75,7 @@ class Counter extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Counter);
|
||||
module.exports = Counter;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
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 Curse extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -54,7 +46,7 @@ class Curse extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#curse'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -64,7 +56,6 @@ class Curse extends Component {
|
||||
targets: ['#curseCircleOne', '#curseFilterOne'],
|
||||
r: 30,
|
||||
easing: 'easeInOutSine',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -72,7 +63,6 @@ class Curse extends Component {
|
||||
targets: ['#curseCircleTwo', '#curseFilterTwo'],
|
||||
r: 60,
|
||||
easing: 'easeInOutSine',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -80,7 +70,6 @@ class Curse extends Component {
|
||||
targets: ['#curseCircleThree', '#curseFilterThree'],
|
||||
r: 90,
|
||||
easing: 'easeInOutSine',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -93,8 +82,7 @@ class Curse extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Curse);
|
||||
module.exports = Curse;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
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 Debuff extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -41,7 +33,7 @@ class Debuff extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#debuff'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -52,7 +44,6 @@ class Debuff extends Component {
|
||||
points: '0,190 100,10 190,190',
|
||||
|
||||
easing: 'easeOutExpo',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -61,7 +52,6 @@ class Debuff extends Component {
|
||||
points: '40,170 100,50 160,170',
|
||||
|
||||
easing: 'easeOutExpo',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -70,7 +60,6 @@ class Debuff extends Component {
|
||||
points: '70,150 100,90 130,150',
|
||||
|
||||
easing: 'easeOutExpo',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -83,8 +72,7 @@ class Debuff extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Debuff);
|
||||
module.exports = Debuff;
|
||||
|
||||
@ -2,17 +2,9 @@ const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const times = require('lodash/times');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Decay extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -40,7 +32,7 @@ class Decay extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#decay'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -55,7 +47,6 @@ class Decay extends Component {
|
||||
rotate(${anime.random(-15, 15)})
|
||||
`,
|
||||
easing: 'easeOutSine',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -68,8 +59,7 @@ class Decay extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Decay);
|
||||
module.exports = Decay;
|
||||
|
||||
@ -1,19 +1,10 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const times = require('lodash/times');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const anime = require('animejs').default;
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Electrify extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -57,7 +48,7 @@ class Electrify extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#electrify'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -109,8 +100,7 @@ class Electrify extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Electrify);
|
||||
module.exports = Electrify;
|
||||
|
||||
@ -1,19 +1,11 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const times = require('lodash/times')
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const anime = require('animejs').default;
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Electrocute extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -55,7 +47,7 @@ class Electrocute extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#electrify'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -101,8 +93,7 @@ class Electrocute extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Electrocute);
|
||||
module.exports = Electrocute;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Haste extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -49,7 +41,7 @@ class Haste extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#haste'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -59,7 +51,6 @@ class Haste extends Component {
|
||||
targets: ['#haste g'],
|
||||
stroke: [COLOURS.GREEN, COLOURS.RED],
|
||||
easing: 'easeInOutSine',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.75,
|
||||
}));
|
||||
|
||||
@ -69,8 +60,6 @@ class Haste extends Component {
|
||||
scale: 10,
|
||||
numOctaves: 5,
|
||||
easing: 'easeInOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -83,8 +72,7 @@ class Haste extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Haste);
|
||||
module.exports = Haste;
|
||||
|
||||
@ -1,18 +1,10 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
const { randomPoints } = require('../../utils');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
function projectile(x, y, radius, colour) {
|
||||
return (
|
||||
<circle
|
||||
@ -60,7 +52,7 @@ class Heal extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#heal'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -70,8 +62,7 @@ class Heal extends Component {
|
||||
targets: ['#heal circle'],
|
||||
cx: 150,
|
||||
cy: 200,
|
||||
delay: TIMES.TARGET_DELAY_MS * 4,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.9,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.6,
|
||||
easing: 'easeOutCirc',
|
||||
direction: 'reverse',
|
||||
}));
|
||||
@ -81,8 +72,7 @@ class Heal extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Heal);
|
||||
module.exports = Heal;
|
||||
|
||||
@ -1,16 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
const anime = require('animejs').default;
|
||||
|
||||
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
|
||||
|
||||
@ -54,7 +47,7 @@ class Hex extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#hex'],
|
||||
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 },
|
||||
],
|
||||
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',
|
||||
easing: 'easeOutExpo',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -75,7 +67,6 @@ class Hex extends Component {
|
||||
easing: 'easeInOutSine',
|
||||
direction: 'alternate',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -88,8 +79,7 @@ class Hex extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Hex);
|
||||
module.exports = Hex;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Hybrid extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -94,12 +86,11 @@ class Hybrid extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#hybrid'],
|
||||
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 },
|
||||
],
|
||||
transform: {
|
||||
value: ['rotate(0)', 'rotate(360)'],
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
direction: 'alternate',
|
||||
},
|
||||
@ -110,7 +101,6 @@ class Hybrid extends Component {
|
||||
r: [10, anime.random(10, 30)],
|
||||
targets: ['#hybrid circle.green-one'],
|
||||
cx: [50, 250, 50, 250],
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
@ -120,7 +110,6 @@ class Hybrid extends Component {
|
||||
r: [10, anime.random(10, 30)],
|
||||
targets: ['#hybrid circle.green-two'],
|
||||
cy: [250, 50, 250, 50],
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
@ -131,7 +120,6 @@ class Hybrid extends Component {
|
||||
targets: ['#hybrid circle.bluewhite-one'],
|
||||
fill: [COLOURS.WHITE, COLOURS.BLUE],
|
||||
cy: [50, 250, 50, 250],
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
@ -142,7 +130,6 @@ class Hybrid extends Component {
|
||||
targets: ['#hybrid circle.bluewhite-two'],
|
||||
cx: [250, 50, 250, 50],
|
||||
fill: [COLOURS.WHITE, COLOURS.BLUE],
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInOutSine',
|
||||
loop: true,
|
||||
@ -153,8 +140,7 @@ class Hybrid extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Hybrid);
|
||||
module.exports = Hybrid;
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const anime = require('animejs').default;
|
||||
|
||||
const {
|
||||
@ -9,13 +7,6 @@ const {
|
||||
COLOURS,
|
||||
} = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Intercept extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -47,7 +38,7 @@ class Intercept extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#intercept'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -61,7 +52,6 @@ class Intercept extends Component {
|
||||
],
|
||||
strokeWidth: 0,
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInCubic',
|
||||
// direction: 'reverse',
|
||||
@ -70,7 +60,6 @@ class Intercept extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#intercept rect'],
|
||||
y: 96,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInCubic',
|
||||
// direction: 'reverse',
|
||||
@ -82,8 +71,6 @@ class Intercept extends Component {
|
||||
scale: 10,
|
||||
numOctaves: 5,
|
||||
easing: 'easeOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -96,8 +83,7 @@ class Intercept extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Intercept);
|
||||
module.exports = Intercept;
|
||||
|
||||
@ -6,7 +6,6 @@ function Invert(id, idle) {
|
||||
return anime({
|
||||
targets: [document.getElementById(id)],
|
||||
rotate: 180,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.45,
|
||||
easing: 'easeInOutElastic',
|
||||
direction: 'alternate',
|
||||
|
||||
@ -1,18 +1,10 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const anime = require('animejs').default;
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Link extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -68,7 +60,7 @@ class Link extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#link'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -78,7 +70,6 @@ class Link extends Component {
|
||||
targets: ['#link path'],
|
||||
strokeDashoffset: [anime.setDashoffset, 0],
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.8,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
easing: 'easeInOutSine',
|
||||
});
|
||||
}
|
||||
@ -91,8 +82,7 @@ class Link extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Link);
|
||||
module.exports = Link;
|
||||
|
||||
@ -1,17 +1,8 @@
|
||||
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 Purge extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -51,7 +42,7 @@ class Purge extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#purge'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -60,7 +51,6 @@ class Purge extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#purge g'],
|
||||
strokeWidth: [15, 0],
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInSine',
|
||||
}));
|
||||
@ -81,8 +71,7 @@ class Purge extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Purge);
|
||||
module.exports = Purge;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
function projectile(x, y, radius, colour) {
|
||||
return (
|
||||
<circle
|
||||
@ -80,7 +72,7 @@ class Purify extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#purify'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -89,7 +81,7 @@ class Purify extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#block'],
|
||||
opacity: [
|
||||
{ value: 0, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS },
|
||||
{ value: 0, duration: TIMES.TARGET_DURATION_MS },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
}));
|
||||
@ -102,7 +94,6 @@ class Purify extends Component {
|
||||
easing: 'easeInOutSine',
|
||||
cx: 128,
|
||||
cy: 24,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -115,8 +106,7 @@ class Purify extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Purify);
|
||||
module.exports = Purify;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
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 Recharge extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -59,7 +51,7 @@ class Recharge extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#recharge'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -71,7 +63,6 @@ class Recharge extends Component {
|
||||
easing: 'easeInOutSine',
|
||||
direction: 'alternate',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -82,7 +73,6 @@ class Recharge extends Component {
|
||||
numOctaves: 5,
|
||||
|
||||
easing: 'easeOutSine',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -95,8 +85,7 @@ class Recharge extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Recharge);
|
||||
module.exports = Recharge;
|
||||
|
||||
@ -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);
|
||||
@ -1,17 +1,9 @@
|
||||
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 Refl extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -54,7 +46,7 @@ class Refl extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#reflect'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -77,8 +69,7 @@ class Refl extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Refl);
|
||||
module.exports = Refl;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
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 Restrict extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -42,7 +34,7 @@ class Restrict extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#restrict'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -52,12 +44,11 @@ class Restrict extends Component {
|
||||
targets: ['#restrict'],
|
||||
scale: {
|
||||
value: 1,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.2,
|
||||
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,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -69,7 +60,6 @@ class Restrict extends Component {
|
||||
numOctaves: 5,
|
||||
easing: 'easeOutSine',
|
||||
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -78,7 +68,7 @@ class Restrict extends Component {
|
||||
|
||||
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,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -88,7 +78,7 @@ class Restrict extends Component {
|
||||
|
||||
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,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -102,8 +92,7 @@ class Restrict extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Restrict);
|
||||
module.exports = Restrict;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
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 Ruin extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -75,7 +67,7 @@ class Ruin extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#ruin'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -86,7 +78,6 @@ class Ruin extends Component {
|
||||
rotate: 180,
|
||||
easing: 'linear',
|
||||
loop: true,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -99,12 +90,7 @@ class Ruin extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
try {
|
||||
this.props.animCb && this.props.animCb();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Ruin);
|
||||
module.exports = Ruin;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
const anime = require('animejs').default;
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Silence extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -47,7 +39,7 @@ class Silence extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#silence'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -58,12 +50,11 @@ class Silence extends Component {
|
||||
rotate: [90, 90],
|
||||
scale: {
|
||||
value: 1,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.2,
|
||||
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,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -73,7 +64,7 @@ class Silence extends Component {
|
||||
|
||||
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,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -83,7 +74,7 @@ class Silence extends Component {
|
||||
|
||||
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,
|
||||
easing: 'easeOutSine',
|
||||
}));
|
||||
@ -97,8 +88,7 @@ class Silence extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Silence);
|
||||
module.exports = Silence;
|
||||
|
||||
@ -1,19 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
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 {
|
||||
constructor() {
|
||||
super();
|
||||
@ -43,7 +33,7 @@ class Siphon extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: '.skill-anim',
|
||||
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 },
|
||||
],
|
||||
}));
|
||||
@ -51,8 +41,7 @@ class Siphon extends Component {
|
||||
anime({
|
||||
targets: '#siphon',
|
||||
r: 0,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInSine',
|
||||
});
|
||||
}
|
||||
@ -61,8 +50,7 @@ class Siphon extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Siphon);
|
||||
module.exports = Siphon;
|
||||
|
||||
@ -1,18 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
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) {
|
||||
return (
|
||||
@ -82,7 +73,7 @@ class SiphonTick extends Component {
|
||||
anime({
|
||||
targets: '#siphon',
|
||||
r: 600,
|
||||
duration: duration * 2 / 3,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeInSine',
|
||||
});
|
||||
|
||||
@ -92,8 +83,8 @@ class SiphonTick extends Component {
|
||||
targets: proj,
|
||||
cx: 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),
|
||||
duration,
|
||||
delay: (Math.random() * TIMES.TARGET_DURATION_MS * 1 / 2),
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
easing: 'easeOutQuad',
|
||||
});
|
||||
});
|
||||
@ -103,8 +94,7 @@ class SiphonTick extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(SiphonTick);
|
||||
module.exports = SiphonTick;
|
||||
|
||||
@ -1,42 +1,17 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
const times = require('lodash/times');
|
||||
|
||||
const { TIMES } = 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}
|
||||
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>
|
||||
);
|
||||
}
|
||||
const GREEN = '#1FF01F';
|
||||
const RED = '#a52a2a';
|
||||
|
||||
class Slay extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.animations = [];
|
||||
this.colour = '#a52a2a';
|
||||
const points = new Array(30).fill(0);
|
||||
this.charges = points.map(() => projectile(150, 420, 7, '#1FF01F'));
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -47,13 +22,16 @@ class Slay extends Component {
|
||||
id="slay"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 300 300">
|
||||
<filter id="slayFilter">
|
||||
<feGaussianBlur stdDeviation="4"/>
|
||||
<feTurbulence type="turbulence" baseFrequency="0.001" numOctaves="3" result="turbulence"/>
|
||||
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
|
||||
</filter>
|
||||
{sword(this.colour)}
|
||||
{this.charges}
|
||||
{times(10, () => (
|
||||
<ellipse
|
||||
cx={anime.random(100, 200)}
|
||||
cy={anime.random(-60, -30)}
|
||||
stroke="none"
|
||||
rx={anime.random(5, 10)}
|
||||
ry={10}
|
||||
fill={RED}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@ -73,73 +51,33 @@ class Slay extends Component {
|
||||
|
||||
anime.set('#slay', {
|
||||
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,
|
||||
});
|
||||
|
||||
this.animations.push(anime({
|
||||
targets: '#slay',
|
||||
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',
|
||||
}));
|
||||
anime.set('#slay ellipse',{
|
||||
fill: RED,
|
||||
})
|
||||
|
||||
this.animations.push(anime({
|
||||
targets: ['#slayFilter feTurbulence', '#slayFilter feDisplacementMap'],
|
||||
baseFrequency: 10,
|
||||
scale: 100,
|
||||
delay: (TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.5),
|
||||
duration: (TIMES.TARGET_DURATION_MS * 0.5),
|
||||
easing: 'easeInQuad',
|
||||
targets: ['#slay ellipse'],
|
||||
cx: 150,
|
||||
cy: 325,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.2,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.4,
|
||||
easing: 'easeOutQuad',
|
||||
direction: 'alternate',
|
||||
}));
|
||||
|
||||
this.animations.push(anime({
|
||||
targets: '#sword',
|
||||
opacity: 0,
|
||||
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',
|
||||
}));
|
||||
});
|
||||
setTimeout(() => anime.set('#slay ellipse',{
|
||||
fill: GREEN,
|
||||
}), TIMES.TARGET_DURATION_MS * 0.5);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Slay);
|
||||
module.exports = Slay;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
const { randomPoints } = require('../../utils');
|
||||
@ -9,13 +8,6 @@ const { randomPoints } = require('../../utils');
|
||||
// logarithmic spiral lifted from
|
||||
// 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) {
|
||||
return (
|
||||
<circle
|
||||
@ -81,7 +73,7 @@ class Sleep extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#sleep'],
|
||||
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',
|
||||
}));
|
||||
@ -91,32 +83,21 @@ class Sleep extends Component {
|
||||
rotate: 180,
|
||||
easing: 'linear',
|
||||
loop: true,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS + TIMES.POST_SKILL_DURATION_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
this.animations.push(anime({
|
||||
targets: ['#stun'],
|
||||
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',
|
||||
}));
|
||||
|
||||
/* 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({
|
||||
targets: ['#charges'],
|
||||
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',
|
||||
}));
|
||||
|
||||
@ -125,11 +106,10 @@ class Sleep extends Component {
|
||||
targets: ['#charges'],
|
||||
cx: 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,
|
||||
easing: 'easeInOutSine',
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
// this is necessary because
|
||||
@ -140,8 +120,7 @@ class Sleep extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Sleep);
|
||||
module.exports = Sleep;
|
||||
|
||||
@ -1,31 +1,16 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
const anime = require('animejs').default;
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Strike extends Component {
|
||||
constructor(props) {
|
||||
constructor() {
|
||||
super();
|
||||
this.props = props;
|
||||
this.animations = [];
|
||||
}
|
||||
|
||||
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 (
|
||||
<svg
|
||||
class="strike-anim"
|
||||
@ -54,8 +39,8 @@ class Strike extends Component {
|
||||
x: [200, 0, 200],
|
||||
height: [200, 10, 0],
|
||||
width: [20, 400, 0],
|
||||
delay: TIMES.TARGET_DELAY_MS * 0.5,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
delay: TIMES.TARGET_DURATION_MS * 0.2,
|
||||
}));
|
||||
|
||||
this.animations.push(anime({
|
||||
@ -64,7 +49,7 @@ class Strike extends Component {
|
||||
scale: 50,
|
||||
numOctaves: 5,
|
||||
easing: 'easeOutSine',
|
||||
delay: TIMES.TARGET_DELAY_MS + (TIMES.TARGET_DURATION_MS / 3),
|
||||
delay: TIMES.TARGET_DURATION_MS / 3,
|
||||
duration: TIMES.TARGET_DURATION_MS / 2,
|
||||
}));
|
||||
}
|
||||
@ -77,9 +62,7 @@ class Strike extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = addState(Strike);
|
||||
module.exports = Strike;
|
||||
|
||||
@ -1,20 +1,11 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES } = require('../../constants');
|
||||
|
||||
// logarithmic spiral lifted from
|
||||
// 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 {
|
||||
constructor() {
|
||||
super();
|
||||
@ -54,7 +45,7 @@ class Stun extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#stun'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -65,7 +56,6 @@ class Stun extends Component {
|
||||
rotate: 180,
|
||||
easing: 'linear',
|
||||
loop: true,
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
}
|
||||
@ -78,8 +68,7 @@ class Stun extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Stun);
|
||||
module.exports = Stun;
|
||||
|
||||
@ -1,17 +1,9 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
class Sustain extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -33,17 +25,17 @@ class Sustain extends Component {
|
||||
</filter>
|
||||
<polyline
|
||||
id="stageOne"
|
||||
points='0 0'
|
||||
points='128,168 80,240 176,240 128,168'
|
||||
style={{ filter: 'url("#sustainFilter")' }}
|
||||
/>
|
||||
<polyline
|
||||
id="stageTwo"
|
||||
points='0 0'
|
||||
points='176,240 212,216 128,96 44,216 80,240'
|
||||
style={{ filter: 'url("#sustainFilter")' }}
|
||||
/>
|
||||
<polyline
|
||||
id="stageThree"
|
||||
points='0 0'
|
||||
points='212,216 248,192 128,24 8,192 44,216'
|
||||
style={{ filter: 'url("#sustainFilter")' }}
|
||||
/>
|
||||
</svg>
|
||||
@ -54,7 +46,7 @@ class Sustain extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#sustain'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -62,17 +54,15 @@ class Sustain extends Component {
|
||||
|
||||
this.animations.push(anime({
|
||||
targets: ['#stageOne'],
|
||||
points: '128,168 80,240 176,240 128,168',
|
||||
keyframes: [
|
||||
{
|
||||
stroke: [COLOURS.GREEN, COLOURS.RED],
|
||||
fill: [null, COLOURS.RED],
|
||||
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.2,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.6,
|
||||
delay: TIMES.TARGET_DURATION_MS * 0.2,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.15,
|
||||
easing: 'easeInOutSine',
|
||||
},
|
||||
],
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -82,13 +72,11 @@ class Sustain extends Component {
|
||||
{
|
||||
stroke: [COLOURS.GREEN, COLOURS.RED],
|
||||
fill: [null, COLOURS.RED],
|
||||
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.5,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.6,
|
||||
delay: TIMES.TARGET_DURATION_MS * 0.35,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.15,
|
||||
easing: 'easeInOutSine',
|
||||
},
|
||||
],
|
||||
points: '176,240 212,216 128,96 44,216 80,240',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -98,13 +86,11 @@ class Sustain extends Component {
|
||||
{
|
||||
stroke: [COLOURS.GREEN, COLOURS.RED],
|
||||
fill: [null, COLOURS.RED],
|
||||
delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.8,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.4,
|
||||
delay: TIMES.TARGET_DURATION_MS * 0.5,
|
||||
duration: TIMES.TARGET_DURATION_MS * 0.15,
|
||||
easing: 'easeInOutSine',
|
||||
},
|
||||
],
|
||||
points: '212,216 248,192 128,24 8,192 44,216',
|
||||
delay: TIMES.TARGET_DELAY_MS,
|
||||
duration: TIMES.TARGET_DURATION_MS,
|
||||
}));
|
||||
|
||||
@ -114,7 +100,7 @@ class Sustain extends Component {
|
||||
scale: 10,
|
||||
numOctaves: 5,
|
||||
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,
|
||||
}));
|
||||
}
|
||||
@ -127,8 +113,7 @@ class Sustain extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Sustain);
|
||||
module.exports = Sustain;
|
||||
|
||||
@ -6,13 +6,6 @@ const { connect } = require('preact-redux');
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
const { randomPoints } = require('../../utils');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animCb } = state;
|
||||
return { animCb };
|
||||
}
|
||||
);
|
||||
|
||||
function projectile(x, y, radius, colour) {
|
||||
return (
|
||||
<circle
|
||||
@ -60,7 +53,7 @@ class Triage extends Component {
|
||||
this.animations.push(anime({
|
||||
targets: ['#triage'],
|
||||
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 },
|
||||
],
|
||||
easing: 'easeInOutSine',
|
||||
@ -80,8 +73,7 @@ class Triage extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(Triage);
|
||||
module.exports = Triage;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
const preact = require('preact');
|
||||
const { Component } = require('preact');
|
||||
const anime = require('animejs').default;
|
||||
const { connect } = require('preact-redux');
|
||||
|
||||
const { TIMES, COLOURS } = require('../../constants');
|
||||
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 {
|
||||
constructor(props) {
|
||||
super();
|
||||
this.team = props.team;
|
||||
this.animations = [];
|
||||
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));
|
||||
@ -79,8 +70,7 @@ class TriageTick extends Component {
|
||||
for (let i = this.animations.length - 1; i >= 0; i--) {
|
||||
this.animations[i].reset();
|
||||
}
|
||||
this.props.animCb && this.props.animCb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = addState(TriageTick);
|
||||
module.exports = TriageTick;
|
||||
|
||||
@ -13,8 +13,8 @@ const { ConstructAnimation } = require('./animations');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const { animSource, animTarget, animText } = state;
|
||||
return { animSource, animTarget, animText };
|
||||
const { animating, animSource, animTarget, resolution, account } = state;
|
||||
return { animating, animSource, animTarget, resolution, account };
|
||||
}
|
||||
);
|
||||
|
||||
@ -43,6 +43,7 @@ class ConstructAvatar extends Component {
|
||||
}
|
||||
|
||||
onClick() {
|
||||
if (this.props.animating) return false;
|
||||
return this.animations.push(wiggle(this.props.construct.id, this.idle));
|
||||
}
|
||||
|
||||
@ -62,19 +63,24 @@ class ConstructAvatar extends Component {
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { animSource, animTarget, animText, construct } = this.props;
|
||||
const { animSource, animTarget, resolution, construct, account } = this.props;
|
||||
// a different text object and text construct
|
||||
if (animText && animText !== prevProps.animText && animText.constructId === construct.id) {
|
||||
return wiggle(construct.id, this.idle);
|
||||
if (resolution && resolution !== prevProps.resolution && resolution.event[1].construct === construct.id) {
|
||||
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
|
||||
if (animSource && animSource !== prevProps.animSource && animSource.constructId === construct.id) {
|
||||
return sourceCast(animSource.constructId, animSource.direction, this.idle);
|
||||
if (animSource && animSource !== prevProps.animSource && animSource.event[1].construct === construct.id) {
|
||||
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
|
||||
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) {
|
||||
case 'Banish': return banish(construct.id, this.idle);
|
||||
case 'Invert': return invert(construct.id, this.idle);
|
||||
@ -86,26 +92,23 @@ class ConstructAvatar extends Component {
|
||||
|
||||
|
||||
shouldComponentUpdate(newProps) {
|
||||
const { animSource, animTarget, animText, construct, mouseOver } = newProps;
|
||||
const { animSource, animTarget, resolution, construct, mouseOver } = newProps;
|
||||
if (animSource !== this.props.animSource) 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 (mouseOver !== this.props.mouseOver) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function ConstructText(props) {
|
||||
function ConstructName(props) {
|
||||
const { construct } = props;
|
||||
if (!construct) return false;
|
||||
|
||||
const text = construct.name;
|
||||
|
||||
return <h3 class={'name'}><span>{text}</span></h3>;
|
||||
return <h3 class={'name'}><span>{construct.name}</span></h3>;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ConstructAvatar: addState(ConstructAvatar),
|
||||
ConstructText,
|
||||
ConstructName,
|
||||
};
|
||||
|
||||
@ -10,6 +10,7 @@ const addState = connect(
|
||||
function receiveState(state) {
|
||||
const {
|
||||
ws,
|
||||
authenticated,
|
||||
account,
|
||||
game,
|
||||
instance,
|
||||
@ -17,6 +18,7 @@ const addState = connect(
|
||||
} = state;
|
||||
|
||||
return {
|
||||
authenticated,
|
||||
account,
|
||||
game,
|
||||
instance,
|
||||
@ -28,6 +30,7 @@ const addState = connect(
|
||||
function Controls(args) {
|
||||
const {
|
||||
game,
|
||||
authenticated,
|
||||
account,
|
||||
instance,
|
||||
nav,
|
||||
@ -38,6 +41,7 @@ function Controls(args) {
|
||||
|
||||
if (game) return <GameCtrl />;
|
||||
if (instance) return <InstanceCtrl />;
|
||||
if (!authenticated) return false;
|
||||
if (nav === 'play' || nav === 'shop' || nav === 'reshape' || !nav) return <PlayCtrl />
|
||||
if (nav === 'team' || nav === 'account') return <TeamCtrl />
|
||||
|
||||
|
||||
@ -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' > </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> </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
client/src/components/front.page.jsx
Normal file
85
client/src/components/front.page.jsx
Normal 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') }>
|
||||
|
||||
</button>
|
||||
<figcaption>Join the Community</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div class="logo"/>
|
||||
<hr />
|
||||
{list()}
|
||||
<hr />
|
||||
<Welcome />
|
||||
<hr />
|
||||
{news}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = addState(Play);
|
||||
89
client/src/components/game.construct.anim.text.jsx
Normal file
89
client/src/components/game.construct.anim.text.jsx
Normal file
@ -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);
|
||||
73
client/src/components/game.construct.effect.box.jsx
Normal file
73
client/src/components/game.construct.effect.box.jsx
Normal file
@ -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);
|
||||
@ -1,95 +1,23 @@
|
||||
const preact = require('preact');
|
||||
const { connect } = require('preact-redux');
|
||||
const anime = require('animejs').default;
|
||||
const range = require('lodash/range');
|
||||
const reactStringReplace = require('react-string-replace');
|
||||
|
||||
const { STATS, removeTier } = require('../utils');
|
||||
const { ConstructAvatar, ConstructText } = require('./construct');
|
||||
const shapes = require('./shapes');
|
||||
const { INFO, TIMES } = require('./../constants');
|
||||
const actions = require('../actions');
|
||||
const { ConstructAvatar, ConstructName } = require('./construct');
|
||||
|
||||
const SkillBtn = require('./skill.btn');
|
||||
|
||||
const addStateText = connect(({ animText, itemInfo }) => ({ animText, itemInfo }));
|
||||
|
||||
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>
|
||||
<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 SkillBtn = require('./game.construct.skill.btn');
|
||||
const ConstructAnimationText = require('./game.construct.anim.text');
|
||||
const ConstructLife = require('./game.construct.life');
|
||||
const ConstructEffectBox = require('./game.construct.effect.box');
|
||||
|
||||
const addState = connect(
|
||||
function receiveState(state) {
|
||||
const {
|
||||
ws,
|
||||
game,
|
||||
account,
|
||||
|
||||
activeSkill,
|
||||
animFocus,
|
||||
animating,
|
||||
animText,
|
||||
gameSkillInfo,
|
||||
itemInfo,
|
||||
tutorialGame,
|
||||
resolution,
|
||||
} = state;
|
||||
|
||||
function selectSkillTarget(targetConstructId) {
|
||||
@ -100,65 +28,23 @@ const addState = connect(
|
||||
}
|
||||
|
||||
return {
|
||||
game,
|
||||
account,
|
||||
animating,
|
||||
animFocus,
|
||||
animText,
|
||||
activeSkill,
|
||||
animFocus,
|
||||
resolution,
|
||||
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 {
|
||||
constructor() {
|
||||
super();
|
||||
this.resolvedLength = 0;
|
||||
}
|
||||
|
||||
shouldComponentUpdate(newProps) {
|
||||
if (newProps.activeSkill !== this.props.activeSkill) 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.player !== this.props.player) return true;
|
||||
if (newProps.tutorialGame !== this.props.tutorialGame) return true;
|
||||
if (newProps.gameSkillInfo !== this.props.gameSkillInfo) return true;
|
||||
if (newProps.resolution && newProps.resolution !== this.props.resolution) {
|
||||
const [type, variant] = newProps.resolution.event;
|
||||
if (variant.construct === this.props.construct.id && type === 'Ko') return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -167,84 +53,46 @@ class GameConstruct extends preact.Component {
|
||||
// Changing state variables
|
||||
activeSkill,
|
||||
animFocus,
|
||||
animText,
|
||||
animating,
|
||||
resolution,
|
||||
selectSkillTarget,
|
||||
construct,
|
||||
player,
|
||||
tutorialGame,
|
||||
gameSkillInfo,
|
||||
// Constants
|
||||
i,
|
||||
itemInfo,
|
||||
// Functions
|
||||
selectSkillTarget,
|
||||
setTutorialGameClear,
|
||||
setGameEffectInfo,
|
||||
} = this.props;
|
||||
|
||||
const koEvent = animText ? animText.text === 'KO!' && animText.constructId === construct.id : false;
|
||||
const ko = construct.green_life.value === 0 && !koEvent ? 'ko' : '';
|
||||
const classes = eventClasses(animating, animFocus, construct, animText);
|
||||
const cssClass = ['ko-transition', 'unfocus'].includes(classes) ? classes : null;
|
||||
|
||||
const stats = ['RedLife', 'GreenLife', 'BlueLife'].map((s, j) => (
|
||||
<div key={j} alt={STATS[s].stat}>
|
||||
{shapes[s]()}
|
||||
<div class="max" >{construct[STATS[s].stat].value} / {construct[STATS[s].stat].max}</div>
|
||||
<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>);
|
||||
// construct green_life comes from game state and won't update during animations
|
||||
// treat the construct as ko for the remainder of the anims if ko event occurs
|
||||
const ko = construct.green_life.value === 0 || this.ko ? 'ko' : '';
|
||||
const koEvent = () => {
|
||||
if (resolution) {
|
||||
const [type, variant] = resolution.event;
|
||||
if (variant.construct === construct.id && type === 'Ko') {
|
||||
this.ko = true;
|
||||
return 'ko-transition';
|
||||
}
|
||||
}
|
||||
const effects = construct.effects.length
|
||||
? 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>);
|
||||
return '';
|
||||
};
|
||||
|
||||
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 (
|
||||
<div
|
||||
onClick={() => {
|
||||
selectSkillTarget(construct.id);
|
||||
setTutorialGameClear(activeSkill, tutorialGame);
|
||||
}}
|
||||
onClick={() => selectSkillTarget(construct.id)}
|
||||
style={ activeSkill ? { cursor: 'pointer' } : {}}
|
||||
class={`game-construct ${ko} ${cssClass}`}>
|
||||
class={`game-construct ${ko} ${koEvent()} ${unfocus}`}>
|
||||
<div class="left">
|
||||
{crypSkills}
|
||||
{effectBox()}
|
||||
<ConstructEffectBox construct={construct} />
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="stats"> {stats} </div>
|
||||
<ConstructLife construct={construct} />
|
||||
<ConstructAvatar construct={construct} />
|
||||
<ConstructText construct={construct} />
|
||||
<ConstructName construct={construct} />
|
||||
<ConstructAnimationText construct={construct} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
49
client/src/components/game.construct.life.jsx
Normal file
49
client/src/components/game.construct.life.jsx
Normal file
@ -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 {
|
||||
activeSkill,
|
||||
game,
|
||||
animating,
|
||||
} = state;
|
||||
|
||||
return {
|
||||
activeSkill,
|
||||
game,
|
||||
animating,
|
||||
};
|
||||
},
|
||||
|
||||
@ -8,6 +8,7 @@ const addState = connect(
|
||||
const {
|
||||
ws,
|
||||
account,
|
||||
authenticated,
|
||||
nav,
|
||||
} = state;
|
||||
|
||||
@ -22,6 +23,7 @@ const addState = connect(
|
||||
|
||||
return {
|
||||
account,
|
||||
authenticated,
|
||||
nav,
|
||||
|
||||
sendInstanceState,
|
||||
@ -48,6 +50,7 @@ const addState = connect(
|
||||
function Header(args) {
|
||||
const {
|
||||
account,
|
||||
authenticated,
|
||||
nav,
|
||||
|
||||
sendAccountStates,
|
||||
@ -56,6 +59,8 @@ function Header(args) {
|
||||
|
||||
if (!account) return false;
|
||||
|
||||
if (!authenticated) return false;
|
||||
|
||||
function navTo(p) {
|
||||
return setNav(p);
|
||||
}
|
||||
@ -68,11 +73,6 @@ function Header(args) {
|
||||
return (
|
||||
<header>
|
||||
<div class="options">
|
||||
<button
|
||||
onClick={() => navTo('play')}
|
||||
class='logo login-btn'>
|
||||
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navTo('play')}
|
||||
class={`login-btn ${nav === 'play' ? 'highlight' : ''}`}>
|
||||
|
||||
@ -23,6 +23,7 @@ const addState = connect(
|
||||
function Top(args) {
|
||||
const {
|
||||
nav,
|
||||
authenticated,
|
||||
} = args;
|
||||
|
||||
if (nav === 'account') return <AccountTop />;
|
||||
|
||||
@ -4,27 +4,38 @@ const { connect } = require('preact-redux');
|
||||
const Main = require('./main');
|
||||
// const Nav = require('./nav');
|
||||
const Controls = require('./controls');
|
||||
const FrontPage = require('./front.page');
|
||||
const Noise = require('./noise');
|
||||
|
||||
const addState = connect(
|
||||
({ game, instance }) => ({ game, instance })
|
||||
({ game, instance, authenticated }) => ({ game, instance, authenticated })
|
||||
);
|
||||
|
||||
function Mnml(args) {
|
||||
const {
|
||||
game,
|
||||
instance,
|
||||
authenticated,
|
||||
} = args;
|
||||
|
||||
const rotateClass = (game || instance) && window.innerHeight < 900 && window.innerWidth < window.innerHeight
|
||||
? 'show'
|
||||
: '';
|
||||
|
||||
if (!authenticated && !instance && !game) return (
|
||||
<div id="mnml" class='front-page'>
|
||||
<Noise />
|
||||
<FrontPage />
|
||||
<div id="rotate" class={rotateClass} ></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div id="mnml">
|
||||
<Main />
|
||||
<Controls />
|
||||
<div id="rotate" class={rotateClass} >
|
||||
</div>
|
||||
<Noise />
|
||||
<div id="rotate" class={rotateClass} ></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
75
client/src/components/noise.jsx
Normal file
75
client/src/components/noise.jsx
Normal 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;
|
||||
@ -6,11 +6,11 @@ const reactStringReplace = require('react-string-replace');
|
||||
const throttle = require('lodash/throttle');
|
||||
|
||||
const shapes = require('./shapes');
|
||||
const { effectInfo, removeTier } = require('../utils');
|
||||
const { effectInfo } = require('../utils');
|
||||
|
||||
const addState = connect(
|
||||
({ game, account, animSkill, animating, itemInfo, gameEffectInfo, tutorialGame }) =>
|
||||
({ game, account, animSkill, animating, itemInfo, gameEffectInfo, tutorialGame })
|
||||
({ game, account, animating, itemInfo, gameEffectInfo, tutorialGame }) =>
|
||||
({ game, account, animating, itemInfo, gameEffectInfo, tutorialGame })
|
||||
);
|
||||
|
||||
class TargetSvg extends Component {
|
||||
@ -31,7 +31,6 @@ class TargetSvg extends Component {
|
||||
if (newProps.game !== this.props.game) return true;
|
||||
if (newProps.account !== this.props.account) 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.tutorialGame !== this.props.tutorialGame) return true;
|
||||
if (newState.width !== this.state.width) return true;
|
||||
@ -44,12 +43,9 @@ class TargetSvg extends Component {
|
||||
// Changing State Variables
|
||||
account,
|
||||
animating,
|
||||
animSkill,
|
||||
game,
|
||||
gameEffectInfo,
|
||||
tutorialGame,
|
||||
// Static
|
||||
itemInfo,
|
||||
} = props;
|
||||
const { width, height } = state;
|
||||
|
||||
@ -87,14 +83,14 @@ class TargetSvg extends Component {
|
||||
const otherTeam = game.players.find(t => t.id !== account.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) {
|
||||
const source = playerTeam.constructs.findIndex(c => c.id === cast.source_construct_id);
|
||||
const defensive = playerTeamIds.includes(cast.target_construct_id);
|
||||
const source = playerTeam.constructs.findIndex(c => c.id === cast.source);
|
||||
const defensive = playerTeamIds.includes(cast.target);
|
||||
const target = defensive
|
||||
? playerTeam.constructs.findIndex(c => c.id === cast.target_construct_id)
|
||||
: otherTeam.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);
|
||||
|
||||
const skillNumber = window.innerWidth <= 800 // mobile styling trigger
|
||||
? playerTeam.constructs[source].skills.findIndex(s => s.skill === cast.skill)
|
||||
|
||||
@ -71,7 +71,6 @@ class Combos extends preact.Component {
|
||||
<div class="combos">
|
||||
<div class="combo-header">
|
||||
<h2>COMBOS</h2>
|
||||
Combine colours and items.
|
||||
</div>
|
||||
<div class="combo-list"
|
||||
onMouseOver={e => e.stopPropagation()}
|
||||
|
||||
@ -5,10 +5,9 @@ const Login = require('./welcome.login');
|
||||
const Register = require('./welcome.register');
|
||||
const Help = require('./welcome.help');
|
||||
// const About = require('./welcome.about');
|
||||
const Demo = require('./demo');
|
||||
|
||||
function Welcome() {
|
||||
const page = this.state.page || 'register';
|
||||
const page = this.state.page || 'login';
|
||||
|
||||
const pageEl = () => {
|
||||
if (page === 'login') return <Login />;
|
||||
@ -17,65 +16,32 @@ function Welcome() {
|
||||
return false;
|
||||
};
|
||||
|
||||
const news = (
|
||||
<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 />;
|
||||
const form = <div>{pageEl()}</div>;
|
||||
|
||||
return (
|
||||
<main class="menu welcome">
|
||||
<header>
|
||||
<div class="options">
|
||||
<button
|
||||
onClick={() => this.setState({ page: 'login' })}
|
||||
class='logo login-btn'>
|
||||
|
||||
</button>
|
||||
<button
|
||||
class={`login-btn ${page === 'login' ? 'highlight' : ''}`}
|
||||
disabled={page === 'login'}
|
||||
onClick={() => this.setState({ page: 'login' })}>
|
||||
Login
|
||||
</button>
|
||||
<button
|
||||
class={`login-btn ${page === 'register' ? 'highlight' : ''}`}
|
||||
disabled={page === 'register'}
|
||||
onClick={() => this.setState({ page: 'register' })}>
|
||||
Register
|
||||
</button>
|
||||
<button
|
||||
class={`login-btn ${page === 'info' ? 'highlight' : ''}`}
|
||||
disabled={page === 'info'}
|
||||
onClick={() => this.setState({ page: 'info' })}>
|
||||
Info
|
||||
</button>
|
||||
<button
|
||||
class={`login-btn ${page === 'help' ? 'highlight' : ''}`}
|
||||
disabled={page === 'help'}
|
||||
onClick={() => this.setState({ page: 'help' })}>
|
||||
Help
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div class="top">
|
||||
{main}
|
||||
<header>
|
||||
<div class="options">
|
||||
<button
|
||||
class={`login-btn ${page === 'login' ? 'highlight' : ''}`}
|
||||
disabled={page === 'login'}
|
||||
onClick={() => this.setState({ page: 'login' })}>
|
||||
Login
|
||||
</button>
|
||||
<button
|
||||
class={`login-btn ${page === 'register' ? 'highlight' : ''}`}
|
||||
disabled={page === 'register'}
|
||||
onClick={() => this.setState({ page: 'register' })}>
|
||||
Register
|
||||
</button>
|
||||
<button
|
||||
class={`login-btn ${page === 'help' ? 'highlight' : ''}`}
|
||||
disabled={page === 'help'}
|
||||
onClick={() => this.setState({ page: 'help' })}>
|
||||
Help
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
{form}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -5,8 +5,7 @@ const eachSeries = require('async/eachSeries');
|
||||
const sample = require('lodash/sample');
|
||||
|
||||
const actions = require('./actions');
|
||||
const { TIMES } = require('./constants');
|
||||
const animations = require('./animations.utils');
|
||||
const { setAnimations, clearAnimations } = require('./animations.utils');
|
||||
const { infoToast, errorToast } = require('./utils');
|
||||
const { tutorialVbox } = require('./tutorial.utils');
|
||||
|
||||
@ -28,10 +27,13 @@ function registerEvents(store) {
|
||||
|
||||
function clearTutorial() {
|
||||
store.dispatch(actions.setTutorial(null));
|
||||
localStorage.setItem('tutorial-complete', true);
|
||||
}
|
||||
|
||||
|
||||
function clearTutorialGame() {
|
||||
store.dispatch(actions.setTutorialGame(null));
|
||||
}
|
||||
|
||||
function setPing(ping) {
|
||||
store.dispatch(actions.setPing(ping));
|
||||
}
|
||||
@ -73,67 +75,23 @@ function registerEvents(store) {
|
||||
}
|
||||
|
||||
function setGame(game) {
|
||||
const { game: currentGame, account, ws, animating } = store.getState();
|
||||
const { game: currentGame, ws, animating } = store.getState();
|
||||
|
||||
if (animating) return false;
|
||||
|
||||
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.setGameSkillInfo(null));
|
||||
// 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) => {
|
||||
if (!r.event) return cb();
|
||||
let timeout = animations.getTime(r.stages);
|
||||
const anims = animations.getObjects(r, game, account);
|
||||
const text = animations.getText(r);
|
||||
store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r, game)));
|
||||
if (anims.animSkill) store.dispatch(actions.setAnimSkill(anims.animSkill));
|
||||
|
||||
if (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);
|
||||
if (r.delay === 0) return cb(); // TargetKo etc
|
||||
setAnimations(r, store);
|
||||
return setTimeout(cb, r.delay);
|
||||
}, err => {
|
||||
if (err) return console.error(err);
|
||||
// clear animation state
|
||||
store.dispatch(actions.setAnimSkill(null));
|
||||
store.dispatch(actions.setAnimSource(null));
|
||||
store.dispatch(actions.setAnimTarget(null));
|
||||
store.dispatch(actions.setAnimText(null));
|
||||
store.dispatch(actions.setAnimating(false));
|
||||
store.dispatch(actions.setGameEffectInfo(null));
|
||||
|
||||
clearAnimations(store);
|
||||
// set the game state so resolutions don't fire twice
|
||||
store.dispatch(actions.setGame(game));
|
||||
ws.sendGameState(game.id);
|
||||
@ -146,16 +104,21 @@ function registerEvents(store) {
|
||||
}
|
||||
|
||||
function setAccount(account) {
|
||||
if (account && process.env.NODE_ENV !== 'development') {
|
||||
LogRocket.init('yh0dy3/mnml');
|
||||
LogRocket.identify(account.id, account);
|
||||
store.dispatch(actions.setAccount(account));
|
||||
}
|
||||
|
||||
if (window.Notification) {
|
||||
window.Notification.requestPermission();
|
||||
}
|
||||
function setAuthenticated(account) {
|
||||
if (account && window.Notification) {
|
||||
window.Notification.requestPermission();
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
LogRocket.identify(account.id, account);
|
||||
}
|
||||
|
||||
store.dispatch(actions.setAccount(account));
|
||||
store.dispatch(actions.setTutorial(null));
|
||||
store.dispatch(actions.setAuthenticated(true));
|
||||
}
|
||||
|
||||
function setEmail(email) {
|
||||
@ -220,23 +183,14 @@ function registerEvents(store) {
|
||||
const player = v.players.find(p => p.id === account.id);
|
||||
store.dispatch(actions.setPlayer(player));
|
||||
|
||||
const skip = v.time_control === 'Practice' && v.phase === 'Lobby';
|
||||
if (skip) {
|
||||
ws.sendInstanceReady(v.id);
|
||||
}
|
||||
if (tutorial) tutorialVbox(player, store, tutorial);
|
||||
|
||||
if (v.phase === 'Finished') {
|
||||
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));
|
||||
}
|
||||
|
||||
@ -252,94 +206,6 @@ function registerEvents(store) {
|
||||
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() {
|
||||
const { ws } = store.getState();
|
||||
const cmds = querystring.parse(location.hash);
|
||||
@ -348,6 +214,11 @@ function registerEvents(store) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function startTutorial() {
|
||||
store.dispatch(actions.setTutorial(1));
|
||||
}
|
||||
|
||||
|
||||
window.addEventListener('hashchange', urlHashChange, false);
|
||||
|
||||
return {
|
||||
@ -356,12 +227,13 @@ function registerEvents(store) {
|
||||
clearInstance,
|
||||
clearMtxActive,
|
||||
clearTutorial,
|
||||
clearTutorialGame,
|
||||
setAccount,
|
||||
setAuthenticated,
|
||||
setAccountInstances,
|
||||
setActiveItem,
|
||||
setActiveSkill,
|
||||
setChatWheel,
|
||||
setDemo,
|
||||
setConstructList,
|
||||
setNewConstruct,
|
||||
setGame,
|
||||
@ -377,6 +249,8 @@ function registerEvents(store) {
|
||||
setSubscription,
|
||||
setWs,
|
||||
|
||||
startTutorial,
|
||||
|
||||
urlHashChange,
|
||||
|
||||
notify,
|
||||
|
||||
@ -10,18 +10,16 @@ function createReducer(defaultState, actionType) {
|
||||
/* eslint-disable key-spacing */
|
||||
module.exports = {
|
||||
account: createReducer(null, 'SET_ACCOUNT'),
|
||||
authenticated: createReducer(null, 'SET_AUTHENTICATED'),
|
||||
activeItem: createReducer(null, 'SET_ACTIVE_VAR'),
|
||||
activeSkill: createReducer(null, 'SET_ACTIVE_SKILL'),
|
||||
|
||||
animating: createReducer(false, 'SET_ANIMATING'),
|
||||
animCb: createReducer(null, 'SET_ANIM_CB'),
|
||||
animSkill: createReducer(null, 'SET_ANIM_SKILL'),
|
||||
animSource: createReducer(null, 'SET_ANIM_SOURCE'),
|
||||
animFocus: createReducer(null, 'SET_ANIM_FOCUS'),
|
||||
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'),
|
||||
chatWheel: createReducer([], 'SET_CHAT_WHEEL'),
|
||||
|
||||
@ -129,6 +129,7 @@ function createSocket(events) {
|
||||
{ game_id: gameId, construct_id: constructId, target_construct_id: targetConstructId, skill },
|
||||
]);
|
||||
events.setActiveSkill(null);
|
||||
events.clearTutorialGame();
|
||||
}
|
||||
|
||||
function sendGameSkillClear(gameId) {
|
||||
@ -255,10 +256,6 @@ function createSocket(events) {
|
||||
events.setItemInfo(info);
|
||||
}
|
||||
|
||||
function onDemo(v) {
|
||||
events.setDemo(v);
|
||||
}
|
||||
|
||||
let pongTimeout;
|
||||
function onPong() {
|
||||
events.setPing(Date.now() - ping);
|
||||
@ -273,6 +270,7 @@ function createSocket(events) {
|
||||
// this object wraps the reply types to a function
|
||||
const handlers = {
|
||||
AccountState: onAccount,
|
||||
AccountAuthenticated: account => events.setAuthenticated(account),
|
||||
AccountConstructs: onAccountConstructs,
|
||||
AccountTeam: onAccountTeam,
|
||||
AccountInstances: onAccountInstances,
|
||||
@ -284,7 +282,6 @@ function createSocket(events) {
|
||||
InstanceState: onInstanceState,
|
||||
ItemInfo: onItemInfo,
|
||||
Pong: onPong,
|
||||
Demo: onDemo,
|
||||
|
||||
// QueueRequested: () => events.notify('PVP queue request received.'),
|
||||
QueueRequested: () => true,
|
||||
@ -303,6 +300,8 @@ function createSocket(events) {
|
||||
ChatWheel: wheel => events.setChatWheel(wheel),
|
||||
// Joining: () => events.notify('Searching for instance...'),
|
||||
|
||||
StartTutorial: () => events.startTutorial(),
|
||||
|
||||
Processing: () => true,
|
||||
Error: errHandler,
|
||||
};
|
||||
|
||||
@ -115,8 +115,9 @@ function tutorialStage(tutorial, clearTutorial, instance) {
|
||||
if (tutorial === 1) {
|
||||
return (
|
||||
<div class='info-item'>
|
||||
<h2>Tutorial</h2>
|
||||
<p> Welcome to the vbox phase tutorial.</p>
|
||||
<h1>Welcome to MNML</h1>
|
||||
<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> Buy the two colours from the store to continue. </p>
|
||||
</div>
|
||||
@ -126,9 +127,9 @@ function tutorialStage(tutorial, clearTutorial, instance) {
|
||||
if (tutorial === 2) {
|
||||
return (
|
||||
<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> 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>
|
||||
);
|
||||
}
|
||||
@ -137,11 +138,11 @@ function tutorialStage(tutorial, clearTutorial, instance) {
|
||||
const constructOne = instance.players[0].constructs[0].name;
|
||||
return (
|
||||
<div class='info-item'>
|
||||
<h2>Tutorial</h2>
|
||||
<h2>Equipping Items</h2>
|
||||
<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> 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>
|
||||
);
|
||||
}
|
||||
@ -149,7 +150,7 @@ function tutorialStage(tutorial, clearTutorial, instance) {
|
||||
if (tutorial === 4) {
|
||||
return (
|
||||
<div class='info-item'>
|
||||
<h2>Tutorial</h2>
|
||||
<h2>Specialisations</h2>
|
||||
<p> You can also buy specialisation items for your constructs. <br />
|
||||
Specialisation items increase stats including power, speed and life. </p>
|
||||
<p> Buy the specialisation item from the store to continue. </p>
|
||||
@ -160,11 +161,12 @@ function tutorialStage(tutorial, clearTutorial, instance) {
|
||||
if (tutorial === 5) {
|
||||
return (
|
||||
<div class='info-item'>
|
||||
<h2>Tutorial</h2>
|
||||
<h2>Specialisations</h2>
|
||||
<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> Click the specialisation item in the stash.<br />
|
||||
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>
|
||||
);
|
||||
}
|
||||
@ -174,11 +176,12 @@ function tutorialStage(tutorial, clearTutorial, instance) {
|
||||
const constructThree = instance.players[0].constructs[2].name;
|
||||
return (
|
||||
<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> 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 />
|
||||
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>
|
||||
);
|
||||
}
|
||||
@ -186,7 +189,7 @@ function tutorialStage(tutorial, clearTutorial, instance) {
|
||||
if (tutorial === 7) {
|
||||
return (
|
||||
<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> Bits are your currency for buying items. <br />
|
||||
You can refill the store by pressing the refill button for 2b. <br />
|
||||
@ -203,22 +206,22 @@ function tutorialStage(tutorial, clearTutorial, instance) {
|
||||
|
||||
return (
|
||||
<div class='info-item'>
|
||||
<h2>Tutorial</h2>
|
||||
<h2>GLHF</h2>
|
||||
<p>That completes the VBOX Tutorial.</p>
|
||||
<p>Press <b>READY</b> to progress to the <b>GAME PHASE</b> <br />
|
||||
You can continue creating new items to upgrade your constructs further. </p>
|
||||
<p>Press the green <b>READY</b> button in the bottom right to progress to the <b>GAME PHASE</b> <br />
|
||||
or continue creating new items to upgrade your constructs further. </p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const classes = tutorial === 8 ? 'focus' : '';
|
||||
const text = tutorial === 8 ? 'Continue' : 'Skip Tutorial'
|
||||
const exitTutorial = <button
|
||||
class={classes}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onMouseDown={exit}> {text} </button>;
|
||||
const exitTutorial = tutorial === 8 ?
|
||||
<button
|
||||
class='focus'
|
||||
onClick={e => e.stopPropagation()}
|
||||
onMouseDown={exit}> Continue </button>
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div class='tutorial'>
|
||||
|
||||
@ -240,28 +240,16 @@ function convertItem(v) {
|
||||
}
|
||||
|
||||
function effectInfo(i) {
|
||||
// FIX ME
|
||||
const hybridBlast = 25;
|
||||
const hasteStrike = 30;
|
||||
function multiplier(s) { // Update later to use server info in future
|
||||
if (s === 'CounterAttack') return 120;
|
||||
if (s === 'CounterAttack+') return 160;
|
||||
if (s === 'CounterAttack++') return 230;
|
||||
|
||||
if (s === 'DecayTick') return 33;
|
||||
if (s === 'DecayTick+') return 45;
|
||||
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;
|
||||
if (s === 'CounterAttack') return 115;
|
||||
if (s === 'CounterAttack+') return 130;
|
||||
if (s === 'CounterAttack++') return 160;
|
||||
if (s === 'Electrocute') return 80;
|
||||
if (s === 'Electrocute+') return 90;
|
||||
if (s === 'Electrocute++') return 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -286,13 +274,13 @@ function effectInfo(i) {
|
||||
case 'Vulnerable': return `Construct will take ${i.meta[1] - 100}% increased red damage`;
|
||||
case 'Silence': return 'Disable construct from casting any blue skills';
|
||||
case 'Wither': return `Construct will take ${100 - i.meta[1]}% reduced healing`; //
|
||||
case 'Decay': return `Construct will take ${multiplier(i.tick.skill)}% of caster's BluePower as blue damage each turn.`; //
|
||||
case '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 '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 'Absorption': return `Increasing construct RedPower and BluePower by ${i.meta[1]}`;
|
||||
case 'Triage': return `Construct will be healed for ${multiplier(i.tick.skill)}% of caster's GreenPower each turn.`;
|
||||
case 'Siphon': return `Construct will take ${multiplier(i.tick.skill)}% of caster's BluePower + GreenPower as blue damage each turn, healing the caster.`;
|
||||
case 'Triage': return `Construct will be healed for ${i.meta[1].amount} green life each turn.`;
|
||||
case 'Siphon': return `Construct will take ${i.meta[1].amount} blue damage each turn, healing the caster.`;
|
||||
|
||||
default: return 'Missing Effect Text';
|
||||
}
|
||||
|
||||
3
core/.cargo/config
Executable file
3
core/.cargo/config
Executable file
@ -0,0 +1,3 @@
|
||||
[target.x86_64-pc-windows-msvc.gnu]
|
||||
rustc-link-search = ["C:\\Program Files\\PostgreSQL\\pg96\\lib"]
|
||||
|
||||
4
core/.gitignore
vendored
Normal file
4
core/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
target/
|
||||
Cargo.lock
|
||||
log/
|
||||
.env
|
||||
13
core/Cargo.toml
Normal file
13
core/Cargo.toml
Normal 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
core/fixme.md
Normal file
5
core/fixme.md
Normal 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
@ -1,8 +1,9 @@
|
||||
use construct::{Stat, EffectMeta};
|
||||
use game::{Colour};
|
||||
use skill::{Skill};
|
||||
use util::{IntPct};
|
||||
|
||||
pub type Cooldown = Option<u8>;
|
||||
pub type Cooldown = Option<usize>;
|
||||
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||
pub enum Effect {
|
||||
@ -41,10 +42,17 @@ pub enum Effect {
|
||||
|
||||
// effects over time
|
||||
Triage,
|
||||
Decay,
|
||||
Regen,
|
||||
Siphon,
|
||||
Triaged, // immunity
|
||||
|
||||
Decay,
|
||||
Decayed, // immunity
|
||||
|
||||
Siphon,
|
||||
Siphoned, // immunity
|
||||
|
||||
Countered,
|
||||
|
||||
// Regen,
|
||||
// Airborne,
|
||||
// Boost
|
||||
// Bleed,
|
||||
@ -66,18 +74,42 @@ impl Effect {
|
||||
pub fn immune(&self, skill: Skill) -> bool {
|
||||
match self {
|
||||
Effect::Banish => true,
|
||||
Effect::Sustain => [
|
||||
Skill::Stun,
|
||||
Skill::Silence,
|
||||
Skill::SilencePlus,
|
||||
Skill::SilencePlusPlus,
|
||||
Skill::Ruin,
|
||||
Skill::RuinPlus,
|
||||
Skill::RuinPlusPlus,
|
||||
Skill::Restrict,
|
||||
Skill::RestrictPlus,
|
||||
Skill::RestrictPlusPlus
|
||||
|
||||
// these provide immunity for the ticks
|
||||
// the base skills will still resolve
|
||||
// but they have early return checks
|
||||
// to ensure the effect is reapplied but damage is not
|
||||
Effect::Siphoned => [
|
||||
Skill::SiphonTick,
|
||||
].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,
|
||||
}
|
||||
}
|
||||
@ -101,7 +133,7 @@ impl Effect {
|
||||
pub fn modifications(&self) -> Vec<Stat> {
|
||||
match self {
|
||||
// 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::Slow => vec![Stat::Speed],
|
||||
|
||||
@ -111,10 +143,10 @@ impl Effect {
|
||||
Effect::Hybrid => vec![Stat::GreenPower],
|
||||
|
||||
// Damage taken changes
|
||||
Effect::Curse => vec![Stat::RedDamageTaken, Stat::BlueDamageTaken],
|
||||
Effect::Pure => vec![Stat::GreenDamageTaken], // increased green taken
|
||||
Effect::Vulnerable => vec![Stat::RedDamageTaken],
|
||||
Effect::Wither => vec![Stat::GreenDamageTaken], // reduced green taken
|
||||
Effect::Curse => vec![Stat::RedDamageReceived, Stat::BlueDamageReceived],
|
||||
Effect::Pure => vec![Stat::GreenHealingReceived], // increased green taken
|
||||
Effect::Vulnerable => vec![Stat::RedDamageReceived],
|
||||
Effect::Wither => vec![Stat::GreenHealingReceived], // reduced green taken
|
||||
|
||||
// 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 {
|
||||
Effect::Amplify |
|
||||
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 {
|
||||
// physical
|
||||
Effect::Stun => Some(Colour::Red),
|
||||
@ -192,18 +227,14 @@ impl Effect {
|
||||
// effects over time
|
||||
Effect::Triage => Some(Colour::Green),
|
||||
Effect::Decay => Some(Colour::Blue),
|
||||
Effect::Regen => Some(Colour::Green),
|
||||
Effect::Siphon => Some(Colour::Blue),
|
||||
Effect::Pure => Some(Colour::Green),
|
||||
|
||||
Effect::Triaged => None,
|
||||
Effect::Decayed => None,
|
||||
Effect::Siphoned => None,
|
||||
Effect::Countered => None,
|
||||
Effect::Ko => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||
pub enum Colour {
|
||||
Red,
|
||||
Blue,
|
||||
Green,
|
||||
}*/
|
||||
}
|
||||
2514
core/src/game.rs
Normal file
2514
core/src/game.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,8 @@
|
||||
use std::fs::File;
|
||||
|
||||
use std::collections::{HashMap};
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use serde_cbor::{from_slice, to_vec};
|
||||
|
||||
use postgres::transaction::Transaction;
|
||||
|
||||
use failure::Error;
|
||||
use failure::err_msg;
|
||||
|
||||
@ -14,16 +10,11 @@ use failure::err_msg;
|
||||
use chrono::prelude::*;
|
||||
use chrono::Duration;
|
||||
|
||||
use account::Account;
|
||||
use player::{Player, Score};
|
||||
use game::{Game};
|
||||
use item::{Item};
|
||||
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)]
|
||||
enum InstancePhase {
|
||||
@ -107,14 +98,14 @@ pub struct Instance {
|
||||
time_control: TimeControl,
|
||||
|
||||
phase: InstancePhase,
|
||||
phase_end: Option<DateTime<Utc>>,
|
||||
phase_start: DateTime<Utc>,
|
||||
pub phase_end: Option<DateTime<Utc>>,
|
||||
pub phase_start: DateTime<Utc>,
|
||||
|
||||
winner: Option<Uuid>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
fn new() -> Instance {
|
||||
pub fn new() -> Instance {
|
||||
Instance {
|
||||
id: Uuid::new_v4(),
|
||||
players: vec![],
|
||||
@ -152,8 +143,8 @@ impl Instance {
|
||||
.collect::<Vec<Uuid>>()
|
||||
}
|
||||
|
||||
// time out lobbies that have been open too long
|
||||
pub fn upkeep(mut self) -> (Instance, Option<Game>) {
|
||||
// time out lobbies that have been open too long
|
||||
if self.phase == InstancePhase::Lobby && self.phase_timed_out() {
|
||||
self.finish();
|
||||
return (self, None);
|
||||
@ -178,7 +169,7 @@ impl Instance {
|
||||
(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 {
|
||||
return Err(err_msg("name must have a length"));
|
||||
}
|
||||
@ -187,12 +178,12 @@ impl Instance {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return Err(err_msg("game full"))
|
||||
}
|
||||
@ -206,7 +197,7 @@ impl Instance {
|
||||
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) {
|
||||
return Err(err_msg("instance not in start or vbox phase"));
|
||||
}
|
||||
@ -308,7 +299,7 @@ impl Instance {
|
||||
self.next_round()
|
||||
}
|
||||
|
||||
fn next_round(&mut self) -> &mut Instance {
|
||||
pub fn next_round(&mut self) -> &mut Instance {
|
||||
if self.finish_condition() {
|
||||
return self.finish();
|
||||
}
|
||||
@ -350,7 +341,7 @@ impl Instance {
|
||||
self
|
||||
}
|
||||
|
||||
fn finished(&self) -> bool {
|
||||
pub fn finished(&self) -> bool {
|
||||
self.phase == InstancePhase::Finished
|
||||
}
|
||||
|
||||
@ -378,7 +369,7 @@ impl Instance {
|
||||
self
|
||||
}
|
||||
|
||||
fn current_game_id(&self) -> Option<Uuid> {
|
||||
pub fn current_game_id(&self) -> Option<Uuid> {
|
||||
if self.phase != InstancePhase::InProgress {
|
||||
return None;
|
||||
}
|
||||
@ -394,7 +385,7 @@ impl Instance {
|
||||
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
|
||||
.iter_mut()
|
||||
@ -436,14 +427,14 @@ impl Instance {
|
||||
}
|
||||
|
||||
// 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
|
||||
.iter_mut()
|
||||
.find(|p| p.id == account)
|
||||
.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
|
||||
.iter_mut()
|
||||
.find(|p| p.id != account)
|
||||
@ -501,334 +492,18 @@ impl Instance {
|
||||
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.account_player(account)?
|
||||
.vbox_unequip(target, construct_id, target_construct_id)?;
|
||||
.vbox_unequip(target, construct_id, target_construct)?;
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use mob::{bot_player, instance_mobs};
|
||||
|
||||
#[test]
|
||||
fn instance_pve_test() {
|
||||
@ -854,7 +529,7 @@ mod tests {
|
||||
let _instance = Instance::new();
|
||||
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);
|
||||
let _player = Player::new(player_account, None, &"test".to_string(), constructs).set_bot(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -865,7 +540,7 @@ mod tests {
|
||||
|
||||
let player_account = Uuid::new_v4();
|
||||
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;
|
||||
|
||||
instance.add_player(player).expect("could not add player");
|
||||
@ -873,7 +548,7 @@ mod tests {
|
||||
|
||||
let player_account = Uuid::new_v4();
|
||||
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;
|
||||
|
||||
instance.add_player(player).expect("could not add player");
|
||||
@ -902,7 +577,7 @@ mod tests {
|
||||
|
||||
let player_account = Uuid::new_v4();
|
||||
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;
|
||||
|
||||
instance.add_player(player).expect("could not add player");
|
||||
@ -910,7 +585,7 @@ mod tests {
|
||||
|
||||
let player_account = Uuid::new_v4();
|
||||
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;
|
||||
instance.add_player(player).expect("could not add player");
|
||||
|
||||
@ -942,7 +617,7 @@ mod tests {
|
||||
|
||||
let player_account = Uuid::new_v4();
|
||||
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;
|
||||
|
||||
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
Loading…
x
Reference in New Issue
Block a user