├── screenshot.png ├── tools ├── rcedit-x64.exe ├── compile-po.sh ├── ui-pot-to-po.sh ├── yml-to-pot.sh ├── generate-ui-pot.sh ├── card-pot-to-po.sh ├── localizationkeys.txt ├── XamlToPot.csx ├── to-po.sh └── card-id-hash-map.txt ├── global.json ├── src └── GwentTracker │ ├── Assets │ ├── collector.ico │ └── fallback.png │ ├── Model │ ├── SaveGameInfo.cs │ ├── CombatDetails.cs │ ├── Location.cs │ ├── CollectionProgress.cs │ ├── Card.cs │ └── MissableInfo.cs │ ├── KeyValuePairExtensions.cs │ ├── Program.cs │ ├── TextureInfo.cs │ ├── Localization │ ├── TranslateExtension.cs │ ├── TranslateStringTypeConverter.cs │ ├── Translate.cs │ └── MoLoader.cs │ ├── EnumExtensions.cs │ ├── App.xaml │ ├── settings.ini │ ├── EnumDescriptionConverter.cs │ ├── ViewLocator.cs │ ├── MissableStateToPathConverter.cs │ ├── data │ ├── missable.yml │ ├── skellige.yml │ ├── northernrealms.yml │ ├── neutral.yml │ └── nilfgaard.yml │ ├── GwentTracker.csproj │ ├── locale │ ├── gwent-tracker.pot │ ├── gwent-tracker.zh.po │ ├── gwent-tracker.zh-cn.po │ ├── gwent-tracker.es-mx.po │ ├── gwent-tracker.ja.po │ ├── gwent-tracker.ko.po │ ├── gwent-tracker.ar.po │ ├── gwent-tracker.es.po │ ├── gwent-tracker.hu.po │ ├── gwent-tracker.it.po │ ├── gwent-tracker.tr.po │ ├── gwent-tracker.fr.po │ ├── gwent-tracker.pt-br.po │ ├── gwent-tracker.de.po │ ├── gwent-tracker.pl.po │ ├── gwent-tracker.ru.po │ └── gwent-tracker.cs.po │ ├── Views │ ├── MainWindow.xaml.cs │ └── MainWindow.xaml │ ├── netfx.props │ ├── ViewModels │ └── CardViewModel.cs │ └── App.xaml.cs ├── .gitmodules ├── publish.ps1 ├── LICENSE ├── publish.sh ├── readme.md ├── .gitattributes ├── GwentTracker.sln ├── changelog.md └── .gitignore /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfvgyhn/gwent-tracker/HEAD/screenshot.png -------------------------------------------------------------------------------- /tools/rcedit-x64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfvgyhn/gwent-tracker/HEAD/tools/rcedit-x64.exe -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "7.0.100", 4 | "rollForward": "latestFeature" 5 | } 6 | } -------------------------------------------------------------------------------- /src/GwentTracker/Assets/collector.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfvgyhn/gwent-tracker/HEAD/src/GwentTracker/Assets/collector.ico -------------------------------------------------------------------------------- /src/GwentTracker/Assets/fallback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfvgyhn/gwent-tracker/HEAD/src/GwentTracker/Assets/fallback.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "W3SavegameEditor"] 2 | path = W3SavegameEditor 3 | url = https://github.com/rfvgyhn/W3SavegameEditor 4 | branch = gwent-tracker 5 | -------------------------------------------------------------------------------- /src/GwentTracker/Model/SaveGameInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GwentTracker.Model 4 | { 5 | public class SaveGameInfo 6 | { 7 | public IEnumerable> CardCopies { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/GwentTracker/Model/CombatDetails.cs: -------------------------------------------------------------------------------- 1 | namespace GwentTracker.Model 2 | { 3 | public class CombatDetails 4 | { 5 | public int Strength { get; set; } 6 | public string[] Types { get; set; } 7 | public string[] Abilities { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/GwentTracker/Model/Location.cs: -------------------------------------------------------------------------------- 1 | namespace GwentTracker.Model 2 | { 3 | public class Location 4 | { 5 | public string Type { get; set; } 6 | public string Npc { get; set; } 7 | public string Area { get; set; } 8 | public string Territory { get; set; } 9 | public string Region { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tools/compile-po.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | dir="../src/GwentTracker/locale" 6 | 7 | rm -f "$dir"/*.mo 8 | 9 | msgfmt -o "$dir/cards.mo" "$dir/cards.pot" 10 | msgfmt -o "$dir/gwent-tracker.mo" "$dir/gwent-tracker.pot" 11 | 12 | find "$dir" -name "*.po" | \ 13 | while IFS= read -r file; do 14 | msgfmt -o "${file%.po}.mo" "${file}" 15 | done 16 | -------------------------------------------------------------------------------- /src/GwentTracker/KeyValuePairExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GwentTracker 4 | { 5 | public static class KeyValuePairExtensions 6 | { 7 | public static void Deconstruct(this KeyValuePair tuple, out T1 key, out T2 value) 8 | { 9 | key = tuple.Key; 10 | value = tuple.Value; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /tools/ui-pot-to-po.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | scriptName=$(basename "$0") 6 | 7 | if [[ ${#@} = 0 ]]; then 8 | locales=("ar" "cs" "de" "es" "es-mx" "fr" "hu" "it" "ja" "ko" "pl" "pt-br" "ru" "tr" "zh" "zh-cn") 9 | else 10 | locales=($@) 11 | fi 12 | 13 | . to-po.sh 14 | 15 | function prepareId() { 16 | echo "$1" | sed 's/\([*]\)/\\\1/g' 17 | } 18 | 19 | addMiscToMap 20 | writePoFiles gwent-tracker 21 | printMissing 22 | 23 | -------------------------------------------------------------------------------- /src/GwentTracker/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.ReactiveUI; 3 | 4 | namespace GwentTracker 5 | { 6 | class Program 7 | { 8 | public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); 9 | 10 | public static AppBuilder BuildAvaloniaApp() 11 | => AppBuilder.Configure() 12 | .UsePlatformDetect() 13 | .LogToTrace() 14 | .UseReactiveUI(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/GwentTracker/TextureInfo.cs: -------------------------------------------------------------------------------- 1 | namespace GwentTracker 2 | { 3 | public class TextureInfo 4 | { 5 | public string RemotePathFormat { get; } 6 | public string LocalPathFormat { get; } 7 | public bool CacheRemote { get; } 8 | 9 | public TextureInfo(string remotePathFormat, string localPathFormat, bool cacheRemote) 10 | { 11 | RemotePathFormat = remotePathFormat; 12 | LocalPathFormat = localPathFormat; 13 | CacheRemote = cacheRemote; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/GwentTracker/Localization/TranslateExtension.cs: -------------------------------------------------------------------------------- 1 | namespace GwentTracker.Localization 2 | { 3 | public class TranslateExtension 4 | { 5 | public string Key { get; set; } 6 | public string Context { get; set; } 7 | 8 | public TranslateExtension(string key) 9 | { 10 | Key = key; 11 | } 12 | 13 | public string ProvideValue() 14 | { 15 | var key = Key; 16 | if (!string.IsNullOrWhiteSpace(Context)) 17 | { 18 | key = $"{Context}|{Key}"; 19 | } 20 | 21 | return new Translate()[key]; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /publish.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | 3 | $target="win-x64" 4 | [xml]$proj = Get-Content src/GwentTracker/GwentTracker.csproj 5 | [string]$version=$proj.Project.PropertyGroup.VersionPrefix 6 | $version=$version.Trim() 7 | $release_name="gwent-tracker_v${version}_${target}" 8 | $target_dir="artifacts\$release_name" 9 | 10 | dotnet publish -r "$target" --self-contained -o "$target_dir" -c Release -p:PublishSingleFile=true src/GwentTracker/GwentTracker.csproj 11 | cp readme.md,changelog.md -Destination "$target_dir" -Force 12 | rm "$target_dir\*" -include *.json, *.pdb 13 | 14 | Compress-Archive -Path "$target_dir" -DestinationPath "$target_dir.zip" -Force 15 | 16 | rm -r "$target_dir" -------------------------------------------------------------------------------- /src/GwentTracker/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace GwentTracker 7 | { 8 | public static class EnumExtensions 9 | { 10 | public static string GetDescription(this T source) where T : Enum 11 | { 12 | var field = source.GetType().GetField(source.ToString()); 13 | if (field == null) 14 | return string.Empty; 15 | 16 | var attributes = field.GetCustomAttributes(false).ToArray(); 17 | return attributes.Any() && !string.IsNullOrEmpty(attributes[0].Description) ? attributes[0].Description : source.ToString(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/GwentTracker/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/GwentTracker/settings.ini: -------------------------------------------------------------------------------- 1 | # Whether or not to automatically read new saves 2 | autoload=true 3 | 4 | # Location of your save game files 5 | defaultSavePath=%USERPROFILE%\Documents\The Witcher 3\gamesaves 6 | 7 | # Where to retrieve card images 8 | texturePath=https://rfvgyhn.blob.core.windows.net/gwent/{0}.png 9 | 10 | # Save a copy of the card texture if it doesn't already exist in the textures folder 11 | cacheRemoteTextures=true 12 | 13 | # Log to a file named log.txt 14 | logToFile=false 15 | 16 | # If left unspecified, culture will default to system's default culture 17 | # Culture codes are: ar, cs, de, en, es, es-mx, fr, hu, it, ja, kr, pl, pt-br, ru, tr, zh, zh-cn 18 | #culture=en 19 | 20 | # In-case your default font doesn't have the glyphs needed by your culture setting, specify a font that does have them 21 | #fontFamily=Noto Sans CJK JP -------------------------------------------------------------------------------- /src/GwentTracker/EnumDescriptionConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Avalonia.Data.Converters; 4 | using GwentTracker.Localization; 5 | 6 | namespace GwentTracker 7 | { 8 | public class EnumDescriptionConverter : IValueConverter 9 | { 10 | private readonly Translate _t; 11 | 12 | public EnumDescriptionConverter() 13 | { 14 | _t = new Translate(); 15 | } 16 | 17 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | if (value != null && value is Enum e) 20 | return _t[e.GetDescription()]; 21 | 22 | return value?.ToString() ?? string.Empty; 23 | } 24 | 25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 26 | { 27 | throw new NotImplementedException(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/GwentTracker/Localization/TranslateStringTypeConverter.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using YamlDotNet.Core; 4 | using YamlDotNet.Core.Events; 5 | using YamlDotNet.Serialization; 6 | 7 | namespace GwentTracker.Localization 8 | { 9 | public class TranslateStringConverter : IYamlTypeConverter 10 | { 11 | private readonly Translate _translate; 12 | 13 | public TranslateStringConverter() 14 | { 15 | _translate = new Translate(); 16 | } 17 | public bool Accepts(Type type) 18 | { 19 | return type == typeof(string); 20 | } 21 | 22 | public object? ReadYaml(IParser parser, Type type) 23 | { 24 | var value = parser.Consume().Value; 25 | return _translate[value]; 26 | } 27 | 28 | public void WriteYaml(IEmitter emitter, object? value, Type type) 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/GwentTracker/Model/CollectionProgress.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | 3 | namespace GwentTracker.Model 4 | { 5 | public class CollectionProgress : ReactiveObject 6 | { 7 | private int _collected; 8 | public int Collected 9 | { 10 | get => _collected; 11 | set => this.RaiseAndSetIfChanged(ref _collected, value); 12 | } 13 | 14 | private int _needed; 15 | public int Needed 16 | { 17 | get => _needed; 18 | set => this.RaiseAndSetIfChanged(ref _needed, value); 19 | } 20 | 21 | private int _copies; 22 | 23 | public int Copies 24 | { 25 | get => _copies; 26 | set => this.RaiseAndSetIfChanged(ref _copies, value); 27 | } 28 | 29 | private int _total; 30 | public int Total 31 | { 32 | get => _total; 33 | set => this.RaiseAndSetIfChanged(ref _total, value); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /tools/yml-to-pot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source="../src/GwentTracker/data" 4 | dest="../src/GwentTracker/locale/cards.pot" 5 | 6 | cat > ${dest} <> ${dest} < Card.${property} 30 | msgid "${escapedValue}" 31 | msgstr "" 32 | EOF 33 | done 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chris 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tools/generate-ui-pot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | domain=gwent-tracker 4 | dest=../src/GwentTracker/locale/${domain}.pot 5 | cardsPot=../src/GwentTracker/locale/cards.pot 6 | sourceFiles=../src/GwentTracker/*Model*/*.cs 7 | 8 | cat > ${dest} <> ${dest} <> ${dest} 36 | -------------------------------------------------------------------------------- /src/GwentTracker/ViewLocator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) The Avalonia Project. All rights reserved. 2 | // Licensed under the MIT license. See licence.md file in the project root for full license information. 3 | 4 | using System; 5 | using Avalonia.Controls; 6 | using Avalonia.Controls.Templates; 7 | using GwentTracker.ViewModels; 8 | 9 | namespace GwentTracker 10 | { 11 | public class ViewLocator : IDataTemplate 12 | { 13 | public bool SupportsRecycling => false; 14 | 15 | public Control Build(object data) 16 | { 17 | var name = data.GetType().FullName?.Replace("ViewModel", "View"); 18 | 19 | if (name is null) 20 | return new TextBlock { Text = "Not Found. Unknown viewmodel" }; 21 | 22 | var type = Type.GetType(name); 23 | 24 | if (type != null) 25 | { 26 | return (Control)Activator.CreateInstance(type); 27 | } 28 | else 29 | { 30 | return new TextBlock { Text = "Not Found: " + name }; 31 | } 32 | } 33 | 34 | public bool Match(object data) 35 | { 36 | return data is MainWindowViewModel; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/GwentTracker/Localization/Translate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NGettext; 4 | using Splat; 5 | 6 | namespace GwentTracker.Localization 7 | { 8 | public class Translate 9 | { 10 | private readonly IEnumerable _catalogs; 11 | 12 | public Translate() : this(null) { } 13 | public Translate(IEnumerable catalog) 14 | { 15 | _catalogs = catalog ?? Locator.Current.GetServices(); 16 | 17 | if (_catalogs == null) 18 | throw new ArgumentNullException(nameof(catalog), "Provide instance in constructor or register IEnumerable with container."); 19 | } 20 | 21 | public string this[string key] 22 | { 23 | get 24 | { 25 | var parts = key.Split('|'); 26 | 27 | if (parts.Length > 1) 28 | return GetTranslation(key, c => c.GetParticularString(parts[0], parts[1])); 29 | 30 | return GetTranslation(key, c => c.GetString(key)); 31 | } 32 | } 33 | 34 | private string GetTranslation(string fallback, Func getString) 35 | { 36 | var value = fallback; 37 | foreach (var catalog in _catalogs) 38 | { 39 | value = getString(catalog); 40 | if (value != fallback) 41 | break; 42 | } 43 | 44 | return value; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | publish() { 6 | target=$1 7 | version=$(grep -oPm1 "(?<=)[^<]+" src/GwentTracker/GwentTracker.csproj) # use something like xml_grep if this regex becomes a problem 8 | release_name="gwent-tracker_v${version}_$target" 9 | icon=$(readlink -f src/GwentTracker/Assets/collector.ico) 10 | rcedit=$(readlink -f tools/rcedit-x64.exe) 11 | 12 | pushd "tools" 13 | ./compile-po.sh 14 | popd 15 | 16 | dotnet restore -r $target 17 | dotnet publish src/GwentTracker/GwentTracker.csproj -r "$target" --self-contained true --no-restore -o "artifacts/$release_name" -c Release -p:PublishSingleFile=true 18 | cp readme.md changelog.md "artifacts/$release_name" 19 | rm artifacts/"$release_name"/*.pdb 20 | 21 | case "$target" in 22 | lin*) 23 | sed -i "s:^defaultSavePath.*$:defaultSavePath=~/.local/share/Steam/steamapps/compatdata/292030/pfx/drive_c/users/steamuser/My Documents/The Witcher 3/gamesaves/:" "artifacts/"$release_name"/settings.ini" 24 | tar czvf "artifacts/$release_name.tar.gz" -C "artifacts" "$release_name" 25 | ;; 26 | win*) 27 | # Workaround for https://github.com/dotnet/sdk/issues/3943 28 | WINEDEBUG=fixme-all wine "${rcedit}" "artifacts/$release_name/GwentTracker.exe" --set-icon "${icon}" 29 | (cd "artifacts/$release_name" && zip -qr -b "${XDG_RUNTIME_DIR:-/tmp}" - .) > "artifacts/$release_name.zip" 30 | ;; 31 | esac 32 | 33 | rm -r "artifacts/$release_name" 34 | } 35 | 36 | publish "linux-x64" 37 | publish "win-x64" 38 | -------------------------------------------------------------------------------- /src/GwentTracker/MissableStateToPathConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Avalonia.Data.Converters; 4 | using Avalonia.Media; 5 | using GwentTracker.Model; 6 | 7 | namespace GwentTracker 8 | { 9 | public class MissableStateToPathConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (value != null && Enum.TryParse(value.ToString(), out MissableState state)) 14 | { 15 | switch (state) 16 | { 17 | case MissableState.Active: 18 | return ""; 19 | case MissableState.Missed: 20 | return Geometry.Parse("M9,7H11L12,9.5L13,7H15L13,12L15,17H13L12,14.5L11,17H9L11,12L9,7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z"); 21 | case MissableState.Obtained: 22 | return Geometry.Parse("M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M12 20C7.59 20 4 16.41 4 12S7.59 4 12 4 20 7.59 20 12 16.41 20 12 20M16.59 7.58L10 14.17L7.41 11.59L6 13L10 17L18 9L16.59 7.58Z"); 23 | default: 24 | throw new ArgumentOutOfRangeException(nameof(value)); 25 | } 26 | } 27 | 28 | throw new ArgumentOutOfRangeException(nameof(value)); 29 | } 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | throw new NotSupportedException(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /tools/card-pot-to-po.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Depends on: 6 | # jq - https://stedolan.github.io/jq/ 7 | # xml-to-json - https://github.com/sinelaw/xml-to-json 8 | 9 | scriptName=$(basename "$0") 10 | data="/mnt/secondary/witcher3/uncooked-data/base/gameplay/items" 11 | hashMap="card-id-hash-map.txt" 12 | 13 | if [[ ${#@} = 0 ]]; then 14 | locales=("ar" "cs" "de" "es" "es-mx" "fr" "hu" "it" "ja" "ko" "pl" "pt-br" "ru" "tr" "zh" "zh-cn") 15 | else 16 | locales=($@) 17 | fi 18 | 19 | . to-po.sh 20 | 21 | function prepareId() { 22 | echo "$1" 23 | } 24 | 25 | function jqstr() { 26 | echo ".redxml.custom.gwint_${1}.card[]" 27 | } 28 | 29 | function getHashFromId() { 30 | grep "$1$" "$hashMap" | head -n1 | cut -d '|' -f 1 31 | } 32 | 33 | function getEnglishString() { 34 | find "$strings" -name "en*.csv" |\ 35 | xargs -n1 grep "${1}" 2>/dev/null |\ 36 | head -n1 |\ 37 | cut -d '|' -f 1,4 |\ 38 | cleanStr ${2} 39 | } 40 | 41 | function addCardsToMap() { 42 | echo "Adding cards to map..." 43 | while read line; do 44 | IFS=' ' read -ra parts <<< "$line" 45 | local index=${parts[0]} 46 | local titleKey=${parts[1]} 47 | local descKey=${parts[2]} 48 | 49 | local titleHash="" 50 | case ${titleKey} in 51 | Mysterious) titleKey=gwint_name_avallach;descKey=gwint_desc_avallach;; 52 | Crach_an_Craite) titleHash=f3a32552;; 53 | esac 54 | 55 | [[ "$titleHash" = "" ]] && titleHash=$(getHashFromId "$titleKey") 56 | local descHash=$(getHashFromId "$descKey") 57 | 58 | addToMap "$(getEnglishString ${titleHash} true)" 59 | addToMap "$(getEnglishString ${descHash} false)" 60 | done <<< $({ xml-to-json "${data}/def_gwint_battle_king_cards.xml" | jq -r "$(jqstr battle_king_card_definitions)"; \ 61 | xml-to-json "${data}/def_gwint_cards_final.xml" | jq "$(jqstr card_definitions_final)"; 62 | } | jq -r '"\(.index) \(.title) \(.description)"') 63 | } 64 | 65 | addCardsToMap 66 | addMiscToMap 67 | writePoFiles cards 68 | printMissing 69 | 70 | -------------------------------------------------------------------------------- /src/GwentTracker/Model/Card.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using ReactiveUI; 5 | 6 | namespace GwentTracker.Model 7 | { 8 | [Flags] 9 | public enum CardSource 10 | { 11 | [Description("Base Game")] 12 | BaseGame = 1, 13 | 14 | [Description("Hearts of Stone")] 15 | HeartsOfStone = 2, 16 | 17 | [Description("Blood and Wine")] 18 | BloodAndWine = 4, 19 | 20 | [Description("DLC")] 21 | Dlc = HeartsOfStone | BloodAndWine 22 | } 23 | 24 | public class Card : ReactiveObject 25 | { 26 | private static readonly HashSet HoSCards = new HashSet { 17, 18, 19, 20, 21, 368, 478, 1005, 2005, 3005, 4005 }; 27 | private static readonly HashSet BandWCards = new HashSet { 22, 23 }; 28 | public int Index { get; set; } 29 | public string Name { get; set; } 30 | public string Flavor { get; set; } 31 | public int MaxCopies { get; set; } = 1; 32 | public int? AttachedTo { get; set; } 33 | 34 | private int _copies; 35 | public int Copies 36 | { 37 | get => _copies; 38 | set => this.RaiseAndSetIfChanged(ref _copies, value); 39 | } 40 | private bool _obtained; 41 | public bool Obtained 42 | { 43 | get => _obtained; 44 | set => this.RaiseAndSetIfChanged(ref _obtained, value); 45 | } 46 | public string Deck { get; set; } 47 | public CombatDetails Combat { get; set; } 48 | public string Type { get; set; } 49 | public Location[] Locations { get; set; } 50 | 51 | public CardSource Source 52 | { 53 | get 54 | { 55 | if (HoSCards.Contains(Index)) 56 | return CardSource.HeartsOfStone; 57 | 58 | if ((Index >= 500 && Index < 600) || Index > 5000 || BandWCards.Contains(Index)) 59 | return CardSource.BloodAndWine; 60 | 61 | return CardSource.BaseGame; 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/GwentTracker/Localization/MoLoader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using NGettext.Loaders; 3 | using NGettext.Plural; 4 | 5 | namespace GwentTracker.Localization 6 | { 7 | public class MoLoader : NGettext.Loaders.MoLoader 8 | { 9 | protected override string GetFileName(string localeDir, string domain, string locale) 10 | { 11 | return Path.Combine(localeDir, $"{domain}.{locale.ToLowerInvariant()}.mo"); 12 | } 13 | 14 | public MoLoader(string domain, string localeDir, IPluralRuleGenerator pluralRuleGenerator, MoFileParser parser) : base(domain, localeDir, pluralRuleGenerator, parser) 15 | { 16 | } 17 | 18 | public MoLoader(string filePath, IPluralRuleGenerator pluralRuleGenerator, MoFileParser parser) : base(filePath, pluralRuleGenerator, parser) 19 | { 20 | } 21 | 22 | public MoLoader(Stream moStream, IPluralRuleGenerator pluralRuleGenerator, MoFileParser parser) : base(moStream, pluralRuleGenerator, parser) 23 | { 24 | } 25 | 26 | public MoLoader(string domain, string localeDir, IPluralRuleGenerator pluralRuleGenerator) : base(domain, localeDir, pluralRuleGenerator) 27 | { 28 | } 29 | 30 | public MoLoader(string domain, string localeDir, MoFileParser parser) : base(domain, localeDir, parser) 31 | { 32 | } 33 | 34 | public MoLoader(string domain, string localeDir) : base(domain, localeDir) 35 | { 36 | } 37 | 38 | public MoLoader(string filePath, IPluralRuleGenerator pluralRuleGenerator) : base(filePath, pluralRuleGenerator) 39 | { 40 | } 41 | 42 | public MoLoader(string filePath, MoFileParser parser) : base(filePath, parser) 43 | { 44 | } 45 | 46 | public MoLoader(string filePath) : base(filePath) 47 | { 48 | } 49 | 50 | public MoLoader(Stream moStream, IPluralRuleGenerator pluralRuleGenerator) : base(moStream, pluralRuleGenerator) 51 | { 52 | } 53 | 54 | public MoLoader(Stream moStream, MoFileParser parser) : base(moStream, parser) 55 | { 56 | } 57 | 58 | public MoLoader(Stream moStream) : base(moStream) 59 | { 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Gwent Tracker [![Translation Progress](https://badges.crowdin.net/gwent-tracker/localized.svg)][1] 2 | 3 | 4 | Application that reads The Witcher 3 save files and reports gwent card 5 | collection progress for the collect 'em all achievement. 6 | 7 | Download the latest release from [Github]. 8 | 9 | ![Screenshot] 10 | 11 | ## Installation 12 | 13 | This application doesn't require any installation. It's a standalone executable. 14 | 15 | 1. Download archive for your operating system 16 | 2. Extract - On windows, right click file and select extract. On linux, use your systems archive manager or `unzip` 17 | 3. Run the executable named _GwentTracker_ 18 | 19 | 20 | ## Usage 21 | 22 | The application will load the latest save in your save game directory. It will also watch that directory for changes so you can leave the app open as you play and it will automatically update. 23 | 24 | All column headers are sortable (including the checkbox column). You can `Shift+Click` column headers to sort by more one than one column at a time. 25 | 26 | You may use the filter text box to narrow down the cards that are shown. For example, typing _velen_ into the textbox and clicking the add button will only show cards with the word velen in their name, deck, type, location or region. 27 | 28 | ## Settings 29 | 30 | Settings are in the file named `settings.ini`. You likely won't need to change any of these if you have a default installation of The Witcher 3. 31 | 32 | | Name | Description 33 | | ------------------- | ------------ 34 | | Autoload | Whether or not to automatically read new saves 35 | | DefaultSavePath | Location of your save game files 36 | | TexturePath | Where to retrieve card images 37 | | CacheRemoteTextures | Save a copy of the card texture if it doesn't already exist in the textures folder 38 | | LogToFile | Log to a file named log.txt. This can be useful for debugging problems 39 | | Culture | If left unspecified, culture will default to system's default culture. Culture codes are: ar, cs, de, en, es, es-mx, fr, hu, it, ja, kr, pl, pt-br, ru, tr, zh, zh-cn 40 | | FontFamily | In-case your default font doesn't have the glyphs needed by your culture setting, specify a font that does have them 41 | 42 | ## Translations 43 | 44 | You can submit translations via [Crowdin] or direct pull requests. 45 | 46 | [1]: https://crowdin.com/project/gwent-tracker 47 | [Github]: https://github.com/Rfvgyhn/gwent-tracker/releases 48 | [Screenshot]: screenshot.png?raw=true 49 | [Crowdin]: https://crwd.in/gwent-tracker -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/GwentTracker/Model/MissableInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive.Linq; 3 | using Avalonia.Media; 4 | using ReactiveUI; 5 | 6 | namespace GwentTracker.Model 7 | { 8 | public enum MissableState 9 | { 10 | Active, 11 | Missed, 12 | Obtained 13 | } 14 | 15 | public class MissableInfo : ReactiveObject 16 | { 17 | public int[] CardIds { get; set; } 18 | public string QuestName { get; set; } 19 | public string[] Notes { get; set; } 20 | public Uri Details { get; set; } 21 | public string Region { get; set; } 22 | 23 | private MissableState _state = MissableState.Active; 24 | public MissableState State 25 | { 26 | get => _state; 27 | set => this.RaiseAndSetIfChanged(ref _state, value); 28 | } 29 | 30 | private string _cutoff; 31 | public string Cutoff 32 | { 33 | get => _cutoff == "self" ? QuestName : _cutoff; 34 | set => _cutoff = value; 35 | } 36 | 37 | private readonly ObservableAsPropertyHelper _detailsVisible; 38 | public bool DetailsVisible => _detailsVisible.Value; 39 | 40 | private readonly ObservableAsPropertyHelper _textWrapping; 41 | public TextWrapping TextWrap => _textWrapping.Value; 42 | 43 | private readonly ObservableAsPropertyHelper _color; 44 | public IBrush Color => _color.Value; 45 | 46 | public MissableInfo() 47 | { 48 | _detailsVisible = this.WhenAnyValue(x => x.State) 49 | .Select(s => s == MissableState.Active) 50 | .ToProperty(this, x => x.DetailsVisible); 51 | _textWrapping = this.WhenAnyValue(x => x.State) 52 | .Select(s => s == MissableState.Active ? TextWrapping.Wrap : TextWrapping.NoWrap) 53 | .ToProperty(this, x => x.TextWrap); 54 | _color = this.WhenAnyValue(x => x.State) 55 | .Select(s => 56 | { 57 | switch (s) 58 | { 59 | case MissableState.Active: 60 | return Brushes.Blue; 61 | case MissableState.Missed: 62 | return Brushes.Red; 63 | case MissableState.Obtained: 64 | return Brushes.Green; 65 | default: 66 | throw new ArgumentOutOfRangeException(nameof(s), s, null); 67 | } 68 | }) 69 | .ToProperty(this, x => x.Color); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tools/localizationkeys.txt: -------------------------------------------------------------------------------- 1 | # Quests 2 | 3 | 1076788|Gwent: Playing Thaler 4 | 1028939|Shock Therapy 5 | 503027|A Dangerous Game 6 | 573611|High Stakes 7 | 433339|A Matter of Life and Death 8 | 1084414|Boatmakers' Hut 9 | 1087893|Person(s) in Distress 10 | 11 | # Other 12 | 13 | 1090467|Unit 14 | 1085124|Hero 15 | 1048308|Velen 16 | 1048311|Novigrad 17 | 1071758|Reason of State 18 | 439463|The Isle of Mists 19 | 397238|Skellige 20 | 1086196|Monsters 21 | 317985|Kaer Trolde 22 | 1055924|Ard Skellig 23 | 1082900|Gedyneith 24 | 1056645|Arinbjorn 25 | 1083005|Urialla Harbor 26 | 1055982|An Skellig 27 | 1055924|Ard Skellige 28 | 1083090|Svorlag 29 | 1055984|Spikeroog 30 | 1056000|Harviken 31 | 1055998|Faroe 32 | 1083114|Trottheim 33 | 166474|Madman Lugos 34 | 1082845|Kaer Muire 35 | 1080245|Benek 36 | 1090466|Leader 37 | 1055925|Kaer Trolde Harbor 38 | 1076354|Passiflora 39 | 1053373|White Orchard 40 | 1056055|Seven Cats Inn 41 | 1082588|Inn at the Crossroads 42 | 175243|Marquise Serenity 43 | 1079409|The Alchemy 44 | 1053453|Oxenfurt 45 | 1056056|Cunny of the Goose 46 | 1076358|Vivaldi Bank 47 | 1053313|Hierarch Square 48 | 351325|Stjepan 49 | 166557|Lambert 50 | 1078464|The Nowhere 51 | 343480|Kaer Morhen 52 | 1053452|Vegelbud Residence 53 | 1085020|Scholar 54 | 472735|Gremist 55 | 545346|Boatwright 56 | 1082481|Oreton 57 | 175253|Olivier 58 | 1076353|Kingfisher 59 | 451163|Trader 60 | 499062|Quartermaster 61 | 1076252|Shopkeeper 62 | 1055961|Midcopse 63 | 1055571|Merchant 64 | 1055965|Lindenvale 65 | 1055991|Claywich 66 | 1076351|Golden Sturgeon 67 | 1084331|Northern Realms 68 | 587384|Haddy 69 | 1048437|Ravvy 70 | 171240|Dijkstra 71 | 481487|Ves 72 | 337285|Bloody Baron 73 | 1055163|Nilfgaardian Nobleman 74 | 1079785|Royal Palace in Vizima 75 | 171290|Zoltan Chivay 76 | 1076356|Rosemary and Thyme 77 | 1082144|Temerian Partisan Hideout 78 | 166803|Druid 79 | 166486|Sjusta 80 | 1055925|Kaer Trolde Harbor 81 | 452889|Old Sage 82 | 1055966|Crow's Perch 83 | 1048434|Caesar Bilzen 84 | 1136327|Upper Mill 85 | 1056063|Carsten 86 | 1209906|The Barrel and Bung Inn 87 | 1204126|Flovive 88 | 1214112|Toussaint 89 | 1205783|Francollarts 90 | 1203226|Caroberta Woods 91 | 1211163|Sommelier 92 | 1209914|The Adder and Jewels Winery 93 | 1205505|Beauclair 94 | 572035|Blacksmith 95 | 1203577|Tourney Grounds 96 | 1203223|Sansretour Valley 97 | 1154987|Pierre 98 | 1171516|Tailor's Workshop 99 | 1205511|Hauteville 100 | 1209913|Perfumery 101 | 543165|Herbalist 102 | 1205915|Castel Ravello Vineyard 103 | 1084926|Armorer 104 | 1204125|The Cockatrice Inn 105 | 1209909|The Pheasantry 106 | 1081957|Card Collector 107 | 1193290|I Have a Gwent Problem 108 | 1138177|Hearts of Stone 109 | 1104501|Shani 110 | 1130173|Shani's Clinic 111 | 1102474|Hilbert 112 | 1136299|Brunwich 113 | 1206915|Blood and Wine 114 | 1208889|Barber 115 | 1017985|Butcher 116 | 1209985|Beauclair Port 117 | 1077334|Madame 118 | 1205758|The Belles of Beauclair 119 | 1209916|Herb Store 120 | -------------------------------------------------------------------------------- /src/GwentTracker/data/missable.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - cardIds: [215, 221] 3 | region: Velen 4 | questName: Boatmakers' Hut Person in Distress 5 | cutoff: None 6 | details: "https://witcher.fandom.com/wiki/Merchant_(Claywich)" 7 | notes: 8 | - Purchase his cards in Claywich after freeing him from the Bandits' Camp. 9 | - cardIds: [12] 10 | region: Novigrad 11 | questName: A Matter of Life and Death 12 | cutoff: self 13 | details: "https://witcher.fandom.com/wiki/A_Matter_of_Life_and_Death_(The_Witcher_3)#Notes" 14 | notes: 15 | - There'll be a small gwent tournament going on in the main courtyard where you'll need to win all the rounds to get all the cards. 16 | - cardIds: [1004, 2004, 3004, 4003] 17 | region: Novigrad 18 | questName: "High Stakes" 19 | cutoff: self 20 | details: 21 | notes: 22 | - If you leave the vicinity of Passiflora the quest will fail. Do not start the quest if you are unsure of winning the gwent matches. You will not be able to go and get more cards If you already signed up for the tournament. 23 | - Consider finishing the Old Pals and Big City Players quests to build a stronger deck before doing this quest. 24 | - You must win the tournament to get every card. Recommend saving before each game. 25 | - Save between wins. This quest is the only way to obtain all leader cards. 26 | - Sasha is the toughest opponent with her Nilfgaardian deck. She plays a very strong spy-themed deck which is also filled with high-strength unit cards and an entire roster of Nilfgaard and neutral heroes. Counter her with a lot of decoys and spy cards; keep the unit cards in the deck as close to 22 as possible and with the highest possible unit strength. The White Flame leader card is useful for countering her unique leader card which draws a unit card from the player's discard pile for her own use. It is HIGHLY RECOMMENDED players make a save if they wish to win the tournament outright, otherwise Sasha beats Tybalt in the final round. She can be romanced if Geralt agrees to her proposition. 27 | - cardIds: [7] 28 | region: Novigrad 29 | questName: "Gwent: Playing Thaler" 30 | cutoff: Reason of State 31 | details: "https://witcher.fandom.com/wiki/Gwent:_Playing_Thaler" 32 | notes: 33 | - Find Thaler by completing A Deadly Plot. 34 | - cardIds: [101, 208, 302] 35 | region: Novigrad 36 | questName: A Dangerous Game 37 | cutoff: The Isle of Mists 38 | details: "https://witcher.fandom.com/wiki/A_Dangerous_Game#Notes" 39 | notes: 40 | - Choose to keep the cards. 41 | - You can still get the cards later if you choose the gold. During The Great Escape, all 3 can be found on the prison commander's body after he's slain. 42 | - cardIds: [303] 43 | region: Skellige 44 | questName: Shock Therapy 45 | cutoff: None 46 | details: "https://witcher.fandom.com/wiki/Shock_Therapy" 47 | notes: 48 | - Complete this quest that can be started by talking to some druids in Ard Skellig. -------------------------------------------------------------------------------- /src/GwentTracker/GwentTracker.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | WinExe 5 | 1.5.1 6 | Latest 7 | Assets/collector.ico 8 | 9 | 10 | true 11 | partial 12 | true 13 | false 14 | false 15 | false 16 | true 17 | 18 | 19 | 20 | 21 | %(Filename) 22 | 23 | 24 | Designer 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Always 49 | true 50 | 51 | 52 | Always 53 | true 54 | false 55 | 56 | 57 | Always 58 | true 59 | 60 | 61 | Never 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/GwentTracker/locale/gwent-tracker.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Content-Type: text/plain; charset=UTF-8\n" 4 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 5 | "X-Poedit-KeywordsList: GetString;GetPluralString:1,2;GetParticularString:1c,2;GetParticularPluralString:1c,2,3;_;_n:1,2;_p:1c,2;_pn:1c,2,3\n" 6 | 7 | #: src/GwentTracker/Model/Card.cs:11 8 | msgid "Base Game" 9 | msgstr "" 10 | 11 | #: src/GwentTracker/Model/Card.cs:14 12 | msgid "Hearts of Stone" 13 | msgstr "" 14 | 15 | #: src/GwentTracker/Model/Card.cs:17 16 | msgid "Blood and Wine" 17 | msgstr "" 18 | 19 | #: src/GwentTracker/Model/Card.cs:20 20 | msgid "DLC" 21 | msgstr "" 22 | 23 | #: src/GwentTracker/ViewModels/MainWindowViewModel.cs:131 24 | msgid "Unable to load card info" 25 | msgstr "" 26 | 27 | #: src/GwentTracker/ViewModels/MainWindowViewModel.cs:176 28 | msgid "Unable to load save game" 29 | msgstr "" 30 | 31 | #: src/GwentTracker/ViewModels/MainWindowViewModel.cs:338 32 | msgid "Obtained {cardNames}" 33 | msgstr "" 34 | 35 | #: src/GwentTracker/Views/MainWindow.xaml:1 -> Window.Title 36 | msgid "GwentTracker" 37 | msgstr "" 38 | 39 | #: src/GwentTracker/Views/MainWindow.xaml:55 -> TextBlock.Text 40 | msgid "Achievement Progress" 41 | msgstr "" 42 | 43 | #: src/GwentTracker/Views/MainWindow.xaml:58 -> TextBlock.Text 44 | msgid "Card Collector" 45 | msgstr "" 46 | 47 | #: src/GwentTracker/Views/MainWindow.xaml:67 -> TextBlock.Text 48 | msgid "I Have a Gwent Problem" 49 | msgstr "" 50 | 51 | #: src/GwentTracker/Views/MainWindow.xaml:78 -> TextBlock.Text 52 | msgid "Card Progress" 53 | msgstr "" 54 | 55 | #: src/GwentTracker/Views/MainWindow.xaml:102 -> TextBlock.Text 56 | msgid "Missable Cards by Quest" 57 | msgstr "" 58 | 59 | #: src/GwentTracker/Views/MainWindow.xaml:113 -> TextBlock.Text 60 | msgid "* {0}" 61 | msgstr "" 62 | 63 | #: src/GwentTracker/Views/MainWindow.xaml:125 -> TextBlock.Text 64 | #: src/GwentTracker/Views/MainWindow.xaml:196 -> DataGridTextColumn.Header 65 | msgid "Region" 66 | msgstr "" 67 | 68 | #: src/GwentTracker/Views/MainWindow.xaml:131 -> TextBlock.Text 69 | msgid "Cutoff" 70 | msgstr "" 71 | 72 | #: src/GwentTracker/Views/MainWindow.xaml:133 -> TextBlock.Text 73 | msgid "The point at which you can no longer obtain the card(s)" 74 | msgstr "" 75 | 76 | #: src/GwentTracker/Views/MainWindow.xaml:149 -> FilterString.Watermark 77 | msgid "Filter" 78 | msgstr "" 79 | 80 | #: src/GwentTracker/Views/MainWindow.xaml:150 -> AddFilter.Content 81 | msgid "Add" 82 | msgstr "" 83 | 84 | #: src/GwentTracker/Views/MainWindow.xaml:178 -> DataGridTextColumn.Header 85 | msgid "Name" 86 | msgstr "" 87 | 88 | #: src/GwentTracker/Views/MainWindow.xaml:179 -> DataGridTemplateColumn.Header 89 | msgid "Copies" 90 | msgstr "" 91 | 92 | #: src/GwentTracker/Views/MainWindow.xaml:193 -> DataGridTextColumn.Header 93 | msgid "Deck" 94 | msgstr "" 95 | 96 | #: src/GwentTracker/Views/MainWindow.xaml:194 -> DataGridTextColumn.Header 97 | msgid "Type" 98 | msgstr "" 99 | 100 | #: src/GwentTracker/Views/MainWindow.xaml:195 -> DataGridTextColumn.Header 101 | msgid "Location" 102 | msgstr "" 103 | 104 | #: src/GwentTracker/Views/MainWindow.xaml:197 -> DataGridTextColumn.Header 105 | msgid "Source" 106 | msgstr "" 107 | 108 | -------------------------------------------------------------------------------- /GwentTracker.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F05256B-DA9E-4E0A-A9AA-F2246CE91BFF}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GwentTracker", "src\GwentTracker\GwentTracker.csproj", "{52AF4092-2357-4C02-91F0-06C15FC6562A}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "W3SavegameEditor.Core", "W3SavegameEditor\W3SavegameEditor.Core\W3SavegameEditor.Core.csproj", "{4E265117-5721-4A1E-9AB2-B4B75C434A9F}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Debug|x64.ActiveCfg = Debug|Any CPU 28 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Debug|x64.Build.0 = Debug|Any CPU 29 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Debug|x86.ActiveCfg = Debug|Any CPU 30 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Debug|x86.Build.0 = Debug|Any CPU 31 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Release|x64.ActiveCfg = Release|Any CPU 34 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Release|x64.Build.0 = Release|Any CPU 35 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Release|x86.ActiveCfg = Release|Any CPU 36 | {52AF4092-2357-4C02-91F0-06C15FC6562A}.Release|x86.Build.0 = Release|Any CPU 37 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Debug|x64.ActiveCfg = Debug|Any CPU 40 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Debug|x64.Build.0 = Debug|Any CPU 41 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Debug|x86.ActiveCfg = Debug|Any CPU 42 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Debug|x86.Build.0 = Debug|Any CPU 43 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Release|x64.ActiveCfg = Release|Any CPU 46 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Release|x64.Build.0 = Release|Any CPU 47 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Release|x86.ActiveCfg = Release|Any CPU 48 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F}.Release|x86.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(NestedProjects) = preSolution 51 | {52AF4092-2357-4C02-91F0-06C15FC6562A} = {0F05256B-DA9E-4E0A-A9AA-F2246CE91BFF} 52 | {4E265117-5721-4A1E-9AB2-B4B75C434A9F} = {0F05256B-DA9E-4E0A-A9AA-F2246CE91BFF} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /src/GwentTracker/Views/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive; 3 | using System.Reactive.Disposables; 4 | using System.Reactive.Linq; 5 | using Avalonia.Controls; 6 | using Avalonia.Input; 7 | using Avalonia.Markup.Xaml; 8 | using Avalonia.Media; 9 | using Avalonia.ReactiveUI; 10 | using Avalonia.Threading; 11 | using GwentTracker.ViewModels; 12 | using ReactiveUI; 13 | 14 | namespace GwentTracker.Views 15 | { 16 | public class MainWindow : ReactiveWindow 17 | { 18 | private DataGrid Cards => this.FindControl("Cards"); 19 | private ItemsControl Messages => this.FindControl("Messages"); 20 | private TextBox FilterString => this.FindControl("FilterString"); 21 | private ItemsControl Filters => this.FindControl("Filters"); 22 | private ProgressBar LoadGameProgress => this.FindControl("LoadGameProgress"); 23 | private Grid SelectedCard => this.FindControl("SelectedCard"); 24 | private Button AddFilter => this.FindControl