core init
This commit is contained in:
parent
677cec24a2
commit
4475887410
142
COMBOS.md
142
COMBOS.md
@ -1,142 +0,0 @@
|
|||||||
# item_info ->
|
|
||||||
|
|
||||||
combos [strike, [R R Attack]]
|
|
||||||
specs [spec [bonus amount, [r g b]]
|
|
||||||
|
|
||||||
# Playthrough
|
|
||||||
|
|
||||||
constructs join game
|
|
||||||
stats randomised
|
|
||||||
|
|
||||||
initial stash drops
|
|
||||||
6 skills
|
|
||||||
6 colours
|
|
||||||
6 specs
|
|
||||||
|
|
||||||
play first round
|
|
||||||
basically duke it out
|
|
||||||
|
|
||||||
# Colours #
|
|
||||||
|
|
||||||
### Red ###
|
|
||||||
Real world concepts
|
|
||||||
Aggressive
|
|
||||||
Apply Buffs
|
|
||||||
Fast & Chaotic
|
|
||||||
|
|
||||||
### Green ###
|
|
||||||
Healing Specialisation
|
|
||||||
Defensive
|
|
||||||
Purge buffs & debuffs
|
|
||||||
|
|
||||||
### Blue ###
|
|
||||||
Fantasy concepts (magical)
|
|
||||||
Aggressive & Defensive
|
|
||||||
Apply Debuffs
|
|
||||||
Slow & Reliable
|
|
||||||
|
|
||||||
# Classes #
|
|
||||||
|
|
||||||
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`
|
|
||||||
|
|
||||||
|
|
||||||
Skills
|
|
||||||
==========
|
|
||||||
|
|
||||||
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`
|
|
||||||
|
|
||||||
# Attack Base #
|
|
||||||
|
|
||||||
RR - Strike
|
|
||||||
GG - Heal
|
|
||||||
BB - Blast
|
|
||||||
RG - Purify
|
|
||||||
GB - Decay
|
|
||||||
RB - Blast
|
|
||||||
|
|
||||||
# Stun Base #
|
|
||||||
|
|
||||||
RR - Strangle
|
|
||||||
GG - Break
|
|
||||||
BB - Ruin
|
|
||||||
RG - Banish
|
|
||||||
GB - Silence
|
|
||||||
RB - Hex
|
|
||||||
|
|
||||||
# Buff Base #
|
|
||||||
|
|
||||||
RR - Empower
|
|
||||||
GR - Triage
|
|
||||||
BB - Absorb
|
|
||||||
RG - Sustain
|
|
||||||
GB - Amplify
|
|
||||||
RB - Haste
|
|
||||||
|
|
||||||
# Debuff Base #
|
|
||||||
|
|
||||||
RR - Restrict
|
|
||||||
GG - Purge
|
|
||||||
BB - Curse
|
|
||||||
RG - Slow
|
|
||||||
GB - Siphon
|
|
||||||
RB - Invert
|
|
||||||
|
|
||||||
# Block Base #
|
|
||||||
|
|
||||||
RR - Counter
|
|
||||||
GG - Reflect
|
|
||||||
BB - Electrify
|
|
||||||
RG - Intercept
|
|
||||||
GB - Life `rename?`
|
|
||||||
RB - Recharge
|
|
||||||
|
|
||||||
|
|
||||||
## Advanced combos ##
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
### T2 / T3 Combos ###
|
|
||||||
|
|
||||||
All current specs / items can be further combo'd into T2 and T3 versions
|
|
||||||
|
|
||||||
# 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`
|
|
||||||
|
|
||||||
Upgraded skills will have a combination of higher damage / longer duration / reduced cooldown
|
|
||||||
Upgraded skills use the same speed formula as previously
|
|
||||||
|
|
||||||
### Spec / Skill hybrid specs ###
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
etc etc
|
|
||||||
|
|
||||||
30 skills * 3 specs => 90 spec / skill hybrid specs -> might be overcomplicated
|
|
||||||
|
|
||||||
|
|
||||||
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 #
|
|
||||||
63
ROADMAP.md
63
ROADMAP.md
@ -1,63 +0,0 @@
|
|||||||
|
|
||||||
### Road Map ###
|
|
||||||
|
|
||||||
# NOW Phase 1 (Dev -> Alpha)
|
|
||||||
|
|
||||||
Form company structure
|
|
||||||
Brainstorm Names?
|
|
||||||
Finalise documents
|
|
||||||
|
|
||||||
Game
|
|
||||||
Server T2 / T3 / Custom Specs
|
|
||||||
Any other outstanding "major" features ???
|
|
||||||
|
|
||||||
# Phase 2 (Alpha -> Beta)
|
|
||||||
|
|
||||||
Friends / Word of mouth testing
|
|
||||||
Server balance adjustments based on data
|
|
||||||
Client improvements based on feedback
|
|
||||||
|
|
||||||
Combat animations
|
|
||||||
|
|
||||||
Make in game shop
|
|
||||||
Payment processors / CC etc
|
|
||||||
Handler for game purchases
|
|
||||||
MTX - Construct Avatars
|
|
||||||
MTX - Skill anims
|
|
||||||
|
|
||||||
Setup company bank accounts
|
|
||||||
Accounting system - Xero etc
|
|
||||||
|
|
||||||
# Phase 3 (Beta -> Release)
|
|
||||||
|
|
||||||
Player Events e.g. chatwheel
|
|
||||||
Matchmaking + ELO / Leaderboard
|
|
||||||
Game skill private fields
|
|
||||||
|
|
||||||
Refine artwork, icons, scaling etc
|
|
||||||
Music
|
|
||||||
|
|
||||||
Marketing materials
|
|
||||||
Videos
|
|
||||||
Twitch
|
|
||||||
Advertisments?
|
|
||||||
Information
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# china shit
|
|
||||||
You need to read the details more carefully. Playsaurus messed up:
|
|
||||||
|
|
||||||
1. They launched in China without registering a trademark
|
|
||||||
|
|
||||||
2. A competitor registered the trademark after 3 months of their launch
|
|
||||||
|
|
||||||
3. They continued to sell for 4 years under a name trademarked by another company, making $73,000+ yearly from that one country
|
|
||||||
|
|
||||||
Now, they complain about it on Reddit, even though China is a 'First to File' company.
|
|
||||||
|
|
||||||
The fault lies entirely with Playsaurus, nothing illegal occurred here.
|
|
||||||
|
|
||||||
https://www.trademarknow.com/blog/first-to-file-versus-first...
|
|
||||||
|
|
||||||
This situation could have occurred in many other countries - the difference is probably that the competitor is content to just sell in China, under a Chinese name, whereas products sold anywhere else would need to use the English name.
|
|
||||||
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
|
|
||||||
43
WORKLOG.md
43
WORKLOG.md
@ -2,31 +2,19 @@
|
|||||||
## NOW
|
## NOW
|
||||||
|
|
||||||
_ntr_
|
_ntr_
|
||||||
* can't reset password without knowing password =\
|
* effects rework
|
||||||
|
|
||||||
|
Siphon = [
|
||||||
|
Apply(Siphon(2T), target)
|
||||||
|
Apply(Siphoning(2T), source)
|
||||||
|
Skill(SiphonTick, source, target)
|
||||||
|
DamageBlue(50% BluePower, target),
|
||||||
|
]
|
||||||
|
|
||||||
* change cooldowns to delay & recharge
|
* change cooldowns to delay & recharge
|
||||||
- delay is cooldown before skill can first be used
|
- delay is cooldown before skill can first be used
|
||||||
- recharge is cooldown after using skill
|
- recharge is cooldown after using skill
|
||||||
- every x speed reduces delay of skills
|
- every x speed reduces delay of skills
|
||||||
* audio
|
|
||||||
* animation effects
|
|
||||||
* vbox combine / buy / equip etc
|
|
||||||
* background music
|
|
||||||
* effects rework
|
|
||||||
|
|
||||||
Siphon =
|
|
||||||
[
|
|
||||||
DamageBlue(50%),
|
|
||||||
Apply(
|
|
||||||
Siphon(2T)
|
|
||||||
- Siphoning(2T)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
Hexagon Set
|
|
||||||
- Pick Colour
|
|
||||||
- Random Walk
|
|
||||||
- Draw hex
|
|
||||||
- Increase intensity for each visit
|
|
||||||
|
|
||||||
_mashy_
|
_mashy_
|
||||||
* represent construct colours during game phase (try %bar or dots)
|
* represent construct colours during game phase (try %bar or dots)
|
||||||
@ -50,6 +38,19 @@ _tba_
|
|||||||
|
|
||||||
## SOON
|
## SOON
|
||||||
|
|
||||||
|
* can't reset password without knowing password =\
|
||||||
|
|
||||||
|
* audio
|
||||||
|
* animation effects
|
||||||
|
* vbox combine / buy / equip etc
|
||||||
|
* background music
|
||||||
|
|
||||||
|
Hexagon Set
|
||||||
|
- Pick Colour
|
||||||
|
- Random Walk
|
||||||
|
- Draw hex
|
||||||
|
- Increase intensity for each visit
|
||||||
|
|
||||||
* combo rework
|
* combo rework
|
||||||
- reduce number of items for creating t2/t3 items from 3 -> 2
|
- reduce number of items for creating t2/t3 items from 3 -> 2
|
||||||
- add lost complexity by adding skill spec items
|
- add lost complexity by adding skill spec items
|
||||||
|
|||||||
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
|
||||||
17
core/Cargo.toml
Normal file
17
core/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "mnml-core"
|
||||||
|
version = "1.10.0"
|
||||||
|
authors = ["ntr <ntr@smokestack.io>", "mashy <mashy@mnml.gg>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "1"
|
||||||
|
serde_derive = "1"
|
||||||
|
|
||||||
|
rand = "0.6"
|
||||||
|
uuid = { version = "0.5", features = ["serde", "v4"] }
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
bcrypt = "0.2"
|
||||||
|
|
||||||
|
failure = "0.1"
|
||||||
|
|
||||||
|
log = "0.4"
|
||||||
1009
core/src/construct.rs
Normal file
1009
core/src/construct.rs
Normal file
File diff suppressed because it is too large
Load Diff
209
core/src/effect.rs
Normal file
209
core/src/effect.rs
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
use construct::{Stat, EffectMeta};
|
||||||
|
use skill::{Skill};
|
||||||
|
use util::{IntPct};
|
||||||
|
|
||||||
|
pub type Cooldown = Option<u8>;
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||||
|
pub enum Effect {
|
||||||
|
Amplify,
|
||||||
|
Banish,
|
||||||
|
Block,
|
||||||
|
Buff,
|
||||||
|
Counter,
|
||||||
|
Curse,
|
||||||
|
Haste,
|
||||||
|
Hybrid,
|
||||||
|
Intercept,
|
||||||
|
Invert,
|
||||||
|
Pure,
|
||||||
|
Purge,
|
||||||
|
Reflect,
|
||||||
|
Restrict,
|
||||||
|
Silence,
|
||||||
|
Slow,
|
||||||
|
Stun,
|
||||||
|
Sustain,
|
||||||
|
Vulnerable,
|
||||||
|
Wither, // Reduce green dmg (healing) taken
|
||||||
|
|
||||||
|
// electric is the buff that applies
|
||||||
|
// electrocute the dmg debuff
|
||||||
|
Electric,
|
||||||
|
Electrocute,
|
||||||
|
|
||||||
|
// absorbtion is the buff
|
||||||
|
// absorb is the increased damage
|
||||||
|
Absorb,
|
||||||
|
Absorption,
|
||||||
|
|
||||||
|
// magic immunity
|
||||||
|
|
||||||
|
// effects over time
|
||||||
|
Triage,
|
||||||
|
Decay,
|
||||||
|
Regen,
|
||||||
|
Siphon,
|
||||||
|
|
||||||
|
// Airborne,
|
||||||
|
// Boost
|
||||||
|
// Bleed,
|
||||||
|
// Blind,
|
||||||
|
// Deadly,
|
||||||
|
// Enslave,
|
||||||
|
// Fury,
|
||||||
|
// Injured,
|
||||||
|
// Leech,
|
||||||
|
// Mesmerise,
|
||||||
|
// Untouchable,
|
||||||
|
// SpeedSiphon,
|
||||||
|
// SpeedIncrease,
|
||||||
|
|
||||||
|
Ko,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
].contains(&skill),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disables_skill(&self, skill: Skill) -> bool {
|
||||||
|
if skill.is_tick() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Effect::Stun => true,
|
||||||
|
Effect::Banish => true,
|
||||||
|
Effect::Silence => skill.colours().contains(&Colour::Blue),
|
||||||
|
Effect::Restrict => skill.colours().contains(&Colour::Red),
|
||||||
|
Effect::Purge => skill.colours().contains(&Colour::Green),
|
||||||
|
Effect::Ko => skill.ko_castable(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modifications(&self) -> Vec<Stat> {
|
||||||
|
match self {
|
||||||
|
// Bases
|
||||||
|
Effect::Block => vec![Stat::RedDamageTaken, Stat::BlueDamageTaken],
|
||||||
|
Effect::Buff => vec![Stat::BluePower, Stat::RedPower, Stat::Speed],
|
||||||
|
Effect::Slow => vec![Stat::Speed],
|
||||||
|
|
||||||
|
// Power changes
|
||||||
|
Effect::Absorption => vec![Stat::RedPower, Stat::BluePower],
|
||||||
|
Effect::Amplify => vec![Stat::RedPower, Stat::BluePower],
|
||||||
|
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
|
||||||
|
|
||||||
|
// Speed
|
||||||
|
Effect::Haste => vec![Stat::Speed],
|
||||||
|
|
||||||
|
_ => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply(&self, value: u64, meta: Option<EffectMeta>) -> u64 {
|
||||||
|
match self {
|
||||||
|
Effect::Amplify |
|
||||||
|
Effect::Vulnerable |
|
||||||
|
Effect::Block |
|
||||||
|
Effect::Buff |
|
||||||
|
Effect::Curse |
|
||||||
|
Effect::Haste |
|
||||||
|
Effect::Slow |
|
||||||
|
Effect::Hybrid |
|
||||||
|
Effect::Pure |
|
||||||
|
Effect::Wither => value.pct(match meta {
|
||||||
|
Some(EffectMeta::Multiplier(d)) => d,
|
||||||
|
_ => 100,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Effect::Absorption => value + match meta {
|
||||||
|
Some(EffectMeta::AddedDamage(d)) => d,
|
||||||
|
_ => {
|
||||||
|
warn!("absorb meta not damage");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
warn!("{:?} does not have a mod effect", self);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn colour(&self) -> Option<Colour> {
|
||||||
|
match self {
|
||||||
|
// physical
|
||||||
|
Effect::Stun => Some(Colour::Red),
|
||||||
|
Effect::Block => Some(Colour::Green),
|
||||||
|
Effect::Buff => Some(Colour::Green),
|
||||||
|
Effect::Counter => Some(Colour::Green),
|
||||||
|
Effect::Vulnerable => Some(Colour::Red),
|
||||||
|
Effect::Restrict => Some(Colour::Red),
|
||||||
|
Effect::Sustain => Some(Colour::Green),
|
||||||
|
Effect::Intercept => Some(Colour::Green),
|
||||||
|
|
||||||
|
// magic
|
||||||
|
Effect::Curse => Some(Colour::Blue),
|
||||||
|
Effect::Banish => None,
|
||||||
|
// Effect::Banish => rng.gen_bool(0.5),
|
||||||
|
|
||||||
|
Effect::Slow => Some(Colour::Blue),
|
||||||
|
Effect::Haste => Some(Colour::Green),
|
||||||
|
Effect::Absorption => Some(Colour::Green),
|
||||||
|
Effect::Reflect => Some(Colour::Green),
|
||||||
|
Effect::Amplify => Some(Colour::Green),
|
||||||
|
Effect::Silence => Some(Colour::Blue),
|
||||||
|
Effect::Wither => Some(Colour::Blue),
|
||||||
|
Effect::Purge => Some(Colour::Blue),
|
||||||
|
|
||||||
|
Effect::Electric => Some(Colour::Green),
|
||||||
|
Effect::Electrocute => Some(Colour::Blue),
|
||||||
|
|
||||||
|
Effect::Absorb => Some(Colour::Green),
|
||||||
|
|
||||||
|
// magic
|
||||||
|
Effect::Hybrid => Some(Colour::Green),
|
||||||
|
Effect::Invert => Some(Colour::Green),
|
||||||
|
|
||||||
|
// 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::Ko => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||||
|
pub enum Colour {
|
||||||
|
Red,
|
||||||
|
Blue,
|
||||||
|
Green,
|
||||||
|
}
|
||||||
1315
core/src/game.rs
Normal file
1315
core/src/game.rs
Normal file
File diff suppressed because it is too large
Load Diff
631
core/src/instance.rs
Normal file
631
core/src/instance.rs
Normal file
@ -0,0 +1,631 @@
|
|||||||
|
|
||||||
|
use std::collections::{HashMap};
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
use failure::err_msg;
|
||||||
|
|
||||||
|
// timekeeping
|
||||||
|
use chrono::prelude::*;
|
||||||
|
use chrono::Duration;
|
||||||
|
|
||||||
|
use player::{Player, Score};
|
||||||
|
use mob::{bot_player, instance_mobs};
|
||||||
|
use game::{Game};
|
||||||
|
use item::{Item};
|
||||||
|
use vbox;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||||
|
enum InstancePhase {
|
||||||
|
Lobby,
|
||||||
|
InProgress,
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ChatState = HashMap<Uuid, String>;
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
|
struct Round {
|
||||||
|
game_id: Option<Uuid>,
|
||||||
|
finished: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Round {
|
||||||
|
fn new() -> Round {
|
||||||
|
Round { game_id: None, finished: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy,Serialize,Deserialize)]
|
||||||
|
pub enum TimeControl {
|
||||||
|
Standard,
|
||||||
|
Slow,
|
||||||
|
Practice,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeControl {
|
||||||
|
fn vbox_time_seconds(&self) -> i64 {
|
||||||
|
match self {
|
||||||
|
TimeControl::Standard => 180,
|
||||||
|
TimeControl::Slow => 240,
|
||||||
|
TimeControl::Practice => panic!("practice vbox seconds called"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn game_time_seconds(&self) -> i64 {
|
||||||
|
match self {
|
||||||
|
TimeControl::Standard => 60,
|
||||||
|
TimeControl::Slow => 120,
|
||||||
|
TimeControl::Practice => panic!("practice game seconds called"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_phase_end(&self) -> Option<DateTime<Utc>> {
|
||||||
|
match self {
|
||||||
|
TimeControl::Practice => None,
|
||||||
|
_ => Some(Utc::now()
|
||||||
|
.checked_add_signed(Duration::seconds(self.vbox_time_seconds()))
|
||||||
|
.expect("could not set vbox phase end")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lobby_timeout(&self) -> DateTime<Utc> {
|
||||||
|
Utc::now()
|
||||||
|
.checked_add_signed(Duration::seconds(15))
|
||||||
|
.expect("could not set phase end")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_phase_end(&self, resolution_time_ms: i64) -> Option<DateTime<Utc>> {
|
||||||
|
match self {
|
||||||
|
TimeControl::Practice => None,
|
||||||
|
_ => Some(Utc::now()
|
||||||
|
.checked_add_signed(Duration::milliseconds(self.game_time_seconds() * 1000 + resolution_time_ms))
|
||||||
|
.expect("could not set game phase end")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
|
pub struct Instance {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
players: Vec<Player>,
|
||||||
|
rounds: Vec<Round>,
|
||||||
|
|
||||||
|
max_players: usize,
|
||||||
|
time_control: TimeControl,
|
||||||
|
|
||||||
|
phase: InstancePhase,
|
||||||
|
phase_end: Option<DateTime<Utc>>,
|
||||||
|
phase_start: DateTime<Utc>,
|
||||||
|
|
||||||
|
winner: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance {
|
||||||
|
fn new() -> Instance {
|
||||||
|
Instance {
|
||||||
|
id: Uuid::new_v4(),
|
||||||
|
players: vec![],
|
||||||
|
rounds: vec![],
|
||||||
|
phase: InstancePhase::Lobby,
|
||||||
|
max_players: 2,
|
||||||
|
name: String::new(),
|
||||||
|
time_control: TimeControl::Standard,
|
||||||
|
phase_start: Utc::now(),
|
||||||
|
phase_end: Some(TimeControl::Standard.lobby_timeout()),
|
||||||
|
winner: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redact(mut self, account: Uuid) -> Instance {
|
||||||
|
self.players = self.players.into_iter()
|
||||||
|
.map(|p| p.redact(account))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phase_timed_out(&self) -> bool {
|
||||||
|
match self.phase_end {
|
||||||
|
Some(t) => Utc::now().signed_duration_since(t).num_milliseconds() > 0,
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timed_out_players(&self) -> Vec<Uuid> {
|
||||||
|
self.players
|
||||||
|
.iter()
|
||||||
|
.filter(|p| !p.ready)
|
||||||
|
.map(|p| p.id)
|
||||||
|
.collect::<Vec<Uuid>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.phase != InstancePhase::InProgress {
|
||||||
|
return (self, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.phase_timed_out() {
|
||||||
|
return (self, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_game = self
|
||||||
|
.timed_out_players()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|p| self.player_ready(*p).unwrap())
|
||||||
|
.collect::<Vec<Game>>()
|
||||||
|
.into_iter()
|
||||||
|
.next();
|
||||||
|
|
||||||
|
(self, new_game)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_name(mut self, name: String) -> Result<Instance, Error> {
|
||||||
|
if name.len() == 0 {
|
||||||
|
return Err(err_msg("name must have a length"));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.name = name;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
if self.players.len() >= self.max_players {
|
||||||
|
return Err(err_msg("game full"))
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.players.iter().find(|p| p.id == player.id) {
|
||||||
|
Some(_p) => return Err(err_msg("already joined")),
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.players.push(player);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// LOBBY CHECKS
|
||||||
|
if self.phase == InstancePhase::Lobby {
|
||||||
|
let i = self.players
|
||||||
|
.iter_mut()
|
||||||
|
.position(|p| p.id == player_id)
|
||||||
|
.ok_or(err_msg("player_id not found"))?;
|
||||||
|
|
||||||
|
let v = !self.players[i].ready;
|
||||||
|
self.players[i].set_ready(v);
|
||||||
|
|
||||||
|
match self.can_start() {
|
||||||
|
true => {
|
||||||
|
self.start();
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
false => return Ok(None),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// GAME PHASE READY
|
||||||
|
let i = self.players
|
||||||
|
.iter_mut()
|
||||||
|
.position(|p| p.id == player_id)
|
||||||
|
.ok_or(err_msg("player_id not found"))?;
|
||||||
|
|
||||||
|
let v = !self.players[i].ready;
|
||||||
|
self.players[i].set_ready(v);
|
||||||
|
|
||||||
|
// start the game even if afk noobs have no skills
|
||||||
|
if !self.phase_timed_out() && self.players[i].constructs.iter().all(|c| c.skills.len() == 0) {
|
||||||
|
return Err(err_msg("your constructs have no skills"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a game object if both players are ready
|
||||||
|
// this should only happen once
|
||||||
|
|
||||||
|
let all_ready = self.round_ready_check();
|
||||||
|
|
||||||
|
if !all_ready {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let game = self.create_round_game();
|
||||||
|
|
||||||
|
let current_round = self.rounds
|
||||||
|
.last_mut()
|
||||||
|
.expect("instance does not have any rounds");
|
||||||
|
|
||||||
|
current_round.game_id = Some(game.id);
|
||||||
|
|
||||||
|
return Ok(Some(game));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round_ready_check(&mut self) -> bool {
|
||||||
|
self.players
|
||||||
|
.iter()
|
||||||
|
.all(|p| p.ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe just embed the games in the instance
|
||||||
|
// but seems hella inefficient
|
||||||
|
fn create_round_game(&mut self) -> Game {
|
||||||
|
let current_round = self.rounds
|
||||||
|
.last_mut()
|
||||||
|
.expect("instance does not have any rounds");
|
||||||
|
|
||||||
|
|
||||||
|
let mut game = Game::new();
|
||||||
|
current_round.game_id = Some(game.id);
|
||||||
|
|
||||||
|
// disable upkeep until players finish their game
|
||||||
|
self.phase_end = None;
|
||||||
|
|
||||||
|
game
|
||||||
|
.set_player_num(2)
|
||||||
|
.set_player_constructs(3)
|
||||||
|
.set_time_control(self.time_control)
|
||||||
|
.set_instance(self.id);
|
||||||
|
|
||||||
|
for player in self.players.clone().into_iter() {
|
||||||
|
game.player_add(player).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(game.can_start());
|
||||||
|
return game.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_start(&self) -> bool {
|
||||||
|
self.players.len() == self.max_players && self.all_ready()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(&mut self) -> &mut Instance {
|
||||||
|
// self.players.sort_unstable_by_key(|p| p.id);
|
||||||
|
self.next_round()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_round(&mut self) -> &mut Instance {
|
||||||
|
if self.finish_condition() {
|
||||||
|
return self.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.phase = InstancePhase::InProgress;
|
||||||
|
self.phase_start = Utc::now();
|
||||||
|
self.phase_end = self.time_control.vbox_phase_end();
|
||||||
|
|
||||||
|
let bits = match self.rounds.len() > 0 {
|
||||||
|
true => 30,
|
||||||
|
false => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.players.iter_mut().for_each(|p| {
|
||||||
|
p.vbox.balance_add(bits);
|
||||||
|
p.set_ready(false);
|
||||||
|
p.vbox.fill();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.rounds.push(Round::new());
|
||||||
|
self.bot_round_actions();
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_condition(&mut self) -> bool {
|
||||||
|
self.players.iter().any(|p| p.score == Score::Win)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(&mut self) -> &mut Instance {
|
||||||
|
self.phase = InstancePhase::Finished;
|
||||||
|
|
||||||
|
for player in self.players.iter() {
|
||||||
|
if player.score == Score::Win {
|
||||||
|
self.winner = Some(player.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finished(&self) -> bool {
|
||||||
|
self.phase == InstancePhase::Finished
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bot_round_actions(&mut self) -> &mut Instance {
|
||||||
|
for bot in self.players.iter_mut().filter(|p| p.bot) {
|
||||||
|
bot.vbox.fill();
|
||||||
|
bot.autobuy();
|
||||||
|
}
|
||||||
|
|
||||||
|
let games = self.players
|
||||||
|
.clone()
|
||||||
|
.iter()
|
||||||
|
.filter(|b| b.bot)
|
||||||
|
.filter_map(|b| self.player_ready(b.id).unwrap())
|
||||||
|
.collect::<Vec<Game>>();
|
||||||
|
|
||||||
|
for game in games {
|
||||||
|
if game.finished() {
|
||||||
|
self.game_finished(&game).unwrap();
|
||||||
|
} else {
|
||||||
|
info!("{:?} unfishededes", game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_game_id(&self) -> Option<Uuid> {
|
||||||
|
if self.phase != InstancePhase::InProgress {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_round = self.rounds
|
||||||
|
.last()
|
||||||
|
.expect("instance does not have any rounds");
|
||||||
|
|
||||||
|
if current_round.finished || current_round.game_id.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_round.game_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> {
|
||||||
|
{
|
||||||
|
let current_round = self.rounds
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|r| r.game_id.is_some())
|
||||||
|
.find(|r| r.game_id.unwrap() == game.id);
|
||||||
|
|
||||||
|
match current_round {
|
||||||
|
Some(c) => c.finished = true,
|
||||||
|
None => return Err(err_msg("instance does not have a round for this game")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you don't win, you lose
|
||||||
|
// ties can happen if both players agree to a draw
|
||||||
|
// or ticks fire and knock everybody out
|
||||||
|
if let Some(winner) = game.winner() {
|
||||||
|
let winner = self.players.iter_mut()
|
||||||
|
.find(|p| p.id == winner.id)
|
||||||
|
.unwrap();
|
||||||
|
winner.score = winner.score.add_win(&Score::Zero);
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.all_games_finished() {
|
||||||
|
self.next_round();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_ready(&self) -> bool {
|
||||||
|
self.players.iter().all(|p| p.ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_games_finished(&self) -> bool {
|
||||||
|
match self.rounds.last() {
|
||||||
|
Some(r) => r.finished,
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PLAYER ACTIONS
|
||||||
|
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> {
|
||||||
|
self.players
|
||||||
|
.iter_mut()
|
||||||
|
.find(|p| p.id != account)
|
||||||
|
.ok_or(err_msg("opponent not in instance"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_action_allowed(&self, account: Uuid) -> Result<(), Error> {
|
||||||
|
if self.players.iter().find(|p| p.id == account).is_none() {
|
||||||
|
return Err(err_msg("player not in this instance"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.phase == InstancePhase::Lobby {
|
||||||
|
return Err(err_msg("game not yet started"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.current_game_id().is_some() {
|
||||||
|
return Err(err_msg("you cannot perform vbox actions while in a game"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_refill(mut self, account: Uuid) -> Result<Instance, Error> {
|
||||||
|
self.vbox_action_allowed(account)?;
|
||||||
|
self.account_player(account)?
|
||||||
|
.vbox_refill()?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_buy(mut self, account: Uuid, group: vbox::ItemType, index: String, construct_id: Option<Uuid>) -> Result<Instance, Error> {
|
||||||
|
self.vbox_action_allowed(account)?;
|
||||||
|
self.account_player(account)?
|
||||||
|
.vbox_buy(group, index, construct_id)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_combine(mut self, account: Uuid, inv_indices: Vec<String>, vbox_indices: vbox::VboxIndices) -> Result<Instance, Error> {
|
||||||
|
self.vbox_action_allowed(account)?;
|
||||||
|
self.account_player(account)?
|
||||||
|
.vbox_combine(inv_indices, vbox_indices)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_refund(mut self, account: Uuid, index: String) -> Result<Instance, Error> {
|
||||||
|
self.vbox_action_allowed(account)?;
|
||||||
|
self.account_player(account)?
|
||||||
|
.vbox_refund(index)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_apply(mut self, account: Uuid, index: String, construct_id: Uuid) -> Result<Instance, Error> {
|
||||||
|
self.vbox_action_allowed(account)?;
|
||||||
|
self.account_player(account)?
|
||||||
|
.vbox_equip(index, construct_id)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_unequip(mut self, account: Uuid, target: Item, construct_id: Uuid, target_construct_id: Option<Uuid>) -> Result<Instance, Error> {
|
||||||
|
self.vbox_action_allowed(account)?;
|
||||||
|
self.account_player(account)?
|
||||||
|
.vbox_unequip(target, construct_id, target_construct_id)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instance_pve_test() {
|
||||||
|
let mut instance = Instance::new();
|
||||||
|
|
||||||
|
let bot = bot_player();
|
||||||
|
let bot_one = bot.id;
|
||||||
|
instance.add_player(bot).unwrap();
|
||||||
|
|
||||||
|
let bot = bot_player();
|
||||||
|
let bot_two = bot.id;
|
||||||
|
instance.add_player(bot).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(instance.phase, InstancePhase::Lobby);
|
||||||
|
instance.player_ready(bot_one).unwrap();
|
||||||
|
instance.player_ready(bot_two).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(instance.phase, InstancePhase::Finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instance_bot_vbox_test() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instance_start_test() {
|
||||||
|
let mut instance = Instance::new();
|
||||||
|
|
||||||
|
assert_eq!(instance.max_players, 2);
|
||||||
|
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let constructs = instance_mobs(player_account);
|
||||||
|
let player = Player::new(player_account, &"a".to_string(), constructs);
|
||||||
|
let a_id = player.id;
|
||||||
|
|
||||||
|
instance.add_player(player).expect("could not add player");
|
||||||
|
assert!(!instance.can_start());
|
||||||
|
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let constructs = instance_mobs(player_account);
|
||||||
|
let player = Player::new(player_account, &"b".to_string(), constructs);
|
||||||
|
let b_id = player.id;
|
||||||
|
|
||||||
|
instance.add_player(player).expect("could not add player");
|
||||||
|
|
||||||
|
assert_eq!(instance.phase, InstancePhase::Lobby);
|
||||||
|
instance.player_ready(a_id).expect("a ready");
|
||||||
|
assert!(!instance.can_start());
|
||||||
|
|
||||||
|
instance.player_ready(b_id).expect("b ready");
|
||||||
|
assert_eq!(instance.phase, InstancePhase::InProgress);
|
||||||
|
|
||||||
|
assert!(!instance.can_start());
|
||||||
|
|
||||||
|
instance.players[0].autobuy();
|
||||||
|
instance.players[1].autobuy();
|
||||||
|
|
||||||
|
instance.player_ready(a_id).expect("a ready");
|
||||||
|
let game = instance.player_ready(b_id).expect("b ready");
|
||||||
|
|
||||||
|
assert!(game.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instance_upkeep_test() {
|
||||||
|
let mut instance = Instance::new();
|
||||||
|
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let constructs = instance_mobs(player_account);
|
||||||
|
let player = Player::new(player_account, &"a".to_string(), constructs);
|
||||||
|
let a_id = player.id;
|
||||||
|
|
||||||
|
instance.add_player(player).expect("could not add player");
|
||||||
|
assert!(!instance.can_start());
|
||||||
|
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let constructs = instance_mobs(player_account);
|
||||||
|
let player = Player::new(player_account, &"b".to_string(), constructs);
|
||||||
|
let b_id = player.id;
|
||||||
|
instance.add_player(player).expect("could not add player");
|
||||||
|
|
||||||
|
instance.players[0].autobuy();
|
||||||
|
|
||||||
|
instance.player_ready(a_id).expect("a ready");
|
||||||
|
instance.player_ready(b_id).expect("b ready");
|
||||||
|
|
||||||
|
instance.phase_end = Some(Utc::now().checked_sub_signed(Duration::seconds(500)).unwrap());
|
||||||
|
|
||||||
|
let (mut instance, new_games) = instance.upkeep();
|
||||||
|
|
||||||
|
assert!(new_games.is_some());
|
||||||
|
|
||||||
|
let game = new_games.unwrap();
|
||||||
|
assert!(game.finished());
|
||||||
|
|
||||||
|
instance.game_finished(&game).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(instance.rounds.len(), 2);
|
||||||
|
assert!(instance.players.iter().all(|p| !p.ready));
|
||||||
|
|
||||||
|
// info!("{:#?}", instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instance_upkeep_idle_lobby_test() {
|
||||||
|
let mut instance = Instance::new();
|
||||||
|
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let constructs = instance_mobs(player_account);
|
||||||
|
let player = Player::new(player_account, &"a".to_string(), constructs);
|
||||||
|
let _a_id = player.id;
|
||||||
|
|
||||||
|
instance.add_player(player).expect("could not add player");
|
||||||
|
assert!(!instance.can_start());
|
||||||
|
|
||||||
|
instance.phase_end = Some(Utc::now().checked_sub_signed(Duration::minutes(61)).unwrap());
|
||||||
|
let (instance, _new_games) = instance.upkeep();
|
||||||
|
|
||||||
|
assert!(instance.finished());
|
||||||
|
}
|
||||||
|
}
|
||||||
1581
core/src/item.rs
Normal file
1581
core/src/item.rs
Normal file
File diff suppressed because it is too large
Load Diff
23
core/src/lib.rs
Normal file
23
core/src/lib.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
extern crate rand;
|
||||||
|
extern crate uuid;
|
||||||
|
extern crate bcrypt;
|
||||||
|
extern crate chrono;
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
|
#[macro_use] extern crate serde_derive;
|
||||||
|
#[macro_use] extern crate failure;
|
||||||
|
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
|
||||||
|
pub mod construct;
|
||||||
|
pub mod effect;
|
||||||
|
pub mod game;
|
||||||
|
pub mod instance;
|
||||||
|
pub mod item;
|
||||||
|
pub mod mob;
|
||||||
|
pub mod names;
|
||||||
|
pub mod player;
|
||||||
|
pub mod skill;
|
||||||
|
pub mod spec;
|
||||||
|
pub mod util;
|
||||||
|
pub mod vbox;
|
||||||
30
core/src/mob.rs
Normal file
30
core/src/mob.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use construct::{Construct};
|
||||||
|
use names::{name};
|
||||||
|
use player::{Player};
|
||||||
|
|
||||||
|
pub fn generate_mob() -> Construct {
|
||||||
|
let mob = Construct::new()
|
||||||
|
.named(&name());
|
||||||
|
|
||||||
|
return mob;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instance_mobs(player_id: Uuid) -> Vec<Construct> {
|
||||||
|
iter::repeat_with(||
|
||||||
|
generate_mob()
|
||||||
|
.set_account(player_id))
|
||||||
|
// .learn(Skill::Attack))
|
||||||
|
.take(3)
|
||||||
|
.collect::<Vec<Construct>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bot_player() -> Player {
|
||||||
|
let bot_id = Uuid::new_v4();
|
||||||
|
let constructs = instance_mobs(bot_id);
|
||||||
|
Player::new(bot_id, &name(), constructs).set_bot(true)
|
||||||
|
}
|
||||||
|
|
||||||
138
core/src/names.rs
Normal file
138
core/src/names.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use rand::prelude::*;
|
||||||
|
use rand::{thread_rng};
|
||||||
|
|
||||||
|
const FIRSTS: [&'static str; 53] = [
|
||||||
|
"artificial",
|
||||||
|
"ambient",
|
||||||
|
"borean",
|
||||||
|
"brewing",
|
||||||
|
"bristling",
|
||||||
|
"compressed",
|
||||||
|
"ceramic",
|
||||||
|
"chromatic",
|
||||||
|
"concave",
|
||||||
|
"convex",
|
||||||
|
"distorted",
|
||||||
|
"deserted",
|
||||||
|
"emotive",
|
||||||
|
"emotionless",
|
||||||
|
"elliptical",
|
||||||
|
"extrasolar",
|
||||||
|
"fierce",
|
||||||
|
"fossilised",
|
||||||
|
"frozen",
|
||||||
|
"gravitational",
|
||||||
|
"jovian",
|
||||||
|
"inverted",
|
||||||
|
"leafy",
|
||||||
|
"lurking",
|
||||||
|
"limitless",
|
||||||
|
"magnetic",
|
||||||
|
"metallic",
|
||||||
|
"mossy",
|
||||||
|
"mighty",
|
||||||
|
"modulated",
|
||||||
|
"nocturnal",
|
||||||
|
"noisy",
|
||||||
|
"nutritious",
|
||||||
|
"powerful",
|
||||||
|
"obscure",
|
||||||
|
"organic",
|
||||||
|
"oxygenated",
|
||||||
|
"oscillating",
|
||||||
|
"ossified",
|
||||||
|
"orbiting",
|
||||||
|
"piscine",
|
||||||
|
"polar",
|
||||||
|
"pure",
|
||||||
|
"recalcitrant",
|
||||||
|
"rogue",
|
||||||
|
"sealed",
|
||||||
|
"subversive",
|
||||||
|
"subterranean",
|
||||||
|
"supercooled",
|
||||||
|
"subsonic",
|
||||||
|
"synthetic",
|
||||||
|
"terrestrial",
|
||||||
|
"weary",
|
||||||
|
];
|
||||||
|
|
||||||
|
const LASTS: [&'static str; 63] = [
|
||||||
|
"artifact",
|
||||||
|
"assembly",
|
||||||
|
"antenna",
|
||||||
|
"alloy",
|
||||||
|
"carrier",
|
||||||
|
"carbon",
|
||||||
|
"console",
|
||||||
|
"construct",
|
||||||
|
"coordinates",
|
||||||
|
"craft",
|
||||||
|
"core",
|
||||||
|
"design",
|
||||||
|
"drone",
|
||||||
|
"distortion",
|
||||||
|
"detector",
|
||||||
|
"energy",
|
||||||
|
"entropy",
|
||||||
|
"exoplanet",
|
||||||
|
"foilage",
|
||||||
|
"forest",
|
||||||
|
"form",
|
||||||
|
"fossil",
|
||||||
|
"frequency",
|
||||||
|
"function",
|
||||||
|
"fusion",
|
||||||
|
"fission",
|
||||||
|
"information",
|
||||||
|
"insulator",
|
||||||
|
"layout",
|
||||||
|
"lifeform",
|
||||||
|
"liquid",
|
||||||
|
"landmass",
|
||||||
|
"lens",
|
||||||
|
"mass",
|
||||||
|
"mantle",
|
||||||
|
"magnetism",
|
||||||
|
"mechanism",
|
||||||
|
"mountain",
|
||||||
|
"nectar",
|
||||||
|
"nebula",
|
||||||
|
"oxide",
|
||||||
|
"orbit",
|
||||||
|
"pattern",
|
||||||
|
"plant",
|
||||||
|
"planet",
|
||||||
|
"poseidon",
|
||||||
|
"problem",
|
||||||
|
"receiver",
|
||||||
|
"replicant",
|
||||||
|
"river",
|
||||||
|
"satellite",
|
||||||
|
"scaffold",
|
||||||
|
"structure",
|
||||||
|
"shape",
|
||||||
|
"signal",
|
||||||
|
"synthesiser",
|
||||||
|
"system",
|
||||||
|
"tower",
|
||||||
|
"transmitter",
|
||||||
|
"traveller",
|
||||||
|
"vibration",
|
||||||
|
"warning",
|
||||||
|
"wildlife",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn name() -> String {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
let first = rng.gen_range(0, FIRSTS.len() - 1);
|
||||||
|
let last = rng.gen_range(0, LASTS.len() - 1);
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
s.push_str(FIRSTS[first]);
|
||||||
|
s.push(' ');
|
||||||
|
s.push_str(LASTS[last]);
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
442
core/src/player.rs
Normal file
442
core/src/player.rs
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
use std::collections::{HashMap};
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
use failure::err_msg;
|
||||||
|
|
||||||
|
use construct::{Construct, Colours};
|
||||||
|
use vbox::{Vbox, ItemType, VboxIndices};
|
||||||
|
use item::{Item, ItemEffect};
|
||||||
|
use effect::{Effect};
|
||||||
|
|
||||||
|
const DISCARD_COST: usize = 2;
|
||||||
|
|
||||||
|
#[derive(Debug,Copy,Clone,Serialize,Deserialize,Eq,PartialEq)]
|
||||||
|
pub enum Score {
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
Three,
|
||||||
|
Adv,
|
||||||
|
|
||||||
|
Win,
|
||||||
|
Lose,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Score {
|
||||||
|
pub fn add_win(self, _opp: &Score) -> Score {
|
||||||
|
match self {
|
||||||
|
Score::Zero => Score::One,
|
||||||
|
Score::One => Score::Two,
|
||||||
|
Score::Two => Score::Win,
|
||||||
|
// Tennis scoring
|
||||||
|
// Score::Three => match opp {
|
||||||
|
// Score::Adv => Score::Three,
|
||||||
|
// Score::Three => Score::Adv,
|
||||||
|
// _ => Score::Win,
|
||||||
|
// }
|
||||||
|
// Score::Adv => Score::Win,
|
||||||
|
|
||||||
|
_ => panic!("faulty score increment {:?}", self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_loss(self) -> Score {
|
||||||
|
match self {
|
||||||
|
// Score::Adv => Score::Three,
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
|
pub struct Player {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub img: Option<Uuid>,
|
||||||
|
pub name: String,
|
||||||
|
pub vbox: Vbox,
|
||||||
|
pub constructs: Vec<Construct>,
|
||||||
|
pub bot: bool,
|
||||||
|
pub ready: bool,
|
||||||
|
pub draw_offered: bool,
|
||||||
|
pub score: Score,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Player {
|
||||||
|
pub fn new(account: Uuid, name: &String, constructs: Vec<Construct>) -> Player {
|
||||||
|
Player {
|
||||||
|
id: account,
|
||||||
|
img: Some(account),
|
||||||
|
name: name.clone(),
|
||||||
|
vbox: Vbox::new(),
|
||||||
|
constructs,
|
||||||
|
bot: false,
|
||||||
|
ready: false,
|
||||||
|
draw_offered: false,
|
||||||
|
score: Score::Zero,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redact(mut self, account: Uuid) -> Player {
|
||||||
|
// all g
|
||||||
|
if account == self.id {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove vbox
|
||||||
|
self.vbox = Vbox::new();
|
||||||
|
|
||||||
|
// hide skills
|
||||||
|
for construct in self.constructs.iter_mut() {
|
||||||
|
construct.skills = vec![];
|
||||||
|
construct.specs = vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_bot(mut self, bot: bool) -> Player {
|
||||||
|
self.bot = bot;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ready(&mut self, ready: bool) -> &mut Player {
|
||||||
|
self.ready = ready;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forfeit(&mut self) -> &mut Player {
|
||||||
|
for construct in self.constructs.iter_mut() {
|
||||||
|
construct.force_ko();
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_win(&mut self) -> &mut Player {
|
||||||
|
self.score = Score::Win;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_lose(&mut self) -> &mut Player {
|
||||||
|
self.score = Score::Lose;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct_get(&mut self, id: Uuid) -> Result<&mut Construct, Error> {
|
||||||
|
self.constructs.iter_mut().find(|c| c.id == id).ok_or(err_msg("construct not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn autobuy(&mut self) -> &mut Player {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
// skill buying phase
|
||||||
|
while self.constructs.iter().any(|c| c.skills.len() < 3) {
|
||||||
|
// find the construct with the smallest number of skills
|
||||||
|
let construct_id = match self.constructs.iter().min_by_key(|c| c.skills.len()) {
|
||||||
|
None => panic!("no constructs in autobuy"),
|
||||||
|
Some(c) => c.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let i = self.vbox.stash.iter()
|
||||||
|
.find(|(_i, v)| v.into_skill().is_some())
|
||||||
|
.map(|(i, _v)| i.clone());
|
||||||
|
|
||||||
|
// got a skill in stash
|
||||||
|
if let Some(i) = i {
|
||||||
|
// AAAAAAAAAAAAAAAAAAAA
|
||||||
|
// there's a bad bug here where if this apply fails
|
||||||
|
// the item in question will be silently dropped
|
||||||
|
let item = self.vbox.stash.remove(&i).unwrap();
|
||||||
|
self.vbox_apply(item, construct_id).ok();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// need to buy one
|
||||||
|
else {
|
||||||
|
|
||||||
|
// do we have any colours in store?
|
||||||
|
let colours = self.vbox.store[&ItemType::Colours].keys()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
// how about a base skill?
|
||||||
|
let base = match self.vbox.store[&ItemType::Skills].iter().next() {
|
||||||
|
Some(b) => Some(b.0.clone()),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// if no: try to refill and start again
|
||||||
|
match colours.len() < 2 || base.is_none() {
|
||||||
|
true => match self.vbox_refill() {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break, // give up
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
let mut vbox_items = HashMap::new();
|
||||||
|
vbox_items.insert(ItemType::Colours, colours);
|
||||||
|
vbox_items.insert(ItemType::Skills, vec![base.unwrap()]);
|
||||||
|
|
||||||
|
match self.vbox_combine(vec![], Some(vbox_items)) {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break, // give up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec buying phase
|
||||||
|
while self.constructs.iter().any(|c| c.specs.len() < 3) {
|
||||||
|
// find the construct with the smallest number of skills
|
||||||
|
let construct_id = match self.constructs.iter().min_by_key(|c| c.specs.len()) {
|
||||||
|
None => panic!("no constructs in autobuy"),
|
||||||
|
Some(c) => c.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let i = self.vbox.stash.iter()
|
||||||
|
.find(|(_i, v)| v.into_spec().is_some())
|
||||||
|
.map(|(i, _v)| i.clone());
|
||||||
|
|
||||||
|
// got a skill in stash
|
||||||
|
if let Some(i) = i {
|
||||||
|
// AAAAAAAAAAAAAAAAAAAA
|
||||||
|
// there's a bad bug here where if this apply fails
|
||||||
|
// the item in question will be silently dropped
|
||||||
|
let item = self.vbox.stash.remove(&i).unwrap();
|
||||||
|
self.vbox_apply(item, construct_id).ok();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// need to buy one
|
||||||
|
else {
|
||||||
|
// do we have any colours in store?
|
||||||
|
let colours = self.vbox.store[&ItemType::Colours].keys()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
// how about a base spec?
|
||||||
|
let base = match self.vbox.store[&ItemType::Specs].iter().next() {
|
||||||
|
Some(b) => Some(b.0.clone()),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// if no: try to refill and start again
|
||||||
|
match colours.len() < 2 || base.is_none() {
|
||||||
|
true => match self.vbox_refill() {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break, // give up
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
let mut vbox_items = HashMap::new();
|
||||||
|
vbox_items.insert(ItemType::Colours, colours);
|
||||||
|
vbox_items.insert(ItemType::Specs, vec![base.unwrap()]);
|
||||||
|
|
||||||
|
match self.vbox_combine(vec![], Some(vbox_items)) {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => break, // give up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgrading phase
|
||||||
|
// NYI
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_refill(&mut self) -> Result<&mut Player, Error> {
|
||||||
|
self.vbox.balance_sub(DISCARD_COST)?;
|
||||||
|
self.vbox.fill();
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bot_vbox_accept(&mut self, group: ItemType) -> Result<&mut Player, Error> {
|
||||||
|
let item = self.vbox.bot_buy(group)?;
|
||||||
|
self.vbox.stash_add(item, None)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_buy(&mut self, group: ItemType, index: String, construct_id: Option<Uuid>) -> Result<&mut Player, Error> {
|
||||||
|
let item = self.vbox.buy(group, &index)?;
|
||||||
|
|
||||||
|
match construct_id {
|
||||||
|
Some(id) => { self.vbox_apply(item, id)?; },
|
||||||
|
None => { self.vbox.stash_add(item, None)?; },
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_combine(&mut self, inv_indices: Vec<String>, vbox_indices: VboxIndices) -> Result<&mut Player, Error> {
|
||||||
|
self.vbox.combine(inv_indices, vbox_indices)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_refund(&mut self, index: String) -> Result<&mut Player, Error> {
|
||||||
|
self.vbox.refund(index)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_equip(&mut self, index: String, construct_id: Uuid) -> Result<&mut Player, Error> {
|
||||||
|
let item = self.vbox.stash.remove(&index)
|
||||||
|
.ok_or(format_err!("no item at index {:?} {:}", self, &index))?;
|
||||||
|
|
||||||
|
self.vbox_apply(item, construct_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_apply(&mut self, item: Item, construct_id: Uuid) -> Result<&mut Player, Error> {
|
||||||
|
match item.effect() {
|
||||||
|
Some(ItemEffect::Skill) => {
|
||||||
|
let skill = item.into_skill().ok_or(format_err!("item {:?} has no associated skill", item))?;
|
||||||
|
let construct = self.construct_get(construct_id)?;
|
||||||
|
// done here because i teach them a tonne of skills for tests
|
||||||
|
let max_skills = 3;
|
||||||
|
if construct.skills.len() >= max_skills {
|
||||||
|
return Err(format_err!("construct at max skills ({:?})", max_skills));
|
||||||
|
}
|
||||||
|
|
||||||
|
if construct.knows(skill) {
|
||||||
|
return Err(format_err!("construct already knows skill ({:?})" , skill));
|
||||||
|
}
|
||||||
|
|
||||||
|
construct.learn_mut(skill);
|
||||||
|
},
|
||||||
|
Some(ItemEffect::Spec) => {
|
||||||
|
let spec = item.into_spec().ok_or(format_err!("item {:?} has no associated spec", item))?;
|
||||||
|
let construct = self.construct_get(construct_id)?;
|
||||||
|
construct.spec_add(spec)?;
|
||||||
|
|
||||||
|
},
|
||||||
|
None => return Err(err_msg("item has no effect on constructs")),
|
||||||
|
}
|
||||||
|
|
||||||
|
// now the item has been applied
|
||||||
|
// recalculate the stats of the whole player
|
||||||
|
let player_colours = self.constructs.iter().fold(Colours::new(), |tc, c| {
|
||||||
|
Colours {
|
||||||
|
red: tc.red + c.colours.red,
|
||||||
|
green: tc.green + c.colours.green,
|
||||||
|
blue: tc.blue + c.colours.blue
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for construct in self.constructs.iter_mut() {
|
||||||
|
construct.apply_modifiers(&player_colours);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vbox_unequip(&mut self, target: Item, construct_id: Uuid, target_construct_id: Option<Uuid>) -> Result<&mut Player, Error> {
|
||||||
|
if self.vbox.stash.len() >= 9 && !target_construct_id.is_some() {
|
||||||
|
return Err(err_msg("too many items stash"));
|
||||||
|
}
|
||||||
|
|
||||||
|
match target.effect() {
|
||||||
|
Some(ItemEffect::Skill) => {
|
||||||
|
let skill = target.into_skill().ok_or(format_err!("item {:?} has no associated skill", target))?;
|
||||||
|
let construct = self.construct_get(construct_id)?;
|
||||||
|
construct.forget(skill)?;
|
||||||
|
},
|
||||||
|
Some(ItemEffect::Spec) => {
|
||||||
|
let spec = target.into_spec().ok_or(format_err!("item {:?} has no associated spec", target))?;
|
||||||
|
let construct = self.construct_get(construct_id)?;
|
||||||
|
construct.spec_remove(spec)?;
|
||||||
|
},
|
||||||
|
None => return Err(err_msg("item has no effect on constructs")),
|
||||||
|
}
|
||||||
|
|
||||||
|
// now the item has been applied
|
||||||
|
// recalculate the stats of the whole player
|
||||||
|
let player_colours = self.constructs.iter().fold(Colours::new(), |tc, c| {
|
||||||
|
Colours {
|
||||||
|
red: tc.red + c.colours.red,
|
||||||
|
green: tc.green + c.colours.green,
|
||||||
|
blue: tc.blue + c.colours.blue
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for construct in self.constructs.iter_mut() {
|
||||||
|
construct.apply_modifiers(&player_colours);
|
||||||
|
}
|
||||||
|
|
||||||
|
match target_construct_id {
|
||||||
|
Some(cid) => { self.vbox_apply(target, cid)?; },
|
||||||
|
None => { self.vbox.stash_add(target, None)?; },
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GAME METHODS
|
||||||
|
pub fn skills_required(&self) -> usize {
|
||||||
|
let required = self.constructs.iter()
|
||||||
|
.filter(|c| !c.is_ko())
|
||||||
|
.filter(|c| c.available_skills().len() > 0)
|
||||||
|
.collect::<Vec<&Construct>>().len();
|
||||||
|
// info!("{:} requires {:} skills this turn", self.id, required);
|
||||||
|
return required;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intercepting(&self) -> Option<&Construct> {
|
||||||
|
self.constructs.iter()
|
||||||
|
.find(|c| c.affected(Effect::Intercept))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct_by_id(&mut self, id: Uuid) -> Option<&mut Construct> {
|
||||||
|
self.constructs.iter_mut().find(|c| c.id == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use mob::instance_mobs;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn player_bot_vbox_test() {
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let constructs = instance_mobs(player_account);
|
||||||
|
let mut player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true);
|
||||||
|
|
||||||
|
player.vbox.fill();
|
||||||
|
player.autobuy();
|
||||||
|
|
||||||
|
assert!(player.constructs.iter().all(|c| c.skills.len() >= 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn player_score_test() {
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let constructs = instance_mobs(player_account);
|
||||||
|
let mut player = Player::new(player_account, &"test".to_string(), constructs).set_bot(true);
|
||||||
|
|
||||||
|
player.score = player.score.add_win(&Score::Zero);
|
||||||
|
player.score = player.score.add_win(&Score::Zero);
|
||||||
|
player.score = player.score.add_win(&Score::Zero);
|
||||||
|
assert_eq!(player.score, Score::Win); // 40 / 0
|
||||||
|
|
||||||
|
// Bo7 tennis scoring
|
||||||
|
/*assert_eq!(player.score, Score::Three); // 40 / 0
|
||||||
|
|
||||||
|
player.score = player.score.add_loss(); // adv -> deuce
|
||||||
|
assert_eq!(player.score, Score::Three);
|
||||||
|
|
||||||
|
player.score = player.score.add_loss(); // adv -> deuce
|
||||||
|
assert_eq!(player.score, Score::Three);
|
||||||
|
|
||||||
|
player.score = player.score.add_win(&Score::Adv); // opp adv -> stays deuce
|
||||||
|
assert_eq!(player.score, Score::Three);
|
||||||
|
|
||||||
|
player.score = player.score.add_win(&Score::Three);
|
||||||
|
assert_eq!(player.score, Score::Adv);
|
||||||
|
|
||||||
|
player.score = player.score.add_win(&Score::Three);
|
||||||
|
assert_eq!(player.score, Score::Win);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
2188
core/src/skill.rs
Normal file
2188
core/src/skill.rs
Normal file
File diff suppressed because it is too large
Load Diff
734
core/src/spec.rs
Normal file
734
core/src/spec.rs
Normal file
@ -0,0 +1,734 @@
|
|||||||
|
use construct::{Stat, Colours};
|
||||||
|
use util::{IntPct};
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
|
pub struct SpecBonus {
|
||||||
|
pub req: Colours,
|
||||||
|
pub bonus: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpecBonus {
|
||||||
|
pub fn get_bonus(&self, c: &Colours) -> u64 {
|
||||||
|
if c.red >= self.req.red && c.blue >= self.req.blue && c.green >= self.req.green {
|
||||||
|
return self.bonus;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
|
pub struct SpecValues {
|
||||||
|
pub base: u64,
|
||||||
|
pub bonuses: Vec<SpecBonus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpecValues {
|
||||||
|
pub fn max_value (&self, c: &Colours) -> u64 {
|
||||||
|
self.bonuses.iter().fold(self.base, |acc, s| acc + s.get_bonus(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base (self) -> u64 {
|
||||||
|
self.base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug,Copy,Clone,Serialize,Deserialize,PartialEq,PartialOrd,Ord,Eq)]
|
||||||
|
pub enum Spec {
|
||||||
|
Speed,
|
||||||
|
SpeedRR,
|
||||||
|
SpeedBB,
|
||||||
|
SpeedGG,
|
||||||
|
SpeedRG,
|
||||||
|
SpeedGB,
|
||||||
|
SpeedRB,
|
||||||
|
|
||||||
|
SpeedRRPlus,
|
||||||
|
SpeedBBPlus,
|
||||||
|
SpeedGGPlus,
|
||||||
|
SpeedRGPlus,
|
||||||
|
SpeedGBPlus,
|
||||||
|
SpeedRBPlus,
|
||||||
|
|
||||||
|
SpeedRRPlusPlus,
|
||||||
|
SpeedBBPlusPlus,
|
||||||
|
SpeedGGPlusPlus,
|
||||||
|
SpeedRGPlusPlus,
|
||||||
|
SpeedGBPlusPlus,
|
||||||
|
SpeedRBPlusPlus,
|
||||||
|
|
||||||
|
Life,
|
||||||
|
LifeGG,
|
||||||
|
LifeRR,
|
||||||
|
LifeBB,
|
||||||
|
LifeRG,
|
||||||
|
LifeGB,
|
||||||
|
LifeRB,
|
||||||
|
LifeGGPlus,
|
||||||
|
LifeRRPlus,
|
||||||
|
LifeBBPlus,
|
||||||
|
LifeRGPlus,
|
||||||
|
LifeGBPlus,
|
||||||
|
LifeRBPlus,
|
||||||
|
LifeGGPlusPlus,
|
||||||
|
LifeRRPlusPlus,
|
||||||
|
LifeBBPlusPlus,
|
||||||
|
LifeRGPlusPlus,
|
||||||
|
LifeGBPlusPlus,
|
||||||
|
LifeRBPlusPlus,
|
||||||
|
|
||||||
|
Power,
|
||||||
|
PowerRR,
|
||||||
|
PowerGG,
|
||||||
|
PowerBB,
|
||||||
|
PowerRG,
|
||||||
|
PowerGB,
|
||||||
|
PowerRB,
|
||||||
|
PowerRRPlus,
|
||||||
|
PowerGGPlus,
|
||||||
|
PowerBBPlus,
|
||||||
|
PowerRGPlus,
|
||||||
|
PowerGBPlus,
|
||||||
|
PowerRBPlus,
|
||||||
|
PowerRRPlusPlus,
|
||||||
|
PowerGGPlusPlus,
|
||||||
|
PowerBBPlusPlus,
|
||||||
|
PowerRGPlusPlus,
|
||||||
|
PowerGBPlusPlus,
|
||||||
|
PowerRBPlusPlus,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spec {
|
||||||
|
pub fn affects(&self) -> Vec<Stat> {
|
||||||
|
match *self {
|
||||||
|
Spec::Power => vec![Stat::BluePower, Stat::RedPower, Stat::GreenPower],
|
||||||
|
Spec::PowerRR => vec![Stat::RedPower],
|
||||||
|
Spec::PowerGG => vec![Stat::GreenPower],
|
||||||
|
Spec::PowerBB => vec![Stat::BluePower],
|
||||||
|
Spec::PowerRG => vec![Stat::GreenPower, Stat::RedPower],
|
||||||
|
Spec::PowerGB => vec![Stat::GreenPower, Stat::BluePower],
|
||||||
|
Spec::PowerRB => vec![Stat::RedPower, Stat::BluePower],
|
||||||
|
Spec::PowerRRPlus => vec![Stat::RedPower],
|
||||||
|
Spec::PowerGGPlus => vec![Stat::GreenPower],
|
||||||
|
Spec::PowerBBPlus => vec![Stat::BluePower],
|
||||||
|
Spec::PowerRGPlus => vec![Stat::GreenPower, Stat::RedPower],
|
||||||
|
Spec::PowerGBPlus => vec![Stat::GreenPower, Stat::BluePower],
|
||||||
|
Spec::PowerRBPlus => vec![Stat::RedPower, Stat::BluePower],
|
||||||
|
Spec::PowerRRPlusPlus => vec![Stat::RedPower],
|
||||||
|
Spec::PowerGGPlusPlus => vec![Stat::GreenPower],
|
||||||
|
Spec::PowerBBPlusPlus => vec![Stat::BluePower],
|
||||||
|
Spec::PowerRGPlusPlus => vec![Stat::GreenPower, Stat::RedPower],
|
||||||
|
Spec::PowerGBPlusPlus => vec![Stat::GreenPower, Stat::BluePower],
|
||||||
|
Spec::PowerRBPlusPlus => vec![Stat::RedPower, Stat::BluePower],
|
||||||
|
|
||||||
|
Spec::Speed => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRR => vec![Stat::Speed],
|
||||||
|
Spec::SpeedBB => vec![Stat::Speed],
|
||||||
|
Spec::SpeedGG => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRG => vec![Stat::Speed],
|
||||||
|
Spec::SpeedGB => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRB => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRRPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedBBPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedGGPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRGPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedGBPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRBPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRRPlusPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedBBPlusPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedGGPlusPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRGPlusPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedGBPlusPlus => vec![Stat::Speed],
|
||||||
|
Spec::SpeedRBPlusPlus => vec![Stat::Speed],
|
||||||
|
|
||||||
|
Spec::Life => vec![Stat::GreenLife],
|
||||||
|
Spec::LifeRR => vec![Stat::RedLife],
|
||||||
|
Spec::LifeBB => vec![Stat::BlueLife],
|
||||||
|
Spec::LifeGG => vec![Stat::GreenLife],
|
||||||
|
Spec::LifeRG => vec![Stat::GreenLife, Stat::RedLife],
|
||||||
|
Spec::LifeGB => vec![Stat::GreenLife, Stat::BlueLife],
|
||||||
|
Spec::LifeRB => vec![Stat::BlueLife, Stat::RedLife],
|
||||||
|
Spec::LifeRRPlus => vec![Stat::RedLife],
|
||||||
|
Spec::LifeBBPlus => vec![Stat::BlueLife],
|
||||||
|
Spec::LifeGGPlus => vec![Stat::GreenLife],
|
||||||
|
Spec::LifeRGPlus => vec![Stat::GreenLife, Stat::RedLife],
|
||||||
|
Spec::LifeGBPlus => vec![Stat::GreenLife, Stat::BlueLife],
|
||||||
|
Spec::LifeRBPlus => vec![Stat::BlueLife, Stat::RedLife],
|
||||||
|
Spec::LifeRRPlusPlus => vec![Stat::RedLife],
|
||||||
|
Spec::LifeBBPlusPlus => vec![Stat::BlueLife],
|
||||||
|
Spec::LifeGGPlusPlus => vec![Stat::GreenLife],
|
||||||
|
Spec::LifeRGPlusPlus => vec![Stat::GreenLife, Stat::RedLife],
|
||||||
|
Spec::LifeGBPlusPlus => vec![Stat::GreenLife, Stat::BlueLife],
|
||||||
|
Spec::LifeRBPlusPlus => vec![Stat::BlueLife, Stat::RedLife],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values(&self) -> SpecValues {
|
||||||
|
match *self {
|
||||||
|
Spec::Power => SpecValues {
|
||||||
|
base: 10,
|
||||||
|
bonuses: vec![]
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerRR=> SpecValues {
|
||||||
|
base: 25,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 15 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 20 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerGG=> SpecValues {
|
||||||
|
base: 25,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 15 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 20 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerBB=> SpecValues {
|
||||||
|
base: 25,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 15 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 20 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerRG=> SpecValues {
|
||||||
|
base: 20,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 5 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 15 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerGB=> SpecValues {
|
||||||
|
base: 20,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 5 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 15 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerRB=> SpecValues {
|
||||||
|
base: 20,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 5 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 15 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerRRPlus => SpecValues {
|
||||||
|
base: 45,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 15 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 25 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 35 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerGGPlus => SpecValues {
|
||||||
|
base: 45,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 15 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 25 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 35 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerBBPlus => SpecValues {
|
||||||
|
base: 45,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 15 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 25 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 35 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerRGPlus => SpecValues {
|
||||||
|
base: 35,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 20 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 25 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerGBPlus => SpecValues {
|
||||||
|
base: 35,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 20 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 25 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerRBPlus => SpecValues {
|
||||||
|
base: 35,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 10 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 20 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 25 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Spec::PowerRRPlusPlus => SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 25 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 45 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 60 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerGGPlusPlus => SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 25 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 45 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 60 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerBBPlusPlus => SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 25 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 45 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 60 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerRGPlusPlus => SpecValues {
|
||||||
|
base: 60,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 20 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 30 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 45 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerGBPlusPlus => SpecValues {
|
||||||
|
base: 60,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 20 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 30 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 45 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::PowerRBPlusPlus => SpecValues {
|
||||||
|
base: 60,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 20 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 30 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 45 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::Speed => SpecValues {
|
||||||
|
base: 40,
|
||||||
|
bonuses: vec![]
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRR=> SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 80 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedGG=> SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 80 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedBB=> SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 80 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRG=> SpecValues {
|
||||||
|
base: 60,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 60 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 60 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 60 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedGB=> SpecValues {
|
||||||
|
base: 60,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 60 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 60 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 60 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRB=> SpecValues {
|
||||||
|
base: 60,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 60 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 60 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 60 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRRPlus => SpecValues {
|
||||||
|
base: 120,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 120 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedGGPlus => SpecValues {
|
||||||
|
base: 120,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 120 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedBBPlus => SpecValues {
|
||||||
|
base: 120,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 120 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRGPlus => SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 80 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedGBPlus => SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 80 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRBPlus => SpecValues {
|
||||||
|
base: 80,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 80 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 80 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRRPlusPlus => SpecValues {
|
||||||
|
base: 160,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 160 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 160 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 160 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedGGPlusPlus => SpecValues {
|
||||||
|
base: 160,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 160 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 160 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 160 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedBBPlusPlus => SpecValues {
|
||||||
|
base: 160,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 160 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 160 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 160 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRGPlusPlus => SpecValues {
|
||||||
|
base: 120,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 120 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedGBPlusPlus => SpecValues {
|
||||||
|
base: 120,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 120 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::SpeedRBPlusPlus => SpecValues {
|
||||||
|
base: 120,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 120 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 120 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::Life => SpecValues {
|
||||||
|
base: 125,
|
||||||
|
bonuses: vec![]},
|
||||||
|
|
||||||
|
Spec::LifeRR=> SpecValues {
|
||||||
|
base: 275,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 75 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 125 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 175 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeGG=> SpecValues {
|
||||||
|
base: 225,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 50 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 75 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 125 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeBB=> SpecValues {
|
||||||
|
base: 275,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 75 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 125 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 175 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeRG=> SpecValues {
|
||||||
|
base: 125,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 50 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 75 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 125 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeGB=> SpecValues {
|
||||||
|
base: 125,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 50 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 75 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 125 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeRB=> SpecValues {
|
||||||
|
base: 175,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 50 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 75 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 125 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeRRPlus => SpecValues {
|
||||||
|
base: 500,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 125 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 225 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 300 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeGGPlus => SpecValues {
|
||||||
|
base: 400,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 90 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 130 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 225 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeBBPlus => SpecValues {
|
||||||
|
base: 500,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 125 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 225 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 300 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeRGPlus => SpecValues {
|
||||||
|
base: 225,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 100 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 150 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 225 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeGBPlus => SpecValues {
|
||||||
|
base: 225,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 100 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 150 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 225 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeRBPlus => SpecValues {
|
||||||
|
base: 350,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 100 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 150 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 225 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Spec::LifeRRPlusPlus => SpecValues {
|
||||||
|
base: 875,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 0 }, bonus: 225 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 0 }, bonus: 400 },
|
||||||
|
SpecBonus { req: Colours { red: 20, green: 0, blue: 0 }, bonus: 525 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeGGPlusPlus => SpecValues {
|
||||||
|
base: 475,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 0 }, bonus: 130 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 0 }, bonus: 225 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 20, blue: 0 }, bonus: 300 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeBBPlusPlus => SpecValues {
|
||||||
|
base: 875,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 5 }, bonus: 225 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 10 }, bonus: 400 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 0, blue: 20 }, bonus: 525 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeRGPlusPlus => SpecValues {
|
||||||
|
base: 400,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 2, blue: 0 }, bonus: 175 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 5, blue: 0 }, bonus: 275 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 10, blue: 0 }, bonus: 400 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeGBPlusPlus => SpecValues {
|
||||||
|
base: 625,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 2, blue: 2 }, bonus: 175 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 5, blue: 5 }, bonus: 275 },
|
||||||
|
SpecBonus { req: Colours { red: 0, green: 10, blue: 10 }, bonus: 400 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
Spec::LifeRBPlusPlus => SpecValues {
|
||||||
|
base: 400,
|
||||||
|
bonuses: vec![
|
||||||
|
SpecBonus { req: Colours { red: 2, green: 0, blue: 2 }, bonus: 175 },
|
||||||
|
SpecBonus { req: Colours { red: 5, green: 0, blue: 5 }, bonus: 275 },
|
||||||
|
SpecBonus { req: Colours { red: 10, green: 0, blue: 10 }, bonus: 400 }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply(&self, modified: u64, base: u64, player_colours: &Colours) -> u64 {
|
||||||
|
match *self {
|
||||||
|
// Percentage multipliers based on base value
|
||||||
|
Spec::Power |
|
||||||
|
Spec::Speed => modified + base.pct(self.values().base),
|
||||||
|
Spec::PowerRR|
|
||||||
|
Spec::PowerGG|
|
||||||
|
Spec::PowerBB|
|
||||||
|
Spec::PowerRG|
|
||||||
|
Spec::PowerGB|
|
||||||
|
Spec::PowerRB|
|
||||||
|
Spec::PowerRRPlus |
|
||||||
|
Spec::PowerGGPlus |
|
||||||
|
Spec::PowerBBPlus |
|
||||||
|
Spec::PowerRGPlus |
|
||||||
|
Spec::PowerGBPlus |
|
||||||
|
Spec::PowerRBPlus |
|
||||||
|
Spec::PowerRRPlusPlus |
|
||||||
|
Spec::PowerGGPlusPlus |
|
||||||
|
Spec::PowerBBPlusPlus |
|
||||||
|
Spec::PowerRGPlusPlus |
|
||||||
|
Spec::PowerGBPlusPlus |
|
||||||
|
Spec::PowerRBPlusPlus |
|
||||||
|
|
||||||
|
Spec::SpeedRR|
|
||||||
|
Spec::SpeedGG|
|
||||||
|
Spec::SpeedBB|
|
||||||
|
Spec::SpeedRG|
|
||||||
|
Spec::SpeedGB|
|
||||||
|
Spec::SpeedRB|
|
||||||
|
Spec::SpeedRRPlus |
|
||||||
|
Spec::SpeedGGPlus |
|
||||||
|
Spec::SpeedBBPlus |
|
||||||
|
Spec::SpeedRGPlus |
|
||||||
|
Spec::SpeedGBPlus |
|
||||||
|
Spec::SpeedRBPlus |
|
||||||
|
Spec::SpeedRRPlusPlus |
|
||||||
|
Spec::SpeedGGPlusPlus |
|
||||||
|
Spec::SpeedBBPlusPlus |
|
||||||
|
Spec::SpeedRGPlusPlus |
|
||||||
|
Spec::SpeedGBPlusPlus |
|
||||||
|
Spec::SpeedRBPlusPlus => modified + base.pct(self.values().max_value(player_colours)),
|
||||||
|
|
||||||
|
// Flat bonus
|
||||||
|
Spec::Life => modified + self.values().base,
|
||||||
|
Spec::LifeRR|
|
||||||
|
Spec::LifeGG|
|
||||||
|
Spec::LifeBB|
|
||||||
|
Spec::LifeRG|
|
||||||
|
Spec::LifeGB|
|
||||||
|
Spec::LifeRB|
|
||||||
|
Spec::LifeRRPlus |
|
||||||
|
Spec::LifeGGPlus |
|
||||||
|
Spec::LifeBBPlus |
|
||||||
|
Spec::LifeRGPlus |
|
||||||
|
Spec::LifeGBPlus |
|
||||||
|
Spec::LifeRBPlus |
|
||||||
|
Spec::LifeRRPlusPlus |
|
||||||
|
Spec::LifeGGPlusPlus |
|
||||||
|
Spec::LifeBBPlusPlus |
|
||||||
|
Spec::LifeRGPlusPlus |
|
||||||
|
Spec::LifeGBPlusPlus |
|
||||||
|
Spec::LifeRBPlusPlus => modified + self.values().max_value(player_colours),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
core/src/util.rs
Normal file
40
core/src/util.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// use net::Db;
|
||||||
|
// Db Commons
|
||||||
|
// use failure::Error;
|
||||||
|
|
||||||
|
// pub fn startup(db: Db) -> Result<(), Error> {
|
||||||
|
// let tx = db.transaction()?;
|
||||||
|
|
||||||
|
// info!("running startup fns");
|
||||||
|
|
||||||
|
// match tx.commit() {
|
||||||
|
// Ok(_) => {
|
||||||
|
// info!("startup processes completed");
|
||||||
|
// Ok(())
|
||||||
|
// },
|
||||||
|
// Err(e) => Err(format_err!("failed to commit startup tx {:?}", e)),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub trait IntPct {
|
||||||
|
fn pct(self, pct: u64) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntPct for u64 {
|
||||||
|
fn pct(self, pct: u64) -> u64 {
|
||||||
|
self * pct / 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_pct_test() {
|
||||||
|
assert_eq!(100.pct(110), 110);
|
||||||
|
assert_eq!(100.pct(50), 50);
|
||||||
|
assert_eq!(1.pct(200), 2);
|
||||||
|
assert_eq!(1.pct(50), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
305
core/src/vbox.rs
Normal file
305
core/src/vbox.rs
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
// refunds
|
||||||
|
use rand::prelude::*;
|
||||||
|
use rand::{thread_rng};
|
||||||
|
use rand::distributions::{WeightedIndex};
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
use failure::err_msg;
|
||||||
|
|
||||||
|
use instance::{Instance};
|
||||||
|
use construct::{Colours};
|
||||||
|
|
||||||
|
use item::*;
|
||||||
|
|
||||||
|
pub type VboxIndices = Option<HashMap<ItemType, Vec<String>>>;
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
|
pub struct Vbox {
|
||||||
|
pub bits: usize,
|
||||||
|
pub store: HashMap<ItemType, HashMap<String, Item>>,
|
||||||
|
pub stash: HashMap<String, Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Copy,Clone,Serialize,Deserialize,Hash,PartialEq,Eq)]
|
||||||
|
pub enum ItemType {
|
||||||
|
Colours,
|
||||||
|
Skills,
|
||||||
|
Specs,
|
||||||
|
}
|
||||||
|
|
||||||
|
const STORE_COLOURS_CAPACITY: usize = 6;
|
||||||
|
const STORE_SKILLS_CAPACITY: usize = 3;
|
||||||
|
const STORE_SPECS_CAPACITY: usize = 3;
|
||||||
|
const STASH_CAPACITY: usize = 6;
|
||||||
|
const STARTING_ATTACK_COUNT: usize = 3;
|
||||||
|
|
||||||
|
impl Vbox {
|
||||||
|
pub fn new() -> Vbox {
|
||||||
|
let mut colours: HashMap<String, Item> = HashMap::new();
|
||||||
|
let mut skills: HashMap<String, Item> = HashMap::new();
|
||||||
|
let mut specs: HashMap<String, Item> = HashMap::new();
|
||||||
|
|
||||||
|
let store = [
|
||||||
|
(ItemType::Colours, colours),
|
||||||
|
(ItemType::Skills, skills),
|
||||||
|
(ItemType::Colours, specs),
|
||||||
|
].iter().cloned().collect();
|
||||||
|
|
||||||
|
let mut stash = HashMap::new();
|
||||||
|
for i in 0..STARTING_ATTACK_COUNT {
|
||||||
|
stash.insert(i.to_string(), Item::Attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vbox {
|
||||||
|
store,
|
||||||
|
stash,
|
||||||
|
bits: 30,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn balance_sub(&mut self, amount: usize) -> Result<&mut Vbox, Error> {
|
||||||
|
let new_balance = self.bits
|
||||||
|
.checked_sub(amount)
|
||||||
|
.ok_or(format_err!("insufficient balance: {:?}", self.bits))?;
|
||||||
|
|
||||||
|
self.bits = new_balance;
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn balance_add(&mut self, amount: usize) -> &mut Vbox {
|
||||||
|
self.bits = self.bits.saturating_add(amount);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill(&mut self) -> &mut Vbox {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
let colours = vec![
|
||||||
|
(Item::Red, 1),
|
||||||
|
(Item::Green, 1),
|
||||||
|
(Item::Blue, 1),
|
||||||
|
];
|
||||||
|
let colour_dist = WeightedIndex::new(colours.iter().map(|item| item.1)).unwrap();
|
||||||
|
|
||||||
|
let skills = vec![
|
||||||
|
(Item::Attack, 1),
|
||||||
|
(Item::Block, 1),
|
||||||
|
(Item::Buff, 1),
|
||||||
|
(Item::Debuff, 1),
|
||||||
|
(Item::Stun, 1),
|
||||||
|
];
|
||||||
|
let skill_dist = WeightedIndex::new(skills.iter().map(|item| item.1)).unwrap();
|
||||||
|
|
||||||
|
let specs = vec![
|
||||||
|
(Item::Power, 1),
|
||||||
|
(Item::Life, 1),
|
||||||
|
(Item::Speed, 1),
|
||||||
|
];
|
||||||
|
let spec_dist = WeightedIndex::new(specs.iter().map(|item| item.1)).unwrap();
|
||||||
|
|
||||||
|
for item_type in [ItemType::Colours, ItemType::Skills, ItemType::Specs].iter() {
|
||||||
|
let (items, num, dist) = match item_type {
|
||||||
|
ItemType::Colours => (&colours, STORE_COLOURS_CAPACITY, &colour_dist),
|
||||||
|
ItemType::Skills => (&skills, STORE_SKILLS_CAPACITY, &skill_dist),
|
||||||
|
ItemType::Specs => (&specs, STORE_SPECS_CAPACITY, &spec_dist),
|
||||||
|
};
|
||||||
|
|
||||||
|
let drops = iter::repeat_with(|| items[dist.sample(&mut rng)].0)
|
||||||
|
.take(num)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, item)| (i.to_string(), item))
|
||||||
|
.collect::<HashMap<String, Item>>();
|
||||||
|
|
||||||
|
self.store.insert(*item_type, drops);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buy(&mut self, item: ItemType, i: &String) -> Result<Item, Error> {
|
||||||
|
// check item exists
|
||||||
|
let selection = self.store
|
||||||
|
.get_mut(&item).ok_or(format_err!("no item group {:?}", item))?
|
||||||
|
.remove(i).ok_or(format_err!("no item at index {:?} {:}", self, i))?;
|
||||||
|
|
||||||
|
self.balance_sub(selection.cost())?;
|
||||||
|
|
||||||
|
Ok(selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stash_add(&mut self, item: Item, index: Option<&String>) -> Result<String, Error> {
|
||||||
|
if self.stash.len() >= STASH_CAPACITY {
|
||||||
|
return Err(err_msg("stash full"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(index) = index {
|
||||||
|
if self.stash.contains_key(index) {
|
||||||
|
return Err(format_err!("slot occupied {:?}", index));
|
||||||
|
}
|
||||||
|
self.stash.insert(index.clone(), item);
|
||||||
|
return Ok(index.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in (0..STASH_CAPACITY).map(|i| i.to_string()) {
|
||||||
|
if !self.stash.contains_key(&i) {
|
||||||
|
self.stash.insert(i.clone(), item);
|
||||||
|
return Ok(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(err_msg("stash full"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bot_buy(&mut self, item: ItemType) -> Result<Item, Error> {
|
||||||
|
let buy_index = self.store[&item]
|
||||||
|
.keys()
|
||||||
|
.next()
|
||||||
|
.ok_or(format_err!("no item in group {:?}", item))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
self.buy(item, &buy_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refund(&mut self, i: String) -> Result<&mut Vbox, Error> {
|
||||||
|
let refunded = self.stash.remove(&i)
|
||||||
|
.ok_or(format_err!("no item at index {:?} {:?}", self.stash, i))?;
|
||||||
|
|
||||||
|
let refund = refunded.cost();
|
||||||
|
// info!("refunding {:?} for {:?}", refund, refunded);
|
||||||
|
self.balance_add(refund);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn combine(&mut self, stash_indices: Vec<String>, store_indices: Option<HashMap<ItemType, Vec<String>>>) -> Result<&mut Vbox, Error> {
|
||||||
|
// find base item for index to insert into
|
||||||
|
let base_index = stash_indices.iter()
|
||||||
|
.find(|i| match self.stash.get(i.clone()) {
|
||||||
|
Some(item) => item.into_skill().is_some(),
|
||||||
|
None => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut input = stash_indices
|
||||||
|
.iter()
|
||||||
|
.map(|i| self.stash.remove(i)
|
||||||
|
.ok_or(format_err!("no item at index {:?} {:?}", self.stash, i)))
|
||||||
|
.collect::<Result<Vec<Item>, Error>>()?;
|
||||||
|
|
||||||
|
if let Some(store_indices) = store_indices {
|
||||||
|
let mut purchased = store_indices.iter()
|
||||||
|
.map(|(g, list)|
|
||||||
|
list.iter()
|
||||||
|
.map(|i| self.buy(*g, i))
|
||||||
|
.collect::<Result<Vec<Item>, Error>>()
|
||||||
|
)
|
||||||
|
.collect::<Result<Vec<Vec<Item>>, Error>>()?
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
input.append(&mut purchased);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the input to align with the combinations
|
||||||
|
// combos are sorted when created
|
||||||
|
input.sort_unstable();
|
||||||
|
let combos = get_combos();
|
||||||
|
let combo = combos.iter().find(|c| c.components == input).ok_or(err_msg("not a combo"))?;
|
||||||
|
|
||||||
|
self.stash_add(combo.item, base_index)?;
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn combine_test() {
|
||||||
|
let mut vbox = Vbox::new();
|
||||||
|
vbox.stash.insert(0.to_string(), Item::Attack);
|
||||||
|
vbox.stash.insert(1.to_string(), Item::Green);
|
||||||
|
vbox.stash.insert(2.to_string(), Item::Green);
|
||||||
|
vbox.combine(vec![0.to_string(), 1.to_string(), 2.to_string()], None).unwrap();
|
||||||
|
assert_eq!(vbox.stash["0"], Item::Heal);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn buy_test() {
|
||||||
|
let mut vbox = Vbox::new();
|
||||||
|
vbox.fill();
|
||||||
|
|
||||||
|
// cannot rebuy same
|
||||||
|
vbox.buy(ItemType::Skills, &0.to_string()).unwrap();
|
||||||
|
assert!(vbox.store[&ItemType::Skills].get(&0.to_string()).is_none());
|
||||||
|
assert!(vbox.buy(ItemType::Skills, &0.to_string()).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn capacity_test() {
|
||||||
|
let mut vbox = Vbox::new();
|
||||||
|
vbox.fill();
|
||||||
|
vbox.stash_add(Item::Red, None).unwrap();
|
||||||
|
vbox.stash_add(Item::Red, None).unwrap();
|
||||||
|
vbox.stash_add(Item::Red, None).unwrap();
|
||||||
|
assert!(vbox.stash_add(Item::Red, None).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn store_and_stash_combine_test() {
|
||||||
|
let mut vbox = Vbox::new();
|
||||||
|
vbox.fill();
|
||||||
|
|
||||||
|
let mut skill_combine_args = HashMap::new();
|
||||||
|
skill_combine_args.insert(ItemType::Colours, vec![0.to_string(), 1.to_string()]);
|
||||||
|
skill_combine_args.insert(ItemType::Skills, vec![0.to_string()]);
|
||||||
|
|
||||||
|
let mut spec_combine_args = HashMap::new();
|
||||||
|
spec_combine_args.insert(ItemType::Colours, vec![2.to_string(), 3.to_string()]);
|
||||||
|
spec_combine_args.insert(ItemType::Specs, vec![0.to_string()]);
|
||||||
|
|
||||||
|
vbox.combine(vec![], Some(skill_combine_args)).unwrap();
|
||||||
|
vbox.combine(vec![], Some(spec_combine_args)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn combos_test() {
|
||||||
|
let mut input = vec![Item::Green, Item::Attack, Item::Green];
|
||||||
|
let combos = get_combos();
|
||||||
|
|
||||||
|
// sort input so they align
|
||||||
|
input.sort_unstable();
|
||||||
|
|
||||||
|
let combo = combos.iter().find(|c| c.components == input);
|
||||||
|
assert!(combo.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn refund_test() {
|
||||||
|
let mut vbox = Vbox::new();
|
||||||
|
vbox.stash.insert(0.to_string(), Item::Strike);
|
||||||
|
vbox.refund(0.to_string()).unwrap();
|
||||||
|
assert_eq!(vbox.bits, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn colours_count_test() {
|
||||||
|
let strike = Item::Strike;
|
||||||
|
|
||||||
|
let mut count = Colours::new();
|
||||||
|
strike.colours(&mut count);
|
||||||
|
assert_eq!(count.red, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn item_info_test() {
|
||||||
|
// info!("{:#?}", item_info());
|
||||||
|
// }
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@ use account::Account;
|
|||||||
use pg::Db;
|
use pg::Db;
|
||||||
|
|
||||||
use construct::{Construct};
|
use construct::{Construct};
|
||||||
use skill::{Skill, Cast, Resolution, Event, resolution_steps};
|
use skill::{Skill, Cast, Resolution, Event, resolve};
|
||||||
use effect::{Effect};
|
use effect::{Effect};
|
||||||
use player::{Player};
|
use player::{Player};
|
||||||
use instance::{TimeControl, instance_game_finished};
|
use instance::{TimeControl, instance_game_finished};
|
||||||
@ -217,6 +217,7 @@ impl Game {
|
|||||||
|
|
||||||
fn pve_add_skills(&mut self) -> &mut Game {
|
fn pve_add_skills(&mut self) -> &mut Game {
|
||||||
let mut pve_skills = vec![];
|
let mut pve_skills = vec![];
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
for mobs in self.players
|
for mobs in self.players
|
||||||
.iter()
|
.iter()
|
||||||
@ -228,8 +229,6 @@ impl Game {
|
|||||||
// info!("{:?} {:?}", mob.name, skill);
|
// info!("{:?} {:?}", mob.name, skill);
|
||||||
match skill {
|
match skill {
|
||||||
Some(s) => {
|
Some(s) => {
|
||||||
let mut rng = thread_rng();
|
|
||||||
|
|
||||||
// the mut marks it as being able to be called
|
// the mut marks it as being able to be called
|
||||||
// more than once
|
// more than once
|
||||||
let mut find_target = || {
|
let mut find_target = || {
|
||||||
@ -479,7 +478,7 @@ impl Game {
|
|||||||
while let Some(cast) = self.stack.pop() {
|
while let Some(cast) = self.stack.pop() {
|
||||||
// info!("{:} casts ", cast);
|
// info!("{:} casts ", cast);
|
||||||
|
|
||||||
let mut resolutions = resolution_steps(&cast, &mut self);
|
let mut resolutions = resolve(&cast, &mut self);
|
||||||
r_animation_ms = resolutions.iter().fold(r_animation_ms, |acc, r| acc + r.clone().get_delay());
|
r_animation_ms = resolutions.iter().fold(r_animation_ms, |acc, r| acc + r.clone().get_delay());
|
||||||
|
|
||||||
|
|
||||||
@ -807,91 +806,6 @@ pub fn game_delete(tx: &mut Transaction, id: Uuid) -> Result<(), Error> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn game_global_startup(tx: &mut Transaction) -> Result<(), Error> {
|
|
||||||
// if game_global_get(tx).is_ok() {
|
|
||||||
// info!("global mm game exists");
|
|
||||||
// return Ok(());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut game = Game::new();
|
|
||||||
|
|
||||||
// game
|
|
||||||
// .set_player_num(2)
|
|
||||||
// .set_player_constructs(3)
|
|
||||||
// .set_mode(GameMode::Pvp);
|
|
||||||
|
|
||||||
// game_write(tx, &game)?;
|
|
||||||
|
|
||||||
// let query = "
|
|
||||||
// INSERT INTO matchmaking (id, game)
|
|
||||||
// VALUES ($1, $2)
|
|
||||||
// RETURNING id;
|
|
||||||
// ";
|
|
||||||
|
|
||||||
// let result = tx
|
|
||||||
// .query(query, &[&Uuid::nil(), &game.id])?;
|
|
||||||
|
|
||||||
// result.iter().next().ok_or(format_err!("no game written"))?;
|
|
||||||
|
|
||||||
// info!("{:} wrote global mm startup", game.id);
|
|
||||||
|
|
||||||
// return Ok(());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn game_global_set(tx: &mut Transaction, game: &Game) -> Result<(), Error> {
|
|
||||||
// let query = "
|
|
||||||
// UPDATE matchmaking
|
|
||||||
// SET game = $1
|
|
||||||
// WHERE id = $2
|
|
||||||
// RETURNING id, game;
|
|
||||||
// ";
|
|
||||||
|
|
||||||
// let result = tx
|
|
||||||
// .query(query, &[&game.id, &Uuid::nil()])?;
|
|
||||||
|
|
||||||
// result.iter()
|
|
||||||
// .next()
|
|
||||||
// .ok_or(err_msg("could not set global game mm"))?;
|
|
||||||
|
|
||||||
// return Ok(());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn game_global_get(tx: &mut Transaction) -> Result<Game, Error> {
|
|
||||||
// let query = "
|
|
||||||
// SELECT * from games
|
|
||||||
// WHERE id = (
|
|
||||||
// SELECT game
|
|
||||||
// FROM matchmaking
|
|
||||||
// WHERE id = $1
|
|
||||||
// );
|
|
||||||
// ";
|
|
||||||
|
|
||||||
// let delete_query = "
|
|
||||||
// DELETE from matchmaking;
|
|
||||||
// ";
|
|
||||||
|
|
||||||
// let result = tx
|
|
||||||
// .query(query, &[&Uuid::nil()])?;
|
|
||||||
|
|
||||||
// let returned = match result.iter().next() {
|
|
||||||
// Some(row) => row,
|
|
||||||
// None => return Err(err_msg("game not found")),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // tells from_slice to cast into a construct
|
|
||||||
// let game_bytes: Vec<u8> = returned.get("data");
|
|
||||||
// let game = match from_slice::<Game>(&game_bytes) {
|
|
||||||
// Ok(g) => g,
|
|
||||||
// Err(_) => {
|
|
||||||
// tx.query(delete_query, &[])?;
|
|
||||||
// return Err(err_msg("matchmaking game was invalid"))
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return Ok(game);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
pub fn game_update(tx: &mut Transaction, game: &Game) -> Result<(), Error> {
|
pub fn game_update(tx: &mut Transaction, game: &Game) -> Result<(), Error> {
|
||||||
let game_bytes = to_vec(&game)?;
|
let game_bytes = to_vec(&game)?;
|
||||||
|
|
||||||
@ -964,13 +878,7 @@ pub fn game_concede(tx: &mut Transaction, account: &Account, game_id: Uuid) -> R
|
|||||||
|
|
||||||
pub fn game_skill_clear(tx: &mut Transaction, account: &Account, game_id: Uuid) -> Result<Game, Error> {
|
pub fn game_skill_clear(tx: &mut Transaction, account: &Account, game_id: Uuid) -> Result<Game, Error> {
|
||||||
let mut game = game_get(tx, game_id)?;
|
let mut game = game_get(tx, game_id)?;
|
||||||
|
|
||||||
game.clear_skill(account.id)?;
|
game.clear_skill(account.id)?;
|
||||||
|
|
||||||
if game.skill_phase_finished() {
|
|
||||||
game = game.resolve_phase_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
game_update(tx, &game)?;
|
game_update(tx, &game)?;
|
||||||
|
|
||||||
Ok(game)
|
Ok(game)
|
||||||
|
|||||||
@ -18,18 +18,12 @@ pub fn dev_resolve(a_id: Uuid, b_id: Uuid, skill: Skill) -> Resolutions {
|
|||||||
if skill.aoe() { // Send an aoe skill event for anims
|
if skill.aoe() { // Send an aoe skill event for anims
|
||||||
resolutions.push(Resolution::new(&a, &b).event(Event::AoeSkill { skill }).stages(EventStages::StartEnd));
|
resolutions.push(Resolution::new(&a, &b).event(Event::AoeSkill { skill }).stages(EventStages::StartEnd));
|
||||||
}
|
}
|
||||||
return resolve(skill, &mut a, &mut b, resolutions);
|
return resolve_skill(skill, &mut a, &mut b, resolutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolution_steps(cast: &Cast, game: &mut Game) -> Resolutions {
|
pub fn resolve(cast: &Cast, game: &mut Game) -> Resolutions {
|
||||||
let mut resolutions = vec![];
|
let mut resolutions = vec![];
|
||||||
|
|
||||||
resolutions = pre_resolve(cast, game, resolutions);
|
|
||||||
|
|
||||||
return resolutions;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -> Resolutions {
|
|
||||||
let skill = cast.skill;
|
let skill = cast.skill;
|
||||||
let source = game.construct_by_id(cast.source_construct_id).unwrap().clone();
|
let source = game.construct_by_id(cast.source_construct_id).unwrap().clone();
|
||||||
let targets = game.get_targets(cast.skill, &source, cast.target_construct_id);
|
let targets = game.get_targets(cast.skill, &source, cast.target_construct_id);
|
||||||
@ -54,7 +48,7 @@ pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolutions = resolve(cast.skill, &mut source, &mut target, resolutions);
|
resolutions = resolve_skill(cast.skill, &mut source, &mut target, resolutions);
|
||||||
|
|
||||||
// save the changes to the game
|
// save the changes to the game
|
||||||
game.update_construct(&mut source);
|
game.update_construct(&mut source);
|
||||||
@ -67,7 +61,7 @@ pub fn pre_resolve(cast: &Cast, game: &mut Game, mut resolutions: Resolutions) -
|
|||||||
return resolutions;
|
return resolutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut resolutions: Vec<Resolution>) -> Resolutions {
|
pub fn resolve_skill(skill: Skill, source: &mut Construct, target: &mut Construct, mut resolutions: Vec<Resolution>) -> Resolutions {
|
||||||
if let Some(_disable) = source.disabled(skill) {
|
if let Some(_disable) = source.disabled(skill) {
|
||||||
// resolutions.push(Resolution::new(source, target).event(Event::Disable { disable, skill }).stages(EventStages::PostOnly));
|
// resolutions.push(Resolution::new(source, target).event(Event::Disable { disable, skill }).stages(EventStages::PostOnly));
|
||||||
return resolutions;
|
return resolutions;
|
||||||
@ -84,7 +78,7 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut
|
|||||||
return resolutions;
|
return resolutions;
|
||||||
}
|
}
|
||||||
resolutions.push(Resolution::new(source, target).event(Event::Reflection { skill }));
|
resolutions.push(Resolution::new(source, target).event(Event::Reflection { skill }));
|
||||||
return resolve(skill, &mut source.clone(), source, resolutions);
|
return resolve_skill(skill, &mut source.clone(), source, resolutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if source.affected(Effect::Haste) {
|
if source.affected(Effect::Haste) {
|
||||||
@ -175,14 +169,14 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut
|
|||||||
|
|
||||||
Skill::Decay|
|
Skill::Decay|
|
||||||
Skill::DecayPlus |
|
Skill::DecayPlus |
|
||||||
Skill::DecayPlusPlus => decay(source, target, resolutions, skill), // dot
|
Skill::DecayPlusPlus => decay(source, target, resolutions, skill),
|
||||||
Skill::DecayTick|
|
Skill::DecayTick|
|
||||||
Skill::DecayTickPlus |
|
Skill::DecayTickPlus |
|
||||||
Skill::DecayTickPlusPlus => decay_tick(source, target, resolutions, skill), // dot
|
Skill::DecayTickPlusPlus => decay_tick(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Haste|
|
Skill::Haste|
|
||||||
Skill::HastePlus |
|
Skill::HastePlus |
|
||||||
Skill::HastePlusPlus => haste(source, target, resolutions, skill), // speed slow
|
Skill::HastePlusPlus => haste(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Heal|
|
Skill::Heal|
|
||||||
Skill::HealPlus |
|
Skill::HealPlus |
|
||||||
@ -206,7 +200,7 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut
|
|||||||
|
|
||||||
Skill::Purge|
|
Skill::Purge|
|
||||||
Skill::PurgePlus |
|
Skill::PurgePlus |
|
||||||
Skill::PurgePlusPlus => purge(source, target, resolutions, skill), // dispel all buffs
|
Skill::PurgePlusPlus => purge(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Purify|
|
Skill::Purify|
|
||||||
Skill::PurifyPlus |
|
Skill::PurifyPlus |
|
||||||
@ -226,26 +220,26 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut
|
|||||||
|
|
||||||
Skill::Link|
|
Skill::Link|
|
||||||
Skill::LinkPlus |
|
Skill::LinkPlus |
|
||||||
Skill::LinkPlusPlus => link(source, target, resolutions, skill), // target is immune to magic damage and fx
|
Skill::LinkPlusPlus => link(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Silence|
|
Skill::Silence|
|
||||||
Skill::SilencePlus |
|
Skill::SilencePlus |
|
||||||
Skill::SilencePlusPlus => silence(source, target, resolutions, skill), // target cannot cast spells
|
Skill::SilencePlusPlus => silence(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Siphon|
|
Skill::Siphon|
|
||||||
Skill::SiphonPlus |
|
Skill::SiphonPlus |
|
||||||
Skill::SiphonPlusPlus => siphon(source, target, resolutions, skill), // dot
|
Skill::SiphonPlusPlus => siphon(source, target, resolutions, skill),
|
||||||
Skill::SiphonTick|
|
Skill::SiphonTick|
|
||||||
Skill::SiphonTickPlus |
|
Skill::SiphonTickPlus |
|
||||||
Skill::SiphonTickPlusPlus => siphon_tick(source, target, resolutions, skill), // dot
|
Skill::SiphonTickPlusPlus => siphon_tick(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Slay|
|
Skill::Slay|
|
||||||
Skill::SlayPlus |
|
Skill::SlayPlus |
|
||||||
Skill::SlayPlusPlus => slay(source, target, resolutions, skill), // hybrid dmg self heal
|
Skill::SlayPlusPlus => slay(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Sleep|
|
Skill::Sleep|
|
||||||
Skill::SleepPlus |
|
Skill::SleepPlus |
|
||||||
Skill::SleepPlusPlus => sleep(source, target, resolutions, skill), // heal stun
|
Skill::SleepPlusPlus => sleep(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Restrict|
|
Skill::Restrict|
|
||||||
Skill::RestrictPlus |
|
Skill::RestrictPlus |
|
||||||
@ -261,24 +255,24 @@ pub fn resolve(skill: Skill, source: &mut Construct, target: &mut Construct, mut
|
|||||||
|
|
||||||
Skill::Break|
|
Skill::Break|
|
||||||
Skill::BreakPlus |
|
Skill::BreakPlus |
|
||||||
Skill::BreakPlusPlus => break_(source, target, resolutions, skill), // no damage stun, adds vulnerable
|
Skill::BreakPlusPlus => break_(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::Triage|
|
Skill::Triage|
|
||||||
Skill::TriagePlus |
|
Skill::TriagePlus |
|
||||||
Skill::TriagePlusPlus => triage(source, target, resolutions, skill), // hot
|
Skill::TriagePlusPlus => triage(source, target, resolutions, skill),
|
||||||
|
|
||||||
Skill::TriageTick|
|
Skill::TriageTick|
|
||||||
Skill::TriageTickPlus |
|
Skill::TriageTickPlus |
|
||||||
Skill::TriageTickPlusPlus => triage_tick(source, target, resolutions, skill), // hot
|
Skill::TriageTickPlusPlus => triage_tick(source, target, resolutions, skill),
|
||||||
|
|
||||||
// Base Skills
|
// Base Skills
|
||||||
Skill::Attack => attack(source, target, resolutions, skill),
|
Skill::Attack => attack(source, target, resolutions, skill),
|
||||||
Skill::Block => block(source, target, resolutions, skill),
|
Skill::Block => block(source, target, resolutions, skill),
|
||||||
Skill::Buff => buff(source, target, resolutions, skill),
|
Skill::Buff => buff(source, target, resolutions, skill),
|
||||||
Skill::Debuff => debuff(source, target, resolutions, skill), // speed slow
|
Skill::Debuff => debuff(source, target, resolutions, skill),
|
||||||
Skill::Stun => stun(source, target, resolutions, skill),
|
Skill::Stun => stun(source, target, resolutions, skill),
|
||||||
|
|
||||||
//Triggered
|
// Triggered
|
||||||
Skill::Electrocute |
|
Skill::Electrocute |
|
||||||
Skill::ElectrocutePlus |
|
Skill::ElectrocutePlus |
|
||||||
Skill::ElectrocutePlusPlus => panic!("should only trigger from electrify hit"),
|
Skill::ElectrocutePlusPlus => panic!("should only trigger from electrify hit"),
|
||||||
@ -1267,8 +1261,6 @@ impl Skill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn defensive(&self) -> bool {
|
pub fn defensive(&self) -> bool {
|
||||||
let mut rng = thread_rng();
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Skill::Amplify|
|
Skill::Amplify|
|
||||||
Skill::AmplifyPlus |
|
Skill::AmplifyPlus |
|
||||||
@ -1311,10 +1303,6 @@ impl Skill {
|
|||||||
Skill::TriagePlus |
|
Skill::TriagePlus |
|
||||||
Skill::TriagePlusPlus => true,
|
Skill::TriagePlusPlus => true,
|
||||||
|
|
||||||
Skill::Banish |
|
|
||||||
Skill::BanishPlus |
|
|
||||||
Skill::BanishPlusPlus => rng.gen_bool(0.5),
|
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2021,7 +2009,7 @@ mod tests {
|
|||||||
assert!(y.affected(Effect::Reflect));
|
assert!(y.affected(Effect::Reflect));
|
||||||
|
|
||||||
let mut results = vec![];
|
let mut results = vec![];
|
||||||
results = resolve(Skill::Blast, &mut x, &mut y, results);
|
results = resolve_skill(Skill::Blast, &mut x, &mut y, results);
|
||||||
|
|
||||||
assert!(x.green_life() < 1024);
|
assert!(x.green_life() < 1024);
|
||||||
|
|
||||||
@ -2052,7 +2040,7 @@ mod tests {
|
|||||||
y.blue_life.force(0);
|
y.blue_life.force(0);
|
||||||
x.green_life.reduce(512);
|
x.green_life.reduce(512);
|
||||||
|
|
||||||
let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]);
|
let mut results = resolve_skill(Skill::Siphon, &mut x, &mut y, vec![]);
|
||||||
|
|
||||||
assert!(y.affected(Effect::Siphon));
|
assert!(y.affected(Effect::Siphon));
|
||||||
assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.multiplier())));
|
assert!(x.green_life() == (512 + 256.pct(Skill::SiphonTick.multiplier()) + 220.pct(Skill::SiphonTick.multiplier())));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user