Large initial push

7 factions already implemented:

- Norther Realms
- Nilfgaard Empire
- Scoia'Tael
- Monsters
- Skellige
- Redenia
- Toussaint

In progress:
- Velen

To do:
- Witchers
- Wild Hunt
This commit is contained in:
Sylver 2023-11-02 02:02:29 +01:00
commit 9e5f83df37
1790 changed files with 34832 additions and 0 deletions

398
.gitignore vendored Normal file
View File

@ -0,0 +1,398 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml

11
LICENSE Normal file
View File

@ -0,0 +1,11 @@
“Commons Clause” License Condition v1.0
The Software is provided to you by the Licensor under the License, as defined below, subject to the following condition.
Without limiting other conditions in the License, the grant of rights under the License will not include, and the License does not grant to you, right to Sell the Software.
For purposes of the foregoing, “Sell” means practicing any or all of the rights granted to you under the License to provide to third parties, for a fee or other consideration (including without limitation fees for hosting or consulting/ support services related to the Software), a product or service whose value derives, entirely or substantially, from the functionality of the Software. Any license notice or attribution required by the License must also include this Commons Cause License Condition notice.
Software: Gwent Classic
License: MIT
Licensor: Arun Sundaram

31
README.md Normal file
View File

@ -0,0 +1,31 @@
# gwent-classic-v4.0
![cover](https://user-images.githubusercontent.com/26311830/116256903-f1599b00-a7b6-11eb-84a1-16dcb5c9bfc6.jpg)
**WORK IN PROGRESS**
A browser remake of the original Gwent minigame from The Witcher 3: Wild Hunt.<br/>Click [here](https://romain-durban.github.io/gwent-classic-v4.0/) to play.
Unlike v3.0 remake, this one is a much deeper redesign, addressing further balance issues. Number of cards is drastically reduced and abilities have been tweaked (and made more complex).
This is based on the huge work from [Novigrad Tavern](https://www.ebay.com/usr/novigrad_tavern), this remake is an attempt to implement the cards they have designed (there are some minor differences though).
## Improvements
Are listed here only the modifications done in this present fork, more were done before by other developers.
TBA
#### Changes
Are listed here only the modifications done in this present fork, more were done before by other developers.
TBA
### Operations included with the keyboard:
**"E"** starts the game<br />
**"X"** uses or modifies the leader card<br />
**"Q"** closes the card explanation windows<br />
**"Space"** passes the round<br />
**"Enter"** plays the cards<br />
**Arrows** select cards on the carousel

2074
abilities.js Normal file

File diff suppressed because it is too large Load Diff

6796
cards.js Normal file

File diff suppressed because it is too large Load Diff

9604
cards.old.js Normal file

File diff suppressed because it is too large Load Diff

848
decks.html Normal file
View File

@ -0,0 +1,848 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Gwent 4.0</title>
<meta charset="utf-8" />
<meta name="author" content="Arun Sundaram & Reynolds Costa" />
<meta name="description" content="Play the original Gwent minigame from The Witcher 3 in your browser! Includes cards from Hearts of Stone and Blood & Wine!" />
<meta name="keywords" content="arun, sundaram, reynolds, costa, randompianist, programmer, gwent, tw3, witcher, card, game, gamedev, play" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="rules.css" />
<link rel="stylesheet" type="text/css" href="pure.min.css" />
<script src="rules.js"></script>
<script src="decks.js"></script>
<script src="cards.js"></script>
<script src="abilities.js"></script>
</head>
<body>
<div id="layout">
<!-- Menu toggle -->
<a href="#menu" id="menuLink" class="menu-link">
<!-- Hamburger icon -->
<span></span>
</a>
<div id="menu">
<div class="pure-menu">
<a class="pure-menu-heading" href="">Gwent 4.0</a>
<ul class="pure-menu-list">
<li class="pure-menu-item menu-other-page"><a href="index.html" class="pure-menu-link">Play Game</a></li>
<li class="pure-menu-item menu-other-page"><a href="rules.html" class="pure-menu-link">Rules</a></li>
<li class="pure-menu-item"><a href="#realms" class="pure-menu-link">Northern Realms</a></li>
<li class="pure-menu-item"><a href="#nilfgaard" class="pure-menu-link">Nilfgaard Empire</a></li>
<li class="pure-menu-item"><a href="#monsters" class="pure-menu-link">Monsters</a></li>
<li class="pure-menu-item"><a href="#scoiatael" class="pure-menu-link">Scoia'Tael</a></li>
<li class="pure-menu-item"><a href="#skellige" class="pure-menu-link">Skellige</a></li>
<li class="pure-menu-item"><a href="#witcher_universe" class="pure-menu-link">Witcher Universe</a></li>
<li class="pure-menu-item"><a href="#toussaint" class="pure-menu-link">Toussaint</a></li>
<li class="pure-menu-item"><a href="#lyria_rivia" class="pure-menu-link">Lyria & Rivia</a></li>
<li class="pure-menu-item"><a href="#syndicate" class="pure-menu-link">Syndicate</a></li>
<li class="pure-menu-item"><a href="#zerrikania" class="pure-menu-link">Zerrikania</a></li>
</ul>
</div>
</div>
<div id="main">
<div class="header">
<h1>Gwent 4.0</h1>
<h2>The Witcher 3 minigame pulled to the browser and pushed further</h2>
</div>
<div class="content">
<!-- INTRO -->
<h2 class="content-subhead" id="setup">Building a deck in Gwent</h2>
<p>Gwent was initially a deck building game. Now, the main difficulty to make Gwent interesting and playable with 2 human players is ... building decks. No kidding.</p>
<p>
In The Witcher 3, the purpose was to build the strongest and most imbalance deck. Of course, players wanted to win and the computer does not complain about always losing.
As a consequence, there was obvious choices of decks to have the highest chances of victory, using Nilfgaard or the Northern Realms as faction, with a lot of spies, medics and decoys.
Such decks are boring to play, especially as multiplier, so we want to get out of this dead end and make something more interesting out of the existing game.
</p>
<p>
Firstly, we want to accept one thing, to have more enjoyable games, decks must be weaker. They must contain less broken cards to leave more room for actual strategy.
Goal is no longer to win 100% of games but instead to enjoy all games.
</p>
<p>
There hasn't yet been any consensus about the best method to constitute a balanced and enjoyable deck. Various discussions can be found on the internet but most of them suggest the draw method, with variations.
Basically, the idea is like this:
<ul>
<li>Pick a Faction (randomly if you prefer)</li>
<li>Pick a Leader (randomly if you prefer)</li>
<li><b>Now constitue your deck as follows (or with your own variation):</b></li>
<li>Shuffle your deck</li>
<li>Draw 3 cards. Keep 1 and discard the 2 others (variation, you can keep two, but only a limited amount of times)</li>
<li>Do this until you have at least 22 unit cards and max 10 special cards.</li>
</ul>
</p>
<p>
For more balance, it is recommanded to add more restrictions, such as:
<ul>
<li>Max 5 heroes per deck.</li>
<li>Max 2 spies per deck.</li>
</ul>
</p>
<!-- APPROACH PROPOSAL -->
<h2 class="content-subhead" id="setup">Our approach</h2>
<p>
The above approach has the advantage to add more variety, because there is some uncertainty in the building of the deck.
It is less likely to face several times the same deck.
We however see several big drawbacks which we don't like:
<ul>
<li>Building your deck over and over is slow. Unless you have fun building a new deck again incrementaly, this is a waste of time.</li>
<li>You are less likely to build an efficient deck with a specific playstyle.</li>
<li>The outcome will be a rather random mix of cards with a limited connection to the Witcher Universe.</li>
</ul>
</p>
<p>
<b>Consequently, we have built a set of decks with the following method and state of mind:</b>
<ul>
<li>A deck must be coherent to a part of the Witcher universe.</li>
<li>A deck must have a playstyle that is unique if possible, but at least different from most decks.</li>
<li>Preferably a deck must have 5 heroes, + or - 1.</li>
<li>Preferably a deck must have between 22 and 30 unit cards (heroes included).</li>
<li>Preferably a deck must have between 5 and 10 special cards, usually around 7.</li>
<li>The weight of a deck should be balanced as compared to the weight of the other decks (often between 270 and 300 in our method).</li>
<li>A character should preferably be available in only one deck.</li>
</ul>
</p>
<!-- WEIGHTS -->
<h2 class="content-subhead" id="setup">Wait, did you say weight?</h2>
<p>
Well yes, our main method for balancing decks is actually similar to the modern Gwent game.
We try to compute a weight for each card (except special cards), which is comparable to the "Provision" value the modern Gwent game is using.
</p>
<p>
Don't be too scared! Just like the classic Gwent 3.0 is simpler with less abilities, it does not need to fine tune the weight of each card accurately.
Basically, the idea is to statistically estimate the strength potential of a card, based on its abilities. This isn't easy to do it for each ability, but we tried to have a good estimation.
Here are the big lines:
<ul>
<li>A basic unit card with a strength of 7 has a weight of 7. Simple, we play it, it adds 7 points to the total score. Of course this can be altered, in good or bad, but this is not the point.</li>
<li>On the other hand, a hero of strength 10 will also add 10 points, but it cannot be altered. We took the decision to apply a multiplier of 2, so the weight of this hero would be 20. This is the trickiest part to weight, the fact it can or cannot be affected by abilities. However, since most hero cards have the same strength (around 10), and if each deck has almost the same amount of heroes, this multiplier does not really have a significance in the balance/total weight.</li>
<li>
Before talking about the other abilities weight, we need to jump to the end to justify some choices.
We have seen that overall, the average weight of a card is around 10, heroes included. It goes down to around 8 without heroes. So this gives us an idea of the average value/potential of a card.
</li>
<li>Let's start with the infamous spy ability. It gives 2 cards but gives some strength to the enemy, so the weight is <code> (2*10) - spy_strength</code> (10 being the average weight of a card).</li>
<li>Similarly, a medic brings back a unit card (not a hero) of average weight 8. However some of these units won't be able to use their ability (such as Muster), so we considerd the weight to be <code>7 + medic_strength</code>.</li>
<li>
Muster and Tight Bond: these are a little tricky because they work in groups. You have several copies of the same card, but their potential changes a lot when used correcly.
Actual potential also changes depending on the number of copies available.
In short, we have chosen to apply a multiplier of 2 for both. It simplifies their behaviour, but a probability analysis of these abilities has shown that, compared to a basic card being played, the effect of these abilities multiples the outcome potential by around 2.
</li>
<li>
We will not go into details for each of the other abilities because they are even more guesses (very situational). Most of the other abilities have an added weight of 5 (added to the unit strength), such as for Scorch, Morale Boost or Summon Avenger.
The main exception to this is Commander's Horn which has an added weight of 10. We have also decided to give an added weigth of 2 to agile cards, which have the option to be played in 2 rows;
</li>
<li>For now, all leaders have a weight of 20 by default. We haven't yet gone into the trouble of fine tuning their weight.</li>
</ul>
</p>
<p>
Let's be realistic, this is not exact science, this is not perfect.
But try it yourself and you'll see that it is already enough to show when a deck is far stronger (put a lot of Tight Bond cards in a deck) or very weak.
We believe it does its job well enough to end up with decks of comparable potential. Tests have shown good results.
</p>
<p>
With all this in mind, we came up with a selection of decks which we think remain true to the Witcher Universe all while being interesting to play.
</p>
<!-- DECKS -->
<h2 class="content-subhead" id="decks">Suggestion of decks</h2>
<p>
For more details about the content of these decks, launch the game and inspect them!
</p>
<!-- NORTHERN REALMS -->
<a id="realms" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/realms_foltest_lord.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-realms">Northern Realms - Temeria</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["nr_foltest_lord"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[0]) + " (" + premade_deck[0].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["foltest_lord"].description);</script><br />
<b>Play style:</b> Revolves around Tight Bond, Spy and siege cards.
</p>
<p>
This deck focuses mostly on units and characters from the realm of Temeria, including Blue Stripes units.
Spy units will increase the chances of getting Tight Bond units together. You will also have a good selection of Siege units to rely on.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/realms_foltest_siegemaster.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-realms">Northern Realms - Aedirn & Kaedwen</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["nr_foltest_siegemaster"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[1]) + " (" + premade_deck[1].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["foltest_siegemaster"].description);</script><br />
<b>Play style:</b> Revolves around Tight Bond, Morale Boost and siege cards.
</p>
<p>
This deck focuses mostly on units and characters from the realms of Aedirn and Kaedwen.
It has a great selection of siege units (several with the Tight Bond ability) which can be boosted with the several Morale Boost applying on the Siege row.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/realms_queen_calanthe.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-realms">Northern Realms - Cintra</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["nr_queen_calanthe"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[2]) + " (" + premade_deck[2].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["queen_calanthe"].description);</script><br />
<b>Play style:</b> Revolves around Muster, Slaughter of Cintra and Summon Avenger.
</p>
<p>
This deck focuses mostly on units and characters from the realm of Cintra and some minor neighbour realms, such as Kerack or Cidaris.
It also brings the power of the Skellige fleet thanks to the presence of Eist Tuirseach.
The idea of the deck is to emphasize the slaughter of Cintra and its rebirth, hence the selection of abilities.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/realms_radovid_ruthless.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-realms">Northern Realms - Redania</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["nr_radovid_ruthless"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[3]) + " (" + premade_deck[3].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["radovid_ruthless"].description);</script><br />
<b>Play style:</b> Revolves around Spies, Medic and Tall units.
</p>
<p>
This deck focuses mostly on units and characters from the realm of Redania.
Its many strong units combine well with spies and medics. You also have the option to try to combine the Crinfrid Reavers.
</p>
</div>
</div>
</div>
<!-- NILFGAARD -->
<a id="nilfgaard" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/nilfgaard_emhyr_relentless.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-nilfgaard">Nilfgaard Empire - Army</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["ne_emhyr_relentless"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[4]) + " (" + premade_deck[4].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["emhyr_relentless"].description);</script><br />
<b>Play style:</b> Revolves around Spies, Medic, Commander's Horn and some Tight Bond.
</p>
<p>
This deck focuses mostly on army units of the Empire of Nilfgaard.
It aims at representing the mass of the army of Nilfgaard, hence the higher amount of cards.
Its many medics and few spies allow it to bring in more units on the board, which can be amplified by the available Commander's Horns.
It has more heroes than usual, but some of them are weak hero cards.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/nilfgaard_emhyr_emperor.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-nilfgaard">Nilfgaard Empire - Nobility</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["ne_emhyr_emperor"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[5]) + " (" + premade_deck[5].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["emhyr_emperor"].description);</script><br />
<b>Play style:</b> Revolves around Spies and Tight Bond.
</p>
<p>
This deck focuses mostly on nobles and aristocrats of the Empire of Nilfgaard.
It aims at representing the court of the Empire and its schemes.
The many spies allow to make great use of the Impera Brigade.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/nilfgaard_emhyr_invader_of_the_north.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-nilfgaard">Nilfgaard Empire - Second Army</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["ne_emhyr_invader_of_the_north"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[6]) + " (" + premade_deck[6].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["emhyr_invader"].description);</script><br />
<b>Play style:</b> Revolves around Muster, Scorch, Medic and Spies.
</p>
<p>
This deck focuses on other army units of the Empire of Nilfgaard.
Unlike the other army deck, this one features some Muster and Scorch and relies less on the mass.
</p>
</div>
</div>
</div>
<!-- MONSTERS -->
<a id="monsters" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/monsters_eredin_bringer_of_death.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-monsters">Monsters - Wild Hunt & Giants</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["mo_eredin_bringer_of_death"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[7]) + " (" + premade_deck[7].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["eredin_bringer_of_death"].description);</script><br />
<b>Play style:</b> Revolves around Muster and tall units.
</p>
<p>
This deck focuses on cards related to the Wild Hunt along with other giant monsters.
Even though Muster of Melee units is the main focus of this deck, it's got a decent selection of Siege units as well.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/monsters_eredin_commander.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-monsters">Monsters - Beasts & Creatures</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["mo_eredin_commander"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[8]) + " (" + premade_deck[8].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["eredin_commander"].description);</script><br />
<b>Play style:</b> Revolves around a lot of Muster units.
</p>
<p>
This deck focuses on the wide variety of monters in the Witcher universe, the smaller but more numerous ones.
This deck relies heavily on a lot of Muster, mostly on the Melee row. This is why this deck has more cards than usual.
It can quickly swarm the opponent, though the cards must be played carefully because you can quickly exhaust of of your musters.
</p>
</div>
</div>
</div>
<!-- SCOIA'TAEL -->
<a id="scoiatael" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/scoiatael_francesca_daisy.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-scoiatael">Scoia'Tael - Commandos</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["sc_francesca_daisy"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[9]) + " (" + premade_deck[9].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["francesca_daisy"].description);</script><br />
<b>Play style:</b> Revolves around Muster and Medic units.
</p>
<p>
This deck focuses on the non-human guerilla fighters of Scoia'Tael.
It relies mostly on Muster, combined with a good amount of medic cards.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/scoiatael_francesca_beautiful.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-scoiatael">Scoia'Tael - Independant Realms</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["sc_francesca_beautiful"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[10]) + " (" + premade_deck[10].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["francesca_beautiful"].description);</script><br />
<b>Play style:</b> Revolves around Muster and Morale Boost units.
</p>
<p>
This deck focuses on the more passific non-human realms, such sa Brokilon, Dol Blathanna and Mahakam.
It relies on a combination of Muster and Morale Boost.
</p>
</div>
</div>
</div>
<!-- SKELLIGE -->
<a id="skellige" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/skellige_crach_an_craite.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-skellige">Skellige - Ard Skellig & Svalblod</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["sk_crach_an_craite"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[11]) + " (" + premade_deck[11].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["crach_an_craite"].description);</script><br />
<b>Play style:</b> Revolves around Berserker, Muster and Tight Bond units.
</p>
<p>
This deck focuses on the main island of Skellige, Ard Skellige, and the two clans base on it (an Craite and Drummond) along with the Svalblod cult.
This decks focuses on the Berserker/Mardroeme abilities, along with some Tight Bond and one very powerfull Muster.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/skellige_king_bran.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-skellige">Skellige - Small Islands</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["sk_king_bran"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[12]) + " (" + premade_deck[12].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["king_bran"].description);</script><br />
<b>Play style:</b> Revolves around Muster, Tight Bond and Medic units.
</p>
<p>
This deck focuses on the other small islands of Skellige and the clans living on them.
It relies on a good combination of Tight Bond and Muster units, supported by a decent amount of medics.
</p>
</div>
</div>
</div>
<!-- WITCHER UNIVERSE -->
<a id="witcher_universe" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/witcher_universe_vilgefortz_magician_kovir.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-witcher_universe">Witcher Universe - Fighters & Mages</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["wu_vilgefortz_magician_kovir"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[13]) + " (" + premade_deck[13].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["vilgefortz_magician_kovir"].description);</script><br />
<b>Play style:</b> Revolves around Muster and Medic units.
</p>
<p>
This deck focuses on some famous fighters and mages of the Witcher universe which aren't very attached to a realm.
It relies mostly on Muster and Medic units.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/witcher_universe_cosimo_malaspina.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-witcher_universe">Witcher Universe - Witchers</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["wu_cosimo_malaspina"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[14]) + " (" + premade_deck[14].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["cosimo_malaspina"].description);</script><br />
<b>Play style:</b> Revolves around Witcher Schools, Resilience and Scorch.
</p>
<p>
This deck focuses on the actual witchers and their school.
It uses a specific set of abilities that encourage to play witchers of the same school together along with their keep.
Instead of the usual special cards, signs are available with some unique abilities.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/witcher_universe_alzur_maker.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-witcher_universe">Witcher Universe - Demons & Creatures</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["wu_alzur_maker"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[15]) + " (" + premade_deck[15].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["alzur_maker"].description);</script><br />
<b>Play style:</b> Revolves around Muster, Scorch and Spy units.
</p>
<p>
This deck focuses on some rare creatures and demons of the witcher universe, including the characters of Heart of Stone.
It relies mostly on Muster and Scorch units, supported by some spies and overall strong units.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/witcher_universe_vilgefortz_sorcerer.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-witcher_universe">Witcher Universe - Geralt & Friends</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["wu_vilgefortz_sorcerer"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[16]) + " (" + premade_deck[16].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["vilgefortz_sorcerer"].description);</script><br />
<b>Play style:</b> Revolves around Summon Avenger and Medic units.
</p>
<p>
This deck focuses on Geralt of Rivia, his close friends and various allies within the Witcher Universe.
Summon Avenger plays an important part of the deck, with Roach & Kelpie summoning Geralt and Ciri.
Combined with Medic units, it can become very powerfull.
</p>
</div>
</div>
</div>
<!-- TOUSSAINT -->
<a id="toussaint" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/toussaint_anna_henrietta_duchess.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-toussaint">Toussaint - Knights & Nobles</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["to_anna_henrietta_duchess"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[17]) + " (" + premade_deck[17].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["anna_henrietta_duchess"].description);</script><br />
<b>Play style:</b> Revolves around Tight Bond, Muster and tall units.
</p>
<p>
This deck focuses on the knights and noble people of Toussaint.
It is composed of mostly strong units, some with Tight Bond, which can be very strong if left unckecked.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/toussaint_anna_henrietta_ladyship.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-toussaint">Toussaint - Monsters & Vampires</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["to_anna_henrietta_ladyship"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[18]) + " (" + premade_deck[18].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["anna_henrietta_ladyship"].description);</script><br />
<b>Play style:</b> Revolves around Muster and tall units.
</p>
<p>
This deck focuses on the monsters and vampires of Toussaint.
It is composed of mostly strong units along with some Muster units. It is a quite resilient mix.
</p>
</div>
</div>
</div>
<!-- LYRIA & RIVIA -->
<a id="lyria_rivia" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/lyria_rivia_meve_princess.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-lyria_rivia">Lyria & Rivia - First Line</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["lr_meve_princess"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[19]) + " (" + premade_deck[19].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["meve_princess"].description);</script><br />
<b>Play style:</b> Revolves around Muster and Medic units.
</p>
<p>
This deck focuses on the first line of the Lyria & Rivia army (mix of Melee and Ranged units).
It is composed of a good mix of Muster units, supported by medics. The shield special cards give great potential to the concentration of Muster units.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/lyria_rivia_meve_white_queen.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-lyria_rivia">Lyria & Rivia - Rear Guard</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["lr_meve_white_queen"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[20]) + " (" + premade_deck[20].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["meve_white_queen"].description);</script><br />
<b>Play style:</b> Revolves around Scorch, Spy and Medic units.
</p>
<p>
This deck focuses on the first line of the Lyria & Rivia army (mix of Melee and Ranged units).
It is composed of a good mix of Muster units, supported by medics. The shield special cards give great potential to the concentration of Muster units.
</p>
</div>
</div>
</div>
<!-- SYNDICATE -->
<a id="syndicate" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/syndicate_cyrus_hemmelfart.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-syndicate">Syndicate - Church of Eternal Fire</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["sy_cyrus_hemmelfart"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[21]) + " (" + premade_deck[21].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["cyrus_hemmelfart"].description);</script><br />
<b>Play style:</b> Revolves around Witch Hunt, Muster and Dimeritium.
</p>
<p>
This deck focuses on the cult of the Eternal Fire along with the Order of the Flaming Rose.
Due to its nature, it has the unique ability Witch Hunt, combined with Muster and Dimeritium Shackles special cards.
This decks looks weak in numbers, but it is quite aggressive.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/syndicate_azar_javed.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-syndicate">Syndicate - Salamandra</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["sy_azar_javed"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[22]) + " (" + premade_deck[22].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["azar_javed"].description);</script><br />
<b>Play style:</b> Revolves around Muster, Scorch and Summon Avenger.
</p>
<p>
This deck focuses on the Salamandra gang.
It relies mostly on many Scorch units along with some Muster and Summon Avenger, making it an aggressive deck as well.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/syndicate_cyprian_wiley.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-syndicate">Syndicate - Novidgrad Gangs</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["sy_cyprian_wiley"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[23]) + " (" + premade_deck[23].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["cyprian_wiley"].description);</script><br />
<b>Play style:</b> Revolves around Spy, Tight Bond and Muster.
</p>
<p>
This deck focuses on gangs of Novigrad (the core of the Syndicate).
It relies mostly heavily on many spies to make the multiple Tight Bond units work.
</p>
</div>
</div>
</div>
<!-- ZERRIKANIA -->
<a id="zerrikania" />
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/zerrikania_zerrikanterment.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-zerrikania">Zerrikania - Dragon Cult</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["ze_zerrikanterment"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[24]) + " (" + premade_deck[24].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["zerrikanterment"].description);</script><br />
<b>Play style:</b> Revolves around Whorshipper and Whorshipped units along with Medic.
</p>
<p>
This deck focuses on the cult of dragons in Zerrikania.
It requires to combine on the board dragons and faithel to make use of the powerfull boost.
Since dragons are rather strong units anyway, you can either play all in a massive round, or spread them accross several rounds.
Usage of Medics will allow to bring back dragons later.
</p>
</div>
</div>
</div>
<div class="pure-g">
<div class="pure-u-1-8">
<img class="pure-img-responsive card-preview" src="img/sm/zerrikania_rarog.jpg" alt="Deck leader">
</div>
<div class="pure-u-7-8">
<div class="deck-list-name faction-zerrikania">Zerrikania - Army</div>
<div class="deck-list-description">
<p>
<b>Leader: </b>
<script>document.write(card_dict["ze_rarog"].name);</script><br />
<b>Deck weight: </b>
<script>document.write(calcDeckWeight(premade_deck[25]) + " (" + premade_deck[25].cards.reduce((a, e) => a + e[1], 0) + " cards)");</script><br />
<b>Leader Ability: </b>
<script>document.write(ability_dict["rarog"].description);</script><br />
<b>Play style:</b> Revolves around Inspire, Scorch and a various selection of abilities.
</p>
<p>
This deck focuses on the fighting units and creatures of Zerrikania.
It primarly features Free Warriors which can boost each other.
This deck also has a balanced variety of most abilities, especially Scorch and Medics;
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

367
decks.js Normal file
View File

@ -0,0 +1,367 @@
let default_decks = [{
"title": "Northern Realms Deck 1 - Spies & Bonds",
"description": "Deck relying mostly on Tight Bonds units and Spies",
"leader": "nr_foltest_lord",
"faction": "realms",
"cards": [
["spe_horn", 1],
["spe_decoy", 2],
["spe_scorch", 1],
["spe_clear", 1],
["spe_fog", 1],
["spe_rain", 1],
["nr_ciri", 1],
["nr_geralt", 1],
["nr_natalis", 1],
["nr_vernon", 1],
["nr_stennis", 1],
["nr_keira", 1],
["nr_dijkstra", 1],
["nr_sheala", 1],
["nr_thaler", 1],
["nr_triss", 1],
["nr_olgierd", 1],
["nr_crinfrid_1", 1],
["nr_crinfrid_2", 1],
["nr_crinfrid_3", 1],
["nr_villen", 1],
["nr_blue_stripes_1", 1],
["nr_blue_stripes_2", 1],
["nr_blue_stripes_3", 1],
["nr_poor_infantry_1", 1],
["nr_poor_infantry_2", 1],
["nr_poor_infantry_3", 1],
["nr_banner_nurse", 1]
]
},
{
"title": "Nilfgaard Deck 1 - Spies & Bonds",
"description": "Deck relying mostly on Spies, Emissaries, some Tight Bond and strong ranged units",
"leader": "ne_emhyr_invader_of_the_north",
"faction": "nilfgaard",
"cards": [
["spe_horn", 1],
["spe_decoy", 2],
["spe_scorch", 1],
["spe_clear", 1],
["ne_ciri", 1],
["ne_geralt", 1],
["ne_yennefer", 1],
["ne_black_archer", 1],
["ne_black_archer_1", 1],
["ne_letho", 1],
["ne_moorvran", 1],
["ne_stefan", 1],
["ne_shilard", 1],
["ne_young_emissary_1", 1],
["ne_young_emissary_2", 1],
["ne_vattier", 1],
["ne_impera_brigade_1", 1],
["ne_impera_brigade_2", 1],
["ne_impera_brigade_3", 1],
["ne_impera_brigade_4", 1],
["ne_archer_support", 1],
["ne_archer_support_1", 1],
["ne_cahir", 1],
["ne_renuald", 1],
["ne_vreemde", 1],
["ne_morteisen", 1],
["ne_albrich", 1],
]
},
{
"title": "Monsters Deck 1 - Massive Muster",
"description": "Deck relying on a lot of muster units",
"leader": "mo_eredin_commander",
"faction": "monsters",
"cards": [
["spe_horn", 1],
["spe_decoy", 1],
["spe_scorch", 1],
["spe_clear", 1],
["spe_fog", 1],
["spe_rain", 1],
["mo_draug", 1],
["mo_imlerith", 1],
["mo_leshen", 1],
["mo_gaunter_odimm", 1],
["mo_kayran", 1],
["mo_witch_velen", 1],
["mo_witch_velen_1", 1],
["mo_witch_velen_2", 1],
["mo_foglet", 1],
["mo_arachas_behemoth", 1],
["mo_nekker", 1],
["mo_nekker_1", 1],
["mo_nekker_2", 1],
["mo_toad", 1],
["mo_katakan", 1],
["mo_arachas", 1],
["mo_arachas_1", 1],
["mo_arachas_2", 1],
["mo_plague_maiden", 1],
["mo_bruxa", 1],
["mo_ekkima", 1],
["mo_fleder", 1],
["mo_garkain", 1],
["mo_gaunter_odimm_darkness", 1]
]
},
{
"title": "Scoia'Tael Deck 1 - Agile & Muster",
"description": "Deck relying on agile units and muster",
"leader": "sc_francesca_pureblood",
"faction": "scoiatael",
"cards": [
["spe_horn", 1],
["spe_decoy", 1],
["spe_scorch", 1],
["spe_clear", 1],
["spe_frost", 1],
["spe_rain", 1],
["sc_iorveth", 1],
["sc_isengrim", 1],
["sc_schirru", 1],
["sc_mahakam", 1],
["sc_mahakam_1", 1],
["sc_saskia", 1],
["sc_vrihedd_brigade_1", 1],
["sc_vrihedd_brigade", 1],
["sc_mysterious_elf", 1],
["sc_filavandrel", 1],
["sc_toruviel", 1],
["sc_yaevinn", 1],
["sc_dol_infantry_1", 1],
["sc_dol_infantry_2", 1],
["sc_havekar_support", 1],
["sc_havekar_support_1", 1],
["sc_havekar_support_2", 1],
["sc_elf_skirmisher", 1],
["sc_elf_skirmisher_1", 1],
["sc_elf_skirmisher_2", 1],
["sc_vrihedd_cadet", 1],
["sc_havekar_nurse", 1],
["sc_havekar_nurse_2", 1],
["sc_temptress", 1]
]
},
{
"title": "Skellige Deck 1 - Berserkers",
"description": "Deck relying mostly on berserker units",
"leader": "sk_holger",
"faction": "skellige",
"cards": [
["spe_mardroeme", 2],
["spe_skellige_fleet", 2],
["spe_horn", 1],
["spe_decoy", 1],
["spe_scorch", 1],
["spe_clear", 1],
["spe_frost", 1],
["spe_rain", 1],
["sk_hjalmar", 1],
["sk_brokva_archer", 1],
["sk_cerys", 1],
["sk_ermion", 1],
["sk_yennefer", 1],
["sk_longship_1", 1],
["sk_longship_3", 1],
["sk_longship_2", 1],
["sk_berserker", 1],
["sk_shield_maiden_1", 1],
["sk_shield_maiden_2", 1],
["sk_shield_maiden_3", 1],
["sk_light_longship_1", 1],
["sk_light_longship_2", 1],
["sk_light_longship_3", 1],
["sk_vildkaarl", 1],
["sk_gremist", 1],
["sk_young_berserker_1", 1],
["sk_young_berserker_2", 1],
["sk_young_berserker_3", 1],
["sk_arnvald", 1],
["sk_kambi", 1]
]
},
{
"title": "Redania Deck 1 - Witch Hunt",
"description": "Deck relying mostly on witch hunters",
"leader": "re_radovid_mad_king",
"faction": "redania",
"cards": [
["spe_horn", 1],
["spe_decoy", 2],
["spe_execution", 1],
["spe_royal_decree", 2],
["spe_clear", 1],
["spe_rain", 1],
["re_carlo_varese", 1],
["re_cyprian_wiley", 1],
["re_francis_bedlam", 1],
["re_black_cat_dog", 1],
["re_redanian_elite", 1],
["re_rico_meiersdorf", 1],
["re_caretaker", 1],
["re_redanian_knight_1", 1],
["re_redanian_knight_2", 1],
["re_eternal_fire_priest", 1],
["re_graden", 1],
["re_kurt", 1],
["re_moreelse", 1],
["re_nathaniel_pastodi", 1],
["re_shani", 1],
["re_witch_hunter", 1],
["re_olgierd", 1],
["re_vlodimir_von_everec", 1],
["re_cyrus_hemmelfart", 1],
["re_sigi_reuven", 1],
["re_caleb_menge", 1],
["re_iris_von_everec", 1]
]
},
{
"title": "Toussaint Deck 1 - Monsters & Knights",
"description": "Deck relying mostly Toussaint monsters, spies and muster",
"leader": "to_anna_henrietta_little_weasel",
"faction": "toussaint",
"cards": [
["spe_horn", 1],
["spe_decoy", 2],
["spe_scorch", 1],
["spe_toussaint_wine", 2],
["spe_clear", 1],
["to_unseen_elder", 1],
["to_damien_tour", 1],
["to_roderick", 1],
["to_gregoire_gorgon", 1],
["to_witch_lynx_crag", 1],
["to_milton", 1],
["to_champion", 1],
["to_duchess_informant", 1],
["to_syanna", 1],
["to_bootblack", 1],
["to_dettlaff", 1],
["to_knight_errant_1", 1],
["to_knight_errant_2", 1],
["to_lui_alberni", 1],
["to_orianna", 1],
["to_regis", 1],
["to_prophet_lebioda", 1],
["to_toussaint_knight_1", 1],
["to_toussaint_knight_2", 1],
["to_toussaint_knight_3", 1],
["to_artorius_vigo", 1],
["to_lady_lake", 1],
["to_vivienne", 1]
]
},
{
"title": "Velen Deck 1 - Cursed Land",
"description": "Deck relying mostly on bond, scorch and drawing cards",
"leader": "ve_lady_wood_weavess",
"faction": "velen",
"cards": [
["spe_horn", 1],
["spe_curse", 3],
["spe_decoy", 2],
["spe_scorch", 1],
["spe_clear", 1],
["ve_bloody_baron", 1],
["ve_geralt", 1],
["ve_johnny", 1],
["ve_fugas", 1],
["ve_pellar", 1],
["ve_anna_strenger", 1],
["ve_fiend", 1],
["ve_chort", 1],
["ve_vesemir", 1],
["ve_thecla", 1],
["ve_deserter_1", 1],
["ve_deserter_2", 1],
["ve_ghoul_1", 1],
["ve_ghoul_2", 1],
["ve_marauder_1", 1],
["ve_marauder_2", 1],
["ve_tamara_strenger", 1],
["ve_angry_peasants", 1],
["ve_hungry_wolves_4", 1],
["ve_hungry_wolves_1", 1],
["ve_hungry_wolves_2", 1],
["ve_hungry_wolves_3", 1],
["ve_abandoned_girl", 1]
]
}
];
/*
*
*/
var premade_deck = default_decks;
//premade_deck = custom_decks.concat(premade_deck);
//premade_deck = custom_decks;
var deckWeightConsts = {
"spyBase": 20,
"heroMult": 2,
"musterMult": 2,
"tightBondMult": 2,
"medic": 7,
"scorch": 5,
"scorch_c": 5,
"morale": 5,
"agile": 2,
"horn": 10,
"berserker": 5,
"mardroeme": 5,
"avenger": 5,
"decoy": 7,
"clearWeatherWeight": 7,
"heroSpyBase": 5,
"cintra_slaughter": 5,
"resilience": 5,
"witcherSchoolWeight": 1,
"witch_hunt": 5,
"whorshipper": 2,
"whorshipped": 2,
"inspire": 3,
"comrade": 7,
"emissary": 5
};
function calcDeckWeight(deck) {
var total = 0;
deck.cards.forEach(entry => {
let c = card_dict[entry[0]];
let cnt = parseInt(entry[1]);
let abi = c["ability"].split(" ");
let cStr = 0;
if (!c["deck"].startsWith("special") && !c["deck"].startsWith("weather")) {
if (abi.includes("spy")) {
cStr = deckWeightConsts["spyBase"] - parseInt(c["strength"]);
if (abi.includes("hero"))
cStr += deckWeightConsts["heroSpyBase"];
if (c.row === "agile")
cStr += deckWeightConsts["agile"];
} else {
cStr = parseInt(c["strength"]);
if (abi.includes("hero"))
cStr *= deckWeightConsts["heroMult"];
if (abi.includes("bond"))
cStr *= deckWeightConsts["tightBondMult"];
if (abi.includes("muster"))
cStr *= deckWeightConsts["musterMult"];
abi.forEach(a => {
if (a in deckWeightConsts)
cStr += deckWeightConsts[a];
});
if (abi.includes("witcher_wolf_school") || abi.includes("witcher_bear_school") || abi.includes("witcher_viper_school") || abi.includes("witcher_cat_school") || abi.includes("witcher_griffin_school"))
cStr += deckWeightConsts["witcherSchoolWeight"];
if (c.row === "agile")
cStr += deckWeightConsts["agile"];
}
}
total += (cStr * cnt);
});
return total;
}

1396
decks.old.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
{"faction":"lyria_rivia","leader":"lr_meve_princess","cards":[["spe_horn",1],["spe_decoy",1],["spe_mantlet",1],["spe_scorch",1],["spe_wyvern_shield",1],["spe_clear",2],["spe_rain",1],["ntr_geralt",1],["lr_gascon",1],["lr_knighthood",1],["lr_prince_anseis",1],["lr_rayla",1],["lr_reynard_odo",1],["lr_royal_guard",1],["lr_lyrian_cavalry",1],["lr_rivian_mauler",1],["lr_pikeman",1],["lr_light_cavalry",2],["lr_grey_rider",2],["lr_isbel_hagge",1],["lr_war_wagon",1],["lr_hajduk",1],["lr_landsknecht",3],["lr_wagenburg",3],["lr_wagon",1]]}

View File

@ -0,0 +1 @@
{"faction":"lyria_rivia","leader":"lr_meve_white_queen","cards":[["spe_horn",1],["spe_decoy",1],["spe_garrison",1],["spe_scorch",1],["spe_frost",2],["spe_clear",1],["lr_carroballista",1],["lr_caldwell",1],["lr_trebuchet",1],["lr_villem",1],["lr_pyrokinesis",1],["lr_spellweaver",1],["lr_eavesdrop",1],["lr_onager",1],["lr_siege",1],["lr_forager",1],["lr_scout",1],["lr_winch",1],["lr_arbalest",3],["lr_sapper",1],["lr_scytheman_2",1],["lr_scytheman_3",1],["lr_physician",1],["lr_piercing_missile",1],["lr_artificer",1],["lr_banner",1]]}

View File

@ -0,0 +1 @@
{"faction":"monsters","leader":"mo_eredin_bringer_of_death","cards":[["spe_horn",1],["spe_decoy",2],["spe_scorch",2],["spe_clear",1],["spe_fog",2],["mo_caranthir",1],["mo_geels",1],["mo_ice_troll",1],["mo_imlerith",1],["mo_jotunn",1],["mo_naglfar",1],["mo_therazane",1],["mo_nithral",1],["mo_old_speartip",1],["mo_witch_velen",1],["mo_witch_velen_1",1],["mo_witch_velen_2",1],["mo_cyclops",1],["mo_earth_elemental",1],["mo_fire_elemental",1],["mo_golem",1],["mo_ifrit_1",1],["mo_ifrit_2",1],["mo_frost_giant",1],["mo_navigator",1],["mo_wild_hunt_rider_1",1],["mo_wild_hunt_rider_2",1],["mo_wild_hunt_rider_3",1]]}

View File

@ -0,0 +1 @@
{"faction":"monsters","leader":"mo_eredin_commander","cards":[["spe_horn",2],["spe_decoy",1],["spe_scorch",1],["spe_clear",1],["spe_rain",2],["mo_dragon_fyresdal",1],["mo_leshen",1],["mo_archgriffin",1],["mo_glustyworp",1],["mo_kayran",1],["mo_alp",1],["mo_armoured_arachas",1],["mo_manticore",1],["mo_morvudd",1],["mo_toad",1],["mo_alghoul",1],["mo_arachas_behemoth",1],["mo_katakan",1],["mo_arachas",1],["mo_arachas_1",1],["mo_arachas_2",1],["mo_lamia_1",1],["mo_lamia_2",1],["mo_lamia_3",1],["mo_bruxa",1],["mo_ekkima",1],["mo_fleder",1],["mo_garkain",1],["mo_manticore_venom",1],["mo_ghoul",1],["mo_ghoul_1",1],["mo_ghoul_2",1]]}

View File

@ -0,0 +1 @@
{"faction":"nilfgaard","leader":"ne_emhyr_relentless","cards":[["spe_decoy",2],["spe_scorch",2],["spe_clear",2],["spe_fog",1],["spe_rain",1],["ne_black_archer",1],["ne_black_archer_1",1],["ne_emissary",1],["ne_heavy_zerri",1],["ne_menno",1],["ne_knight",1],["ne_guardian",1],["ne_imperial_golem",1],["ne_deithwen_arbalest",1],["ne_hefty_helge",1],["ne_shilard",1],["ne_alba_armored_cavalry",1],["ne_daerlan_soldier",1],["ne_nausicaa_brigade",1],["ne_alba_pikeman_1",1],["ne_alba_spearman",1],["ne_magne_division",1],["ne_slave_infantry_1",1],["ne_slave_infantry_2",1],["ne_nauzicaa_1",1],["ne_nauzicaa_2",1],["ne_nauzicaa_3",1],["ne_archer_support",1],["ne_archer_support_1",1],["ne_nausicaa_standard_bearer",1],["ne_standard_bearer",1],["ne_vicovaro_medic",1],["ne_siege_support",1]]}

View File

@ -0,0 +1 @@
{"faction":"nilfgaard","leader":"ne_emhyr_emperor","cards":[["spe_horn",2],["spe_scorch",1],["spe_clear",2],["spe_fog",2],["ne_bribery",1],["ne_cantarella",1],["ne_letho",1],["ne_moorvran",1],["ne_usurper",1],["ne_peter_saar_gwynleve",1],["ne_stefan",1],["ne_fergus_emreis",1],["ne_jan_calveit",1],["ne_xarthisius",1],["ne_assire",1],["ne_cahir",1],["ne_fringilla",1],["ne_imperial_diviner",1],["ne_impera_enforcers",1],["ne_renuald",1],["ne_young_emissary_1",1],["ne_young_emissary_2",1],["ne_vattier",1],["ne_impera_brigade_1",1],["ne_impera_brigade_2",1],["ne_impera_brigade_3",1],["ne_impera_brigade_4",1],["ne_impera_brigade_5",1],["ne_treason",1]]}

View File

@ -0,0 +1 @@
{"faction":"nilfgaard","leader":"ne_emhyr_invader_of_the_north","cards":[["spe_horn",2],["spe_decoy",1],["spe_scorch",1],["spe_clear",1],["spe_fog",1],["spe_rain",1],["ne_ffion_gaernel",1],["ne_spotter",1],["ne_tibor",1],["ne_ardal_aep_dahy",1],["ne_vrygheff",1],["ne_ramon_tyrconnel",1],["ne_traheaern_vdyffir",1],["ne_epidemic",1],["ne_joachim_de_wett",1],["ne_vreemde",1],["ne_ard_feainn_heavy_cavalry",1],["ne_ard_feainn_tortoise",3],["ne_glynnis_loernach",1],["ne_mangonel",1],["ne_ard_feainn_crossbowman",1],["ne_illusionist",1],["ne_ard_feainn_light_cavalry",3],["ne_imperial_practitioner",1],["ne_rot_tosser",1]]}

View File

@ -0,0 +1 @@
{"faction":"realms","leader":"nr_foltest_lord","cards":[["spe_horn",1],["spe_decoy",1],["spe_scorch",1],["spe_fog",1],["nr_esterad",1],["nr_natalis",1],["nr_vernon",1],["nr_foltest_pride",1],["nr_mage_infiltrator",1],["nr_queen_adalia",1],["ntr_triss",1],["nr_ballista",1],["nr_siege_tower",1],["nr_trebuchet_1",1],["nr_stripes_scout",1],["nr_aretuza_adept",1],["nr_blue_stripes_1",1],["nr_blue_stripes_2",1],["nr_blue_stripes_3",1],["nr_blue_stripes_skirmisher_1",1],["nr_blue_stripes_skirmisher_2",1],["nr_blue_stripes_skirmisher_3",1],["nr_dijkstra",1],["nr_temerian_soldier_1",1],["nr_temerian_soldier_2",1],["nr_temerian_soldier_3",1],["nr_temerian_drummer",1],["nr_thaler",1]]}

View File

@ -0,0 +1 @@
{"faction":"realms","leader":"nr_foltest_siegemaster","cards":[["spe_horn",2],["spe_decoy",2],["spe_scorch",2],["spe_clear",1],["spe_fog",1],["nr_ban_ard_tutor",1],["nr_demavend",1],["nr_henselt",1],["nr_seltkirk",1],["nr_vandergrift",1],["nr_catapult_1",1],["nr_catapult_2",1],["nr_dun_banner_heavy_cavalry_1",1],["nr_dun_banner_heavy_cavalry_2",1],["nr_kaedweni_knight",1],["nr_dethmold",1],["nr_banner_nurse",1],["nr_kaedweni_sergeant",1],["nr_stennis",1],["nr_reinforced_trebuchet_1",1],["nr_reinforced_trebuchet_2",1],["nr_reinforced_ballista_1",1],["nr_reinforced_ballista_2",1],["nr_kaedweni_reinforcement",1],["nr_odrin",1],["nr_kaedweni_siege_support_1",1],["nr_kaedweni_siege_support_2",1]]}

View File

@ -0,0 +1 @@
{"faction":"realms","leader":"nr_queen_calanthe","cards":[["spe_horn",1],["spe_decoy",2],["spe_scorch",1],["spe_slaughther_cintra_1",1],["spe_slaughther_cintra_2",1],["spe_clear",1],["spe_storm",1],["ntr_ciri",1],["nr_kraken",1],["nr_eist_tuirseach",1],["nr_pavetta",1],["nr_viraxas",1],["nr_cintrian_knight",1],["nr_egmund",1],["nr_vissegerd",1],["nr_kistrin_verden",1],["nr_roegner",1],["nr_windhalm",1],["nr_cintrian_royal_guard",1],["nr_young_ciri",1],["nr_kerack_fleet_frigate",1],["nr_cintrian_peasants",1],["nr_kerack_fleet_marine_1",1],["nr_kerack_fleet_marine_2",1],["nr_sha",1],["nr_skellige_fleet_1",1],["nr_skellige_fleet_2",1],["nr_skellige_fleet_3",1],["nr_eylembert_tigg",1]]}

View File

@ -0,0 +1 @@
{"faction":"realms","leader":"nr_radovid_ruthless","cards":[["spe_horn",1],["spe_decoy",2],["spe_scorch",1],["spe_clear",1],["spe_rain",1],["nr_eyck_denesle",1],["nr_donimir_troy",1],["nr_flying_redanian",1],["nr_philippa",1],["nr_tridam_infantry",1],["nr_voymir",1],["nr_redanian_knight",1],["nr_falibor",1],["nr_radovid_royal_guards",1],["nr_trollololo",1],["nr_ewald_borsodi",1],["nr_reinforced_tower",1],["nr_battering_ram",1],["nr_reaver_scout",1],["nr_redanian_elite",1],["nr_crinfrid_1",1],["nr_crinfrid_2",1],["nr_crinfrid_3",1],["nr_redanian_archer",1],["nr_siegfried",1],["nr_dijkstra",1],["nr_joachim_gratz",1],["nr_milo_vanderbeck",1],["nr_shani",1],["nr_marching_orders",1]]}

View File

@ -0,0 +1 @@
{"faction":"scoiatael","leader":"sc_francesca_daisy","cards":[["spe_horn",2],["spe_decoy",2],["spe_scorch",1],["spe_clear",1],["spe_rain",1],["sc_saesenthessis_dragon",1],["sc_aelirenn",1],["sc_iorveth",1],["sc_isengrim",1],["sc_saesenthessis_saskia",1],["sc_vrihedd_officer",1],["sc_neophyte",1],["sc_elven_mercenary_1",1],["sc_elven_mercenary_2",1],["sc_yaevinn",1],["sc_havekar_support",1],["sc_havekar_support_1",1],["sc_havekar_support_2",1],["sc_vrihedd_brigade",1],["sc_vrihedd_brigade_1",1],["sc_vrihedd_dragoon_1",1],["sc_vrihedd_dragoon_2",1],["sc_vrihedd_dragoon_3",1],["sc_vrihedd_sapper",1],["sc_havekar_nurse",1],["sc_havekar_nurse_1",1],["sc_havekar_nurse_2",1]]}

View File

@ -0,0 +1 @@
{"faction":"scoiatael","leader":"sc_francesca_beautiful","cards":[["spe_horn",1],["spe_decoy",1],["spe_scorch",2],["spe_frost",1],["spe_clear",1],["spe_rain",1],["sc_brouver_hoog",1],["sc_eithne",1],["sc_milva",1],["sc_gabor_zigrin",1],["sc_aglais",1],["sc_dol_blathanna_sentry",1],["sc_elven_deadeye",1],["sc_mahakam_pyrotechnician",1],["sc_morenn",1],["sc_yannick_brass",1],["sc_barclay",1],["sc_mahakam_guard_1",1],["sc_mahakam_guard_2",1],["sc_dol_blathanna_trapper",1],["sc_dryads_1",1],["sc_dryads_2",1],["sc_dryads_3",1],["sc_dryads_4",1],["sc_dol_blathanna_guard_1",1],["sc_dol_blathanna_guard_2",1],["sc_dol_blathanna_guard_3",1],["sc_mahakam_ale",1]]}

View File

@ -0,0 +1 @@
{"faction":"skellige","leader":"sk_crach_an_craite","cards":[["spe_horn",1],["spe_decoy",1],["spe_mardroeme",3],["spe_scorch",1],["spe_clear",1],["spe_storm",2],["sk_svalblod",1],["sk_cerys",1],["sk_harald_cripple",1],["sk_hjalmar",1],["sk_vabjorn",1],["sk_ermion",1],["sk_svalblod_fanatic",1],["sk_morkvarg",1],["sk_blueboy",1],["sk_an_craite_greatsword",1],["sk_craite_warrior_1",1],["sk_craite_warrior_2",1],["sk_craite_warrior_3",1],["sk_madman_lugos",1],["sk_an_craite_raiders",1],["sk_drummond_warmonger",1],["sk_berserker",1],["sk_shield_maiden_1",1],["sk_shield_maiden_2",1],["sk_shield_maiden_3",1],["sk_young_berserker",3]]}

View File

@ -0,0 +1 @@
{"faction":"skellige","leader":"sk_king_bran","cards":[["spe_horn",2],["spe_decoy",2],["spe_scorch",2],["spe_frost",3],["sk_olaf",1],["sk_hemdall",1],["sk_eist_tuirseach",1],["sk_vigi_loon",1],["sk_jutta",1],["sk_arnjolf",1],["sk_otkell",1],["sk_dagur",1],["sk_brokva_archer",2],["sk_dimun_pirate",1],["sk_dimun_pirate_captain",1],["sk_war_longship_1",1],["sk_war_longship_2",1],["sk_war_longship_3",1],["sk_light_longship",3],["sk_heymaey_flaminica",1],["sk_tuirseach_warrior_1",1],["sk_tuirseach_warrior_2",1],["sk_birna",1],["sk_kambi",1],["sk_sigrdrifa",1]]}

View File

@ -0,0 +1 @@
{"faction":"syndicate","leader":"sy_cyrus_hemmelfart","cards":[["spe_horn",1],["spe_decoy",2],["spe_dimeritium_shackles",2],["spe_clear",1],["spe_rain",1],["sy_inquisitor_helveed",1],["sy_jacques_aldersberg",1],["sy_lonely_champion",1],["sy_ulrich",1],["sy_octavia_hale",1],["sy_caleb_menge",1],["sy_moreelse",1],["sy_roderick_wett",1],["sy_walter_veritas",1],["sy_flaming_rose_footman",1],["sy_fabian_hale",1],["sy_ignatius_hale",1],["sy_cleric_flaming_rose",1],["sy_temple_guard",1],["sy_inquisitional_pyres",1],["sy_eternal_fire_disciple",2],["sy_eternal_fire_priest_1",1],["sy_eternal_fire_priest_2",1],["sy_eternal_fire_priest_3",1],["sy_witch_hunter_executioner",1],["sy_eternal_fire_inquisitor",1],["sy_witch_hunter",1]]}

View File

@ -0,0 +1 @@
{"faction":"syndicate","leader":"sy_azar_javed","cards":[["spe_horn",1],["spe_decoy",2],["spe_scorch",2],["spe_clear",1],["spe_rain",1],["sy_fallen_rayla",1],["sy_greater_brothers",1],["sy_frightener",1],["sy_failed_experiment",1],["sy_fisstech",1],["sy_professor",1],["sy_gellert_bleinheim",1],["sy_roland_bleinheim",1],["sy_mutant_killer",1],["sy_salamandra_mage",1],["sy_savolla",1],["sy_mutant_maker",1],["sy_mutated_hound_1",1],["sy_mutated_hound_2",1],["sy_salamandra_assassin",1],["sy_salamandra_assassin_2",1],["sy_salamandra_lackey",1],["sy_mutant",3],["sy_fisstech_trafficker",1],["sy_stolen_mutagens",1]]}

View File

@ -0,0 +1 @@
{"faction":"syndicate","leader":"sy_cyprian_wiley","cards":[["spe_horn",1],["spe_decoy",2],["spe_scorch",2],["spe_vivaldi_bank",2],["spe_clear",1],["spe_rain",1],["sy_igor_hook",1],["sy_boris",1],["sy_casino_bouncers",1],["sy_whoreson_senior",1],["sy_bare_knuckle_brawler",1],["sy_hvitr_aelydia",1],["sy_madame_luiza",1],["sy_sewer_raider",1],["sy_sly_seductress",1],["sy_bincy_blumerholdt",1],["sy_ferko",1],["sy_rico_meiersdorf",1],["sy_robber_1",1],["sy_robber_2",1],["sy_robber_3",1],["sy_robber_4",1],["sy_imke",1],["sy_beggar",1],["sy_cleaver_gang_1",1],["sy_cleaver_gang_2",1],["sy_cleaver_gang_3",1],["sy_cleaver_gang_4",1],["sy_freak_show_1",1],["sy_freak_show_2",1],["sy_freak_show_3",1],["sy_freak_show_4",1]]}

View File

@ -0,0 +1 @@
{"faction":"toussaint","leader":"to_anna_henrietta_duchess","cards":[["spe_horn",2],["spe_decoy",1],["spe_scorch",1],["spe_toussaint_wine",1],["spe_frost",1],["spe_clear",1],["spe_rain",1],["to_artorius_vigo",1],["to_damien_tour",1],["to_fringilla_vigo",1],["to_gregoire_gorgon",1],["to_champion",1],["to_jousting_champion",1],["to_beauclair_cavalry_1",1],["to_beauclair_cavalry_2",1],["to_vivienne_oriole",1],["to_knight_errant_1",1],["to_knight_errant_2",1],["to_knight_errant_3",1],["to_lady_lake",1],["to_milton",1],["to_palmerin",1],["to_dun_tynne_infantry",1],["to_fisher_king",1],["to_panther_1",1],["to_panther_2",1],["to_panther_3",1],["to_guillaume",1],["to_syanna",1]]}

View File

@ -0,0 +1 @@
{"faction":"toussaint","leader":"to_anna_henrietta_ladyship","cards":[["spe_horn",1],["spe_scorch",1],["spe_toussaint_wine",2],["spe_clear",2],["spe_rain",1],["to_unseen_elder",1],["to_dettlaff_higher_vampire",1],["to_dettlaff_ghastly_beast",1],["to_yghern",1],["to_lacerate",1],["to_orianna",1],["to_protofleder",1],["to_tufo_monster",1],["to_dettlaff",1],["to_shaelmaar",1],["to_alpha_garkain",1],["to_golyat",1],["to_archespore",1],["to_barghest_1",1],["to_barghest_2",1],["to_barghest_3",1],["to_bruxa_corvo_bianco",1],["to_cloud_giant",1],["to_fleder",1],["to_garkain",1],["to_wolves_1",1],["to_wolves_2",1]]}

View File

@ -0,0 +1 @@
{"faction":"witcher_universe","leader":"wu_vilgefortz_magician_kovir","cards":[["spe_horn",1],["spe_decoy",1],["spe_scorch",1],["spe_frost",1],["spe_clear",1],["spe_fog",1],["wu_leo_bonhart",1],["wu_dorregaray",1],["wu_gerhart_aelle",1],["wu_dana_meadbh",1],["wu_strays_spalla",1],["wu_azar_javed",1],["wu_gascon",1],["wu_ralf_blunden",1],["wu_coral",1],["wu_tissaia",1],["wu_gascon_light_cavalry",1],["wu_ortolan",1],["wu_lydia_bredevoort",1],["wu_operator",1],["wu_rience",1],["wu_gascon_infiltrator",1],["wu_gascon_slinger",1],["wu_rats_giselher",1],["wu_bomb_heaver",1],["wu_rats_asse",1],["wu_rats_iskra",1],["wu_rats_kayleigh",1],["wu_rats_mistle",1],["wu_rats_reef",1],["wu_visenna",1]]}

View File

@ -0,0 +1 @@
{"faction":"witcher_universe","leader":"wu_cosimo_malaspina","cards":[["spe_sign_aard",2],["spe_sign_axii",2],["spe_sign_igni",2],["spe_sign_quen",2],["spe_sign_yrden",2],["wu_arnaghad",1],["wu_erland",1],["wu_geralt_1",1],["wu_gezras",1],["wu_letho",1],["wu_coen",1],["wu_eskel",1],["wu_gerd",1],["wu_gorthur_gvaed",1],["wu_haern_caduch",1],["wu_ivar",1],["wu_kaer_morhen",1],["wu_kaer_seren",1],["wu_stygga_castle",1],["wu_gaetan",1],["wu_lambert",1],["wu_warrit",1],["wu_brehen",1],["wu_ivo_belhaven",1],["wu_keldar",1],["wu_kolgrim",1],["wu_vesemir",1],["wu_swallow_potion",1]]}

View File

@ -0,0 +1 @@
{"faction":"witcher_universe","leader":"wu_alzur_maker","cards":[["spe_horn",1],["spe_decoy",2],["spe_scorch",2],["spe_sign_yrden",1],["spe_clear",1],["spe_rain",1],["wu_boris",1],["wu_idr",1],["wu_myrgtabrakke",1],["wu_phoenix",1],["wu_iris_von_everec",1],["wu_idarran_ulivo",1],["wu_nivellen",1],["wu_cicada",1],["wu_mad_kiyan",1],["wu_sarah",1],["wu_vlodimir_von_everec",1],["wu_djinn",1],["wu_marlene_trastamara",1],["ntr_olgierd",1],["wu_raging_bear",1],["wu_vincent_meis",1],["wu_doppler_1",1],["ntr_gaunter_odimm_darkness",3],["wu_iris_companions_2",1],["wu_iris_companions_1",1],["ntr_gaunter_odimm",1]]}

View File

@ -0,0 +1 @@
{"faction":"witcher_universe","leader":"wu_vilgefortz_sorcerer","cards":[["spe_horn",1],["spe_sign_aard",1],["spe_clear",1],["spe_fog",1],["spe_rain",1],["ntr_ciri",1],["ntr_geralt",1],["ntr_triss",1],["ntr_villen",1],["ntr_yennefer",1],["wu_doppler_2",1],["ntr_vesemir",1],["wu_angouleme",1],["ntr_emiel",1],["wu_tea_vea_1",1],["wu_tea_vea_2",1],["ntr_zoltan",1],["wu_kalkstein",1],["ntr_dandelion",1],["wu_pellar",1],["wu_rhapsodic_melody",1],["wu_vysogota",1],["wu_nenneke",1],["ntr_mysterious_elf",1],["wu_iola",1],["wu_kelpie",1],["wu_roach",1]]}

355
factions.js Normal file
View File

@ -0,0 +1,355 @@
"use strict"
var factions = {
realms: {
name: "Northern Realms",
factionAbility: player => game.roundStart.push(async () => {
if (game.roundCount > 1 && game.roundHistory[game.roundCount - 2].winner !== player) {
player.deck.draw(player.hand);
await ui.notification("north", 1200);
}
return false;
}),
activeAbility: false,
abilityUses: 0,
description: "Draw a card from your deck whenever you lose a round.",
unavailableSpecials: []
},
nilfgaard: {
name: "Nilfgaardian Empire",
description: "Wins any round that ends in a draw.",
activeAbility: false,
abilityUses: 0,
unavailableSpecials: []
},
monsters: {
name: "Monsters",
factionAbility: player => {
game.gameStart.push(() => { player.capabilities["endTurnRetake"] = 1; return true; });
game.roundEnd.push(async () => {
if (player.capabilities["endTurnRetake"] < 1)
return false;
let cards = new CardContainer();
cards.cards = player.getAllRowCards();
let retakeCard = null;
if (cards.cards.length == 0)
return false;
if (player.controller instanceof ControllerAI) {
player.capabilities["endTurnRetake"] = 0;
retakeCard = player.controller.medic(player.leader, cards);
await ui.notification("monsters", 1200);
await board.toHand(retakeCard, retakeCard.currentLocation);
return true;
} else {
let c = await ui.popup("Retake a card [E]", (p) => p.choice = true, "Not yet [Q]", (p) => p.choice = false, "Would you like to retake one of your cards?", "Once per battle, you may retake any of your cards from the board to your hand.");
if (c) {
await ui.queueCarousel(cards, 1, (c, i) => retakeCard = c.cards[i], c => true, true, false, "Which card to retake?");
player.capabilities["endTurnRetake"] = 0;
await ui.notification("monsters", 1200);
await board.toHand(retakeCard, retakeCard.currentLocation);
return true;
}
}
return false;
});
},
description: "Once per battle, at the end of the round, you may take one card back to your hand.",
activeAbility: false,
abilityUses: 0,
unavailableSpecials: []
},
scoiatael: {
name: "Scoia'tael",
factionAbility: player => game.roundStart.push(async () => {
let notif = "";
if (!game.isPvP() && player === player_me && !(player.controller instanceof ControllerAI)) {
await ui.popup("Go First [E]", () => game.currPlayer = player, "Let Opponent Start [Q]", () => game.currPlayer = player.opponent(), "Would you like to go first?", "The Scoia'tael faction perk allows you to decide each round who will get to go first.");
notif = game.currPlayer.tag + "-first";
} else if (game.isPvP()) {
await ui.popup("Player 1 first [E]", () => game.currPlayer = player_me, "Player 2 first [Q]", () => game.currPlayer = player_op, "Who should go first?", "The Scoia'tael faction perk allows you to decide each round who will get to go first.");
notif = game.currPlayer.tag + "-first";
} else if (player.controller instanceof ControllerAI) {
if (Math.random() < 0.5) {
game.currPlayer = player;
notif = "scoiatael";
} else {
game.currPlayer = player.opponent();
notif = game.currPlayer.tag + "-first";
}
}
//Tricky bit, sides are actually swapped shortly after
game.currPlayer = game.currPlayer.opponent();
// If first round, set first player too
if (game.roundCount == 1)
game.firstPlayer = game.currPlayer;
await ui.notification(notif, 1200);
return false;
}),
description: "Decides who takes first turn on each round.",
activeAbility: false,
abilityUses: 0,
unavailableSpecials: []
},
skellige: {
name: "Skellige",
factionAbility: player => game.roundStart.push(async () => {
if (game.roundCount != 3)
return false;
await ui.notification("skellige-" + player.tag, 1200);
await Promise.all(player.grave.findCardsRandom(c => c.isUnit(), 2).map(c => board.toRow(c, player.grave)));
return true;
}),
description: "2 random cards from the graveyard are placed on the battlefield at the start of the third round.",
activeAbility: false,
abilityUses: 0,
unavailableSpecials: []
},
witcher_universe: {
name: "Witcher Universe",
factionAbility: async player => {
await ui.notification("witcher_universe", 1200);
},
factionAbilityInit: player => game.roundStart.push(async () => {
player.updateFactionAbilityUses(1);
return false;
}),
description: "Can skip a turn once every round.",
activeAbility: true,
abilityUses: 1,
weight: (player) => {
return 20;
},
unavailableSpecials: []
},
toussaint: {
name: "Toussaint",
factionAbility: async player => {
let monsters = player.getAllRowCards().filter(c => c.abilities.includes("monster_toussaint") && !c.isLocked());
if (monsters.length < 1)
return;
let targetCard = null;
if (player.controller instanceof ControllerAI) {
let targets = monsters.map(c => new Card(c.target, card_dict[c.target], player));
let best = player.controller.getHighestWeightCard(targets);
if(best)
targetCard = monsters.filter(c => c.target === best.key)[0];
} else {
await ui.queueCarousel({ cards: monsters }, 1, async (c, i) => targetCard = c.cards[i], () => true, true, false, "Choose which monster to transform.");
}
if (targetCard) {
let newCard = new Card(targetCard.target, card_dict[targetCard.target], player);
targetCard.currentLocation.removeCard(targetCard);
player.deck.addCard(newCard);
//await board.addCardToRow(newCard, targetCard.currentLocation, player);
if (player.controller instanceof ControllerAI) {
newCard.autoplay(player.deck);
} else {
// let player select where to play the card
player.selectCardDestination(newCard, player.deck);
}
}
},
factionAbilityInit: player => {
game.gameStart.push(async () => {
player.mulliganCount = 3;
return true;
});
game.turnStart.push(async () => {
if (player.getAllRowCards().filter(c => c.abilities.includes("monster_toussaint")).length > 0)
player.updateFactionAbilityUses(1);
else
player.updateFactionAbilityUses(0);
return false;
})
},
weight: (player) => {
let monsters = player.getAllRowCards().filter(c => c.abilities.includes("monster_toussaint") && !c.isLocked());
if (monsters.length < 1)
return 0;
let targets = monsters.map(c => new Card(c.target, card_dict[c.target], player));
let w = player.controller.getWeights(targets).sort((a, b) => (b.weight - a.weight));
return w[0].weight;
},
activeAbility: true,
abilityUses: 0,
description: "At the beginning of the game, you can change 3 cards instead of 2. During the game, you can turn a Toussaint Monster into its next form instead of playing a card.",
unavailableSpecials: ["spe_frost", "spe_fog","spe_rain"]
},
lyria_rivia: {
name: "Lyria & Rivia",
factionAbility: player => {
let card = new Card("spe_lyria_rivia_morale", card_dict["spe_lyria_rivia_morale"], player);
card.removed.push(() => setTimeout(() => card.holder.grave.removeCard(card), 2000));
card.placed.push(async () => await ui.notification("lyria_rivia", 1200));
player.endTurnAfterAbilityUse = false;
ui.showPreviewVisuals(card);
ui.enablePlayer(true);
if (!(player.controller instanceof ControllerAI))
ui.setSelectable(card, true);
},
activeAbility: true,
abilityUses: 1,
description: "Apply a Morale Boost effect in the selected row (boost all units by 1 in this turn).",
weight: (player) => {
let units = player.getAllRowCards().concat(player.hand.cards).filter(c => c.isUnit()).filter(c => !c.abilities.includes("spy"));
let rowStats = {
"close": 0,
"ranged": 0,
"siege": 0,
"agile": 0
};
units.forEach(c => {
rowStats[c.row] += 1;
});
rowStats["close"] += rowStats["agile"];
return Math.max(rowStats["close"], rowStats["ranged"], rowStats["siege"]);
},
unavailableSpecials: []
},
syndicate: {
name: "Syndicate",
factionAbility: player => game.gameStart.push(async () => {
let card = new Card("sy_sigi_reuven", card_dict["sy_sigi_reuven"], player);
await board.addCardToRow(card, card.row, card.holder);
}),
activeAbility: false,
abilityUses: 0,
description: "Starts the game with the Hero card Sigi Reuven on the board.",
unavailableSpecials: []
},
zerrikania: {
name: "Zerrikania",
factionAbility: player => game.roundStart.push(async () => {
if (game.roundCount > 1 && !(game.roundHistory[game.roundCount - 2].winner === player)) {
if (player.grave.findCards(c => c.isUnit()) <= 0)
return;
let grave = player.grave;
let respawns = [];
if (player.controller instanceof ControllerAI) {
respawns.push({
card: player.controller.medic(player.leader, grave)
});
} else {
await ui.queueCarousel(player.grave, 1, (c, i) => respawns.push({
card: c.cards[i]
}), c => c.isUnit(), true);
}
await Promise.all(respawns.map(async wrapper => {
let res = wrapper.card;
grave.removeCard(res);
grave.addCard(res);
await res.animate("medic");
await res.autoplay(grave);
}));
await ui.notification("zerrikania", 1200);
}
return false;
}),
activeAbility: false,
abilityUses: 0,
description: "Restore a unit card of your choice whenever you lose a round.",
unavailableSpecials: []
},
redania: {
name: "Redania",
factionAbility: async player => {
await ui.notification("redania", 1200);
},
factionAbilityInit: player => game.gameStart.push(async () => {
player.updateFactionAbilityUses(1);
return false;
}),
description: "Can skip a turn once per game.",
activeAbility: true,
abilityUses: 1,
weight: (player) => {
return 20;
},
unavailableSpecials: ["spe_scorch"]
},
velen: {
name: "Velen",
factionAbilityAction: async player => {
await ui.notification("velen", 1000);
let cardsToDraw = player.velenCardDraw + player.getAllRowCards().filter(c => c.abilities.includes("soothsayer")).length;
let cards = { cards: player.deck.cards.slice(0, cardsToDraw) };
let targetCard = null;
ui.helper.showMessage(String(player.destroyedCards)+" card(s) were destroyed last round.",3);
if (player.controller instanceof ControllerAI) {
targetCard = player.controller.getHighestWeightCard(cards.cards);
} else {
try {
Carousel.curr.cancel();
} catch (err) { }
await ui.queueCarousel(cards, 1, (c, i) => targetCard = c.cards[i], c => true, true, true, "Choose up to one card to draw and play immediatly");
}
if (player.destroyedCards > 1) {
player.destroyedCards = 1;
} else {
player.destroyedCards = 0;
}
cards.cards.forEach(c => {
if (c !== targetCard) {
// Remove to shuffle back at a random place (default behaviour on addCard in deck)
player.deck.removeCard(c);
player.deck.addCard(c);
}
});
// Game can get stuck if the player selects a Decoy but there is no decoyable card
if (targetCard.key === "spe_decoy") {
let units = player.getAllRowCards().filter(c => c.isUnit());
if (units.length == 0) {
await board.toHand(targetCard, player.deck);
targetCard = null;
}
}
if (player.controller instanceof ControllerAI) {
if(targetCard)
targetCard.autoplay(player.deck);
// If more than 1 card was destroyed, we trigger the faction ability once more
if (player.destroyedCards > 0) {
await factions["velen"].factionAbilityAction(player);
}
} else {
if (targetCard) {
// let player select where to play the card
let choiceDone = false;
player.selectCardDestination(targetCard, player.deck, async () => {
choiceDone = true;
});
// We sleep until the choice is made, otherwise the round starts as normal
await sleepUntil(() => choiceDone, 100);
}
// If more than 1 card was destroyed, we trigger the faction ability once more
if (player.destroyedCards > 0) {
await factions["velen"].factionAbilityAction(player);
}
}
},
factionAbility: player => {
game.gameStart.push(async () => {
player.destroyedCards = 0;
player.velenCardDraw = 1;
return false;
});
game.unitDestroyed.push(async c => {
if (c.isUnit())
player.destroyedCards += 1;
});
game.turnStart.push(async () => {
if (player.destroyedCards > 0) {
await factions["velen"].factionAbilityAction(player);
}
return false;
});
},
description: "Every time when a unit card of any player dies, draw one card from your deck and play it immediatly. If several unit cards die at the same time, repeat this action once. If you don't want to play a drawn card, shuffle it back into the deck.",
activeAbility: false,
abilityUses: 0,
weight: (player) => {
return 0;
},
unavailableSpecials: []
},
}

326
gametracker.js Normal file
View File

@ -0,0 +1,326 @@
"use strict"
class GameTracker {
constructor(player1,player2) {
this.player1 = player1;
this.player2 = player2;
this.rounds = [];
this.winner = null;
}
startRound() {
this.currentRound = new Round(this.rounds.length + 1);
this.currentRound.setBeforeStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
}
endRound(winpl) {
this.currentRound.setAfterStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
this.currentRound.endRound(winpl);
this.rounds.push(this.currentRound);
}
startTurn(pl) {
this.currentRound.startTurn(pl);
}
endTurn() {
this.currentRound.endTurn();
}
getCurrentTurn() {
return this.currentRound.currentTurn;
}
endGame(winpl) {
this.winner = winpl;
}
getScores() {
let scores = [];
this.rounds.forEach(r => {
scores.push([r.finalScores[0], r.finalScores[1]]);
});
return scores;
}
getCardImpacts() {
let data = {};
this.rounds.forEach(r => {
r.turns.forEach(t => {
if (t.action instanceof UnitCardAction) {
let ckey = t.action.card.key;
if (!data[ckey])
data[ckey] = [];
data[ckey].push(t.action.impact);
}
});
});
return data;
}
getAbilitiesImpact() {
let cimp = this.getCardImpacts();
let data = {"bond":[],"muster":[],"medic":[],"witcher":[],"whorshiped":[],"inspire":[],"scorch":[],"scorch_c":[],"morale":[],"horn":[], "witch_hunt":[]};
Object.keys(cimp).forEach(key => {
let abi = card_dict[key]["ability"].split(" ");
if (abi.includes("bond")) {
data["bond"] = data["bond"].concat(cimp[key].map(v => v / card_dict[key]["strength"]));
} else if (abi.includes("muster")) {
data["muster"] = data["muster"].concat(cimp[key].map(v => v / card_dict[key]["strength"]));
} else if (abi.includes("medic")) {
data["medic"] = data["medic"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.at(-1) && abi.at(-1).startsWith("witcher_")) {
data["witcher"] = data["witcher"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("whorshiped")) {
data["whorshiped"] = data["whorshiped"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("inspire")) {
data["inspire"] = data["inspire"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("scorch")) {
data["scorch"] = data["scorch"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("scorch_c")) {
data["scorch_c"] = data["scorch_c"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("morale")) {
data["morale"] = data["morale"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("horn")) {
data["horn"] = data["horn"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("witch_hunt")) {
data["witch_hunt"] = data["witch_hunt"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
}
});
return data;
}
}
class Round {
constructor(n) {
this.number = n;
this.turns = [];
this.winner = null;
this.finalScores = [0, 0];
this.start_hand_size = [0, 0];
this.end_hand_size = [0, 0];
}
startTurn(pl) {
this.currentTurn = new Turn(this.turns.length + 1, this, pl);
this.currentTurn.setBeforeStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
}
endTurn() {
this.currentTurn.setAfterStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
this.turns.push(this.currentTurn);
}
setBeforeStats(scores, rows, handsize) {
this.startScores = scores;
this.startRows = [];
rows.forEach(r => {
this.startRows.push(Object.assign({}, r));
});
this.start_hand_size = handsize;
}
setAfterStats(scores, rows, handsize) {
this.endScores = scores;
this.endRows = [];
rows.forEach(r => {
this.endRows.push(Object.assign({}, r));
});
this.end_hand_size = handsize;
this.turns.forEach(t => {
if (t.action instanceof UnitCardAction && t.action.card.currentLocation instanceof Row) {
let pl = t.action.card.holder;
if (!t.action.card.isLocked() &&
(t.action.card.abilities.includes("bond")
|| t.action.card.abilities.includes("whorshipped")
|| t.action.card.abilities.includes("inspire")
|| (t.action.card.abilities.at(-1) && t.action.card.abilities.at(-1).startsWith("witcher_"))
)) {
t.action.impact = t.action.card.power;
} else if (!t.action.card.isLocked() &&
(t.action.card.abilities.includes("scorch")
|| t.action.card.abilities.includes("scorch_c")
|| t.action.card.abilities.includes("medic")
|| t.action.card.abilities.includes("witch_hunt")
)) {
t.action.impact = (t.endScores[getPlayerIndex(pl)] - t.startScores[getPlayerIndex(pl)]) + (t.startScores[getPlayerIndex(pl.opponent())] - t.endScores[getPlayerIndex(pl.opponent())]);
} else if (!t.action.card.isLocked() && t.action.card.abilities.includes("morale")) {
t.action.impact = t.action.card.power + t.action.card.currentLocation.cards.filter(c => c.isUnit()).length;
} else if (!t.action.card.isLocked() && t.action.card.abilities.includes("horn")) {
t.action.impact = t.action.card.power + t.action.card.currentLocation.cards.filter(c => c.isUnit()).reduce((a,c) => a + (c.power / 2),0);
}
}
});
}
endRound(winpl) {
this.winner = winpl;
this.finalScores = [player_me.total, player_op.total];
}
}
class Turn {
constructor(n,rnd,pl) {
this.number = n;
this.round = rnd;
this.player = pl;
this.action = null;
this.start_hand_size = [0, 0];
this.end_hand_size = [0, 0];
}
setBeforeStats(scores, rows, handsize) {
this.startScores = scores;
this.startRows = [];
rows.forEach(r => {
this.startRows.push(Object.assign({}, r));
});
this.start_hand_size = handsize;
}
setAfterStats(scores, rows, handsize) {
this.endScores = scores;
this.endRows = [];
rows.forEach(r => {
this.endRows.push(Object.assign({}, r));
});
this.end_hand_size = handsize;
if (this.action instanceof UnitCardAction) {
var pl = this.action.card.holder;
if (this.action.card.abilities.length === 0) {
this.action.impact = this.action.card.power;
} else if (this.action.card.abilities.includes("muster")) {
// We devide the impact by the number of cards from hand it took
let handdiff = Math.max(1, (this.start_hand_size[getPlayerIndex(pl)] - this.end_hand_size[getPlayerIndex(pl)]));
this.action.impact = (this.endScores[getPlayerIndex(pl)] - this.startScores[getPlayerIndex(pl)]) / handdiff;
}
}
}
playUnitCard(card, row) {
this.action = new UnitCardAction(this, card, row.getRowType());
}
playWeatherCard(card) {
this.action = new WeatherCardAction(this, card.key, card.abilities.at(-1));
}
playSpecialCard(card, row) {
this.action = new SpecialCardAction(this, card.key, row.getRowType());
}
playSpecialCardBoard(card) {
this.action = new SpecialCardAction(this, card.key, "board");
}
passAction() {
this.action = new PassAction(this);
}
useLeader(card) {
this.action = new LeaderAction(this, card.key);
}
useLeaderOnRow(card,r) {
this.action = new LeaderAction(this, card.key, r.getRowType());
}
useLeaderOnCard(card,t) {
this.action = new LeaderAction(this, card.key, false, t.key);
}
summary() {
let action_string = "No action";
if (this.action)
action_string = this.action.summary();
return `[Round:${this.round.number}] [Turn:${this.number}] [ScoresBefore:${this.startScores[0]}/${this.startScores[1]}]\nPlayer ${this.player.tag}: ${action_string}]\n[ScoresAfter:${this.endScores[0]}/${this.endScores[1]}]`;
}
}
class Action {
constructor(turn) {
this.turn = turn;
this.targetRow = false;
this.targetCard = false;
this.impact = 0;
}
summary() {
return "No output";
}
}
class UnitCardAction extends Action {
constructor(turn, card, r) {
super(turn);
this.card = card;
this.key = card.key;
this.targetRow = r;
}
summary() {
return `Card '${this.key}' played in ${this.targetRow} row.`;
}
}
class SpecialCardAction extends Action {
constructor(turn, ckey, r) {
super(turn);
this.key = ckey;
this.targetRow = r;
}
summary() {
return `Special Card '${this.key}' played on ${this.targetRow}.`;
}
}
class WeatherCardAction extends Action {
constructor(turn, ckey, type) {
super(turn);
this.key = ckey;
this.type = type;
}
summary() {
return `Weather Card '${this.key}' played with ${this.type} effect.`;
}
}
class LeaderAction extends Action {
constructor(turn, ckey, targetRow=false, targetCard=false) {
super(turn);
this.key = ckey;
this.targetRow = targetRow;
this.targetCard = targetCard;
}
summary() {
return `Leader '${this.key}' ability used`;
}
}
class FactionAction extends Action {
constructor(turn, ckey) {
super(turn);
this.key = ckey;
}
}
class PassAction extends Action {
constructor(turn) {
super(turn);
}
summary() {
return "Passed";
}
}
// UTIL FUNCTIONS
function getPlayerIndex(pl) {
return pl.tag === "me" ? 0 : 1;
}

5775
gwent.js Normal file

File diff suppressed because it is too large Load Diff

BIN
img/board.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
img/icons/anim_ambush.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
img/icons/anim_bond.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
img/icons/anim_comrade.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
img/icons/anim_curse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
img/icons/anim_emissary.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
img/icons/anim_goetia.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
img/icons/anim_horn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
img/icons/anim_immortal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
img/icons/anim_invoke.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
img/icons/anim_lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
img/icons/anim_medic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
img/icons/anim_morale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
img/icons/anim_muster.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
img/icons/anim_scorch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
img/icons/anim_seize.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
img/icons/anim_shield.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
img/icons/anim_spy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

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