feat: Add new combo cards and implement app settings persistence

- Introduced six new combo cards with unique abilities and IDs.
- Created AppSettings class to manage application settings, including starting health.
- Updated MainViewModel to utilize AppSettings for player health initialization.
- Modified DeckBuilderView and GameView to reflect new navigation and combo slot functionality.
- Added buttons for removing cards from combo slots in GameView.
- Implemented settings UI to allow users to configure starting health.
- Added PowerShell scripts for managing combo cards and finding available IDs.
This commit is contained in:
alexandriaa
2026-04-12 19:10:22 -04:00
parent f7db1d0d59
commit eae7632bf6
47 changed files with 922 additions and 127 deletions
@@ -0,0 +1,54 @@
{
"description": "Card Database - Documents all cards in the game with their Template IDs",
"notes": "IDs 1-200 are base/combinable creature cards, IDs 10000+ are combo/created cards",
"lastUpdated": "2025-01-13",
"idRanges": {
"baseCards": "1-200",
"comboCards": "10000+"
},
"combiningFormula": "comboTemplateId = 10000 + (minId * 1000) + maxId",
"combinableTypes": ["Creature", "Blank"],
"nonCombinableTypes": ["Spell", "Weapon", "Armor", "Artifact", "Event", "Enchantment"],
"baseCards": [
{ "id": 1, "name": "Irradiated Blob", "element": "Radioactivity", "type": "Creature", "manaCost": 1, "health": 6, "power": 1, "rarity": "Common", "combinable": true },
{ "id": 2, "name": "Confused Predator", "element": "Radioactivity", "type": "Creature", "manaCost": 1, "health": 3, "power": 3, "rarity": "Common", "combinable": true },
{ "id": 3, "name": "Geiger Giant", "element": "Radioactivity", "type": "Creature", "manaCost": 5, "health": 10, "power": 4, "rarity": "Rare", "combinable": true },
{ "id": 4, "name": "Fruiting Body", "element": "Fungus", "type": "Creature", "manaCost": 4, "health": 3, "power": 4, "rarity": "Common", "combinable": true },
{ "id": 5, "name": "Mycelium Spore", "element": "Fungus", "type": "Creature", "manaCost": 2, "health": 1, "power": 1, "rarity": "Common", "combinable": true },
{ "id": 6, "name": "Refuse Slinger", "element": "Toxin", "type": "Creature", "manaCost": 1, "health": 1, "power": 2, "rarity": "Common", "combinable": true },
{ "id": 7, "name": "Pupae Patroller", "element": "Toxin", "type": "Creature", "manaCost": 3, "health": 2, "power": 3, "rarity": "Uncommon", "combinable": true },
{ "id": 8, "name": "Brood Monarch", "element": "Toxin", "type": "Creature", "manaCost": 6, "health": 4, "power": 3, "rarity": "Rare", "combinable": true },
{ "id": 9, "name": "Undead Stump", "element": "Fungus", "type": "Creature", "manaCost": 5, "health": 4, "power": 4, "rarity": "Uncommon", "combinable": true },
{ "id": 10, "name": "Amalgamation", "element": "Flesh", "type": "Creature", "manaCost": 2, "health": 3, "power": 5, "rarity": "Common", "combinable": true },
{ "id": 11, "name": "Heat Mote", "element": "Thermodynamics", "type": "Creature", "manaCost": 1, "health": 1, "power": 2, "rarity": "Common", "combinable": true },
{ "id": 12, "name": "Cold Mote", "element": "Thermodynamics", "type": "Creature", "manaCost": 1, "health": 2, "power": 1, "rarity": "Common", "combinable": true },
{ "id": 13, "name": "Anthropomorphized Meat", "element": "Food", "type": "Creature", "manaCost": 1, "health": 8, "power": 0, "rarity": "Common", "combinable": true },
{ "id": 14, "name": "Jubilee Jester", "element": "Food", "type": "Creature", "manaCost": 5, "health": 6, "power": 6, "rarity": "Rare", "combinable": true },
{ "id": 15, "name": "Questing Tendril", "element": "Eldritch", "type": "Creature", "manaCost": 1, "health": 2, "power": 1, "rarity": "Common", "combinable": true },
{ "id": 16, "name": "Occult Dabbler", "element": "Eldritch", "type": "Creature", "manaCost": 2, "health": 2, "power": 2, "rarity": "Common", "combinable": true },
{ "id": 17, "name": "Dwarf Star Spawn", "element": "Eldritch", "type": "Creature", "manaCost": 2, "health": 1, "power": 3, "rarity": "Common", "combinable": true },
{ "id": 18, "name": "Malformed Summon", "element": "Eldritch", "type": "Creature", "manaCost": 3, "health": 2, "power": 3, "rarity": "Common", "combinable": true },
{ "id": 19, "name": "Eater of Hope", "element": "Eldritch", "type": "Creature", "manaCost": 5, "health": 3, "power": 8, "rarity": "Uncommon", "combinable": true },
{ "id": 20, "name": "Greater Star Spawn", "element": "Eldritch", "type": "Creature", "manaCost": 5, "health": 3, "power": 4, "rarity": "Uncommon", "combinable": true },
{ "id": 21, "name": "Herald of Jaa'aird'thuun", "element": "Eldritch", "type": "Creature", "manaCost": 4, "health": 4, "power": 4, "rarity": "Rare", "combinable": true },
{ "id": 22, "name": "Jaa'aird'thuun", "element": "Eldritch", "type": "Creature", "manaCost": 7, "health": 8, "power": 8, "rarity": "Legendary", "combinable": true }
],
"comboCards": [
{ "id": 11013, "name": "Slightly Glowing Sponge Cakes", "combo": "Irradiated Blob(1) + Anthropomorphized Meat(13)", "element": "Radioactivity", "type": "Creature", "combinable": true, "comboOnly": true, "formula": "10000 + (1*1000) + 13 = 11013" },
{ "id": 12012, "name": "Tempature Mote", "combo": "Heat Mote(11) + Cold Mote(12)", "element": "Thermodynamics", "type": "Creature", "combinable": true, "comboOnly": true, "formula": "10000 + (11*1000) + 12 = 12012" }
],
"comboExamples": [
{ "cards": "Irradiated Blob + Heat Mote", "formula": "10000 + (1*1000) + 11", "resultId": 11011 },
{ "cards": "Irradiated Blob + Cold Mote", "formula": "10000 + (1*1000) + 12", "resultId": 11012 },
{ "cards": "Heat Mote + Cold Mote", "formula": "10000 + (11*1000) + 12", "resultId": 12012 },
{ "cards": "Fruiting Body + Mycelium Spore", "formula": "10000 + (4*1000) + 5", "resultId": 5004 }
],
"notes": [
"Base cards (1-200) are combinable creatures and blanks only",
"Combo cards use IDs 10000+ and are created via combining two base cards",
"Combo formula: 10000 + (minBaseId * 1000) + maxBaseId",
"Only Creature and Blank card types can be combined",
"Spells, Weapons, Armor, Artifacts, Events, and Enchantments cannot be combined",
"Quest table cards (Dwarf Star Spawn, Malformed Summon, etc.) are obtained via quest tokens, not combining"
]
}
+15 -1
View File
@@ -129,6 +129,12 @@ public enum WeaponTargetType
public class Card
{
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Template ID for lookups: 1-200 for base cards, 1000-2000 for combo cards
/// </summary>
public int TemplateId { get; set; }
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public CardType Type { get; set; }
@@ -146,7 +152,7 @@ public class Card
public bool IsLegendary { get; set; }
public int Rarity { get; set; } // 1 = common, 2 = uncommon, 3 = rare, 4 = legendary
public bool IsCombinable { get; set; } // true if this card can be used in the combine system
public bool IsCombinable { get; set; } = true; // true if this card can be used in the combine system
public bool IsComboOnly { get; set; } // true if this card can only be obtained via combining, not added to decks
/// <summary>
@@ -159,6 +165,7 @@ public class Card
return new Card
{
Id = Guid.NewGuid().ToString(),
TemplateId = this.TemplateId,
Name = this.Name,
Description = this.Description,
Type = this.Type,
@@ -219,6 +226,7 @@ public class CreatureCard : Card
var clone = new CreatureCard
{
Id = Guid.NewGuid().ToString(),
TemplateId = this.TemplateId,
Name = this.Name,
Description = this.Description,
Type = this.Type,
@@ -277,6 +285,7 @@ public class SpellCard : Card
return new SpellCard
{
Id = Guid.NewGuid().ToString(),
TemplateId = this.TemplateId,
Name = this.Name,
Description = this.Description,
Type = this.Type,
@@ -328,6 +337,7 @@ public class ArtifactCard : Card
var clone = new ArtifactCard
{
Id = Guid.NewGuid().ToString(),
TemplateId = this.TemplateId,
Name = this.Name,
Description = this.Description,
Type = this.Type,
@@ -380,6 +390,7 @@ public class EventCard : Card
return new EventCard
{
Id = Guid.NewGuid().ToString(),
TemplateId = this.TemplateId,
Name = this.Name,
Description = this.Description,
Type = this.Type,
@@ -431,6 +442,7 @@ public class BlankCard : Card
return new BlankCard
{
Id = Guid.NewGuid().ToString(),
TemplateId = this.TemplateId,
Name = this.Name,
Description = this.Description,
Type = this.Type,
@@ -482,6 +494,7 @@ public class WeaponCard : Card
return new WeaponCard
{
Id = Guid.NewGuid().ToString(),
TemplateId = this.TemplateId,
Name = this.Name,
Description = this.Description,
Type = this.Type,
@@ -604,6 +617,7 @@ public class ArmorCard : Card
return new ArmorCard
{
Id = Guid.NewGuid().ToString(),
TemplateId = this.TemplateId,
Name = this.Name,
Description = this.Description,
Type = this.Type,
@@ -0,0 +1,185 @@
using Card = MagicalDeckbuilder.Cards.Card;
using MagicalDeckbuilder.Logging;
namespace MagicalDeckbuilder.Combining;
/// <summary>
/// Central registry for card combination recipes.
/// Defines all valid combinations between base cards.
/// Formula: combo_template_id = 10000 + (min_id * 1000) + max_id
/// </summary>
public class CardRecipeRegistry
{
private static CardRecipeRegistry? _instance;
private static readonly object _lock = new();
/// <summary>
/// Singleton instance
/// </summary>
public static CardRecipeRegistry Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
_instance ??= new CardRecipeRegistry();
}
}
return _instance;
}
}
/// <summary>
/// Maps combo template ID to the source card template IDs
/// Key: comboTemplateId (int), Value: Tuple of (card1TemplateId, card2TemplateId)
/// </summary>
private readonly Dictionary<int, (int Card1Id, int Card2Id)> _recipes = new();
/// <summary>
/// Reverse lookup: maps source card pair to combo template ID
/// </summary>
private readonly Dictionary<(int, int), int> _reverseLookup = new();
private CardRecipeRegistry()
{
InitializeDefaultRecipes();
}
/// <summary>
/// Calculate the combo template ID for two card template IDs
/// Formula: 10000 + (minId * 1000) + maxId
/// </summary>
public static int CalculateComboTemplateId(int templateId1, int templateId2)
{
var minId = Math.Min(templateId1, templateId2);
var maxId = Math.Max(templateId1, templateId2);
return 10000 + (minId * 1000) + maxId;
}
/// <summary>
/// Calculate the combo template ID for two cards
/// </summary>
public static int CalculateComboTemplateId(Card card1, Card card2)
{
return CalculateComboTemplateId(card1.TemplateId, card2.TemplateId);
}
/// <summary>
/// Check if a combo recipe exists for two cards
/// </summary>
public bool HasRecipe(int templateId1, int templateId2)
{
var key = (Math.Min(templateId1, templateId2), Math.Max(templateId1, templateId2));
return _reverseLookup.ContainsKey(key);
}
/// <summary>
/// Check if a combo recipe exists for two cards
/// </summary>
public bool HasRecipe(Card card1, Card card2)
{
return HasRecipe(card1.TemplateId, card2.TemplateId);
}
/// <summary>
/// Get the combo template ID for two cards, if a recipe exists
/// </summary>
public int? GetComboTemplateId(int templateId1, int templateId2)
{
var key = (Math.Min(templateId1, templateId2), Math.Max(templateId1, templateId2));
return _reverseLookup.TryGetValue(key, out var comboId) ? comboId : null;
}
/// <summary>
/// Get the source card IDs for a combo template ID
/// </summary>
public (int Card1Id, int Card2Id)? GetSourceCards(int comboTemplateId)
{
return _recipes.TryGetValue(comboTemplateId, out var recipe) ? recipe : null;
}
/// <summary>
/// Get all registered combo template IDs
/// </summary>
public IEnumerable<int> GetAllComboTemplateIds()
{
return _recipes.Keys;
}
/// <summary>
/// Register a new combination recipe
/// </summary>
public void RegisterRecipe(int card1TemplateId, int card2TemplateId, int comboTemplateId)
{
var sortedCards = (Math.Min(card1TemplateId, card2TemplateId), Math.Max(card1TemplateId, card2TemplateId));
_recipes[comboTemplateId] = (sortedCards.Item1, sortedCards.Item2);
_reverseLookup[sortedCards] = comboTemplateId;
ErrorLogger.Instance.Debug("CardRecipeRegistry",
$"Registered recipe: {card1TemplateId} + {card2TemplateId} = {comboTemplateId}");
}
/// <summary>
/// Validate that a combo card's template ID matches its recipe
/// </summary>
public bool ValidateComboCard(int card1TemplateId, int card2TemplateId, int comboTemplateId)
{
var expectedComboId = CalculateComboTemplateId(card1TemplateId, card2TemplateId);
return expectedComboId == comboTemplateId;
}
/// <summary>
/// Initialize default combination recipes
/// Only creature and blank cards can be combined.
/// Formula: comboId = 10000 + (minId * 1000) + maxId
/// </summary>
private void InitializeDefaultRecipes()
{
// Radioactive combinations
RegisterRecipe(1, 11, CalculateComboTemplateId(1, 11)); // Irradiated Blob + Heat Mote = 11011
RegisterRecipe(1, 12, CalculateComboTemplateId(1, 12)); // Irradiated Blob + Cold Mote = 11012
RegisterRecipe(1, 13, CalculateComboTemplateId(1, 13)); // Irradiated Blob + Anthropomorphized Meat = 11013
// Thermodynamics combinations
RegisterRecipe(11, 12, CalculateComboTemplateId(11, 12)); // Heat Mote + Cold Mote = 12012
RegisterRecipe(11, 11, CalculateComboTemplateId(11, 11)); // Heat Mote + Heat Mote = 12011
RegisterRecipe(12, 12, CalculateComboTemplateId(12, 12)); // Cold Mote + Cold Mote = 12012 (same as above, but different source)
// Food combinations
RegisterRecipe(13, 1, CalculateComboTemplateId(1, 13)); // Anthropomorphized Meat + Irradiated Blob = 11013 (same as radioactive meat)
// Flesh combinations
RegisterRecipe(10, 13, CalculateComboTemplateId(10, 13)); // Amalgamation + Anthropomorphized Meat = 13010
// Fungus combinations
RegisterRecipe(4, 5, CalculateComboTemplateId(4, 5)); // Fruiting Body + Mycelium Spore = 5004
RegisterRecipe(5, 9, CalculateComboTemplateId(5, 9)); // Mycelium Spore + Undead Stump = 9005
// Toxin combinations
RegisterRecipe(6, 8, CalculateComboTemplateId(6, 8)); // Refuse Slinger + Brood Monarch = 8006
// Eldritch combinations
RegisterRecipe(15, 16, CalculateComboTemplateId(15, 16)); // Questing Tendril + Occult Dabbler = 16015
RegisterRecipe(17, 18, CalculateComboTemplateId(17, 18)); // Dwarf Star Spawn + Malformed Summon = 18017
RegisterRecipe(19, 20, CalculateComboTemplateId(19, 20)); // Eater of Hope + Greater Star Spawn = 20019
RegisterRecipe(21, 22, CalculateComboTemplateId(21, 22)); // Herald + Jaa'aird'thuun = 22021
ErrorLogger.Instance.Info("CardRecipeRegistry", $"Initialized {_recipes.Count} default combination recipes");
}
/// <summary>
/// Debug: Print all registered recipes
/// </summary>
public void DebugPrintRecipes()
{
ErrorLogger.Instance.Info("CardRecipeRegistry", "=== All Combination Recipes ===");
foreach (var kvp in _recipes.OrderBy(x => x.Key))
{
ErrorLogger.Instance.Info("CardRecipeRegistry",
$" {kvp.Value.Card1Id} + {kvp.Value.Card2Id} = {kvp.Key}");
}
}
}
@@ -3,29 +3,32 @@ using Card = MagicalDeckbuilder.Cards.Card;
namespace MagicalDeckbuilder.Combining;
/// <summary>
/// Looks up pre-made combination cards based on two input cards.
/// Format: combine_{sorted_id1}_{sorted_id2}
/// Looks up pre-made combination cards based on two input cards' TemplateIds.
/// Base cards use IDs 1-200, combo cards use IDs 10000+.
/// Formula: combo_template_id = 10000 + (min_id * 1000) + max_id
/// This ensures unique, collision-free IDs for all combinations.
/// Example: Card 1 + Card 2 = 10000 + 1000 + 2 = 11002
/// Example: Card 10 + Card 20 = 10000 + 10000 + 20 = 20020
/// </summary>
public class CombinationLookup
{
private readonly Dictionary<string, Card> _combinationCards = new();
private readonly Dictionary<int, Card> _comboCardsByTemplateId = new();
/// <summary>
/// Initialize with a collection of pre-made combination cards
/// </summary>
public void LoadCombinationCards(IEnumerable<Card> cards)
{
_combinationCards.Clear();
_comboCardsByTemplateId.Clear();
foreach (var card in cards)
{
// Pre-made combo-only cards are stored in the deck
// They have IsComboOnly = true and IsCombinable = true
if (card.IsComboOnly && card.IsCombinable)
// They should have TemplateId in range 1000-2000
if (card.IsComboOnly && card.IsCombinable && card.TemplateId >= 1000)
{
// Create a key from the card name in lowercase with underscores
var key = card.Name.ToLowerInvariant().Replace(" ", "_").Replace("'", "");
_combinationCards[key] = card;
_comboCardsByTemplateId[card.TemplateId] = card;
}
}
}
@@ -36,44 +39,74 @@ public class CombinationLookup
/// </summary>
public Card? FindCombination(Card card1, Card card2)
{
// Sort IDs so order doesn't matter (combine_a_b == combine_b_a)
var ids = new[] { card1.Id, card2.Id }.OrderBy(x => x).ToArray();
var key = $"combine_{ids[0]}_{ids[1]}";
if (card1.TemplateId <= 0 || card2.TemplateId <= 0)
{
// Fallback to name-based lookup if template IDs not set
return FindCombinationByName(card1, card2);
}
if (_combinationCards.TryGetValue(key, out var result))
// Calculate combo template ID: 10000 + (minId * 1000) + maxId
// Example: Card 1 + Card 2 = 10000 + 1000 + 2 = 11002
// Example: Card 10 + Card 20 = 10000 + 10000 + 20 = 20020
var ids = new[] { card1.TemplateId, card2.TemplateId }.OrderBy(x => x).ToArray();
var comboTemplateId = 10000 + (ids[0] * 1000) + ids[1];
if (_comboCardsByTemplateId.TryGetValue(comboTemplateId, out var result))
{
return result;
}
// Try matching by checking combo card descriptions for the two base card names
// The combo card description contains "(combo X + Y)" format
// Fallback to name-based lookup
return FindCombinationByName(card1, card2);
}
/// <summary>
/// Fallback: find combination by matching card names in combo card names
/// </summary>
private Card? FindCombinationByName(Card card1, Card card2)
{
if (card1.Name == card2.Name)
{
var cardName = card1.Name.ToLowerInvariant();
foreach (var kvp in _comboCardsByTemplateId.Values)
{
var comboName = kvp.Name.ToLowerInvariant();
if (comboName.Contains(cardName) && comboName != cardName)
{
return kvp;
}
}
return null;
}
var name1 = card1.Name.ToLowerInvariant();
var name2 = card2.Name.ToLowerInvariant();
foreach (var kvp in _combinationCards)
foreach (var kvp in _comboCardsByTemplateId.Values)
{
var desc = kvp.Value.Description.ToLowerInvariant();
// Check if description contains both card names (with "combo" keyword)
if (desc.Contains("combo") &&
(desc.Contains(name1) || desc.Contains(name1.Replace(" ", ""))) &&
(desc.Contains(name2) || desc.Contains(name2.Replace(" ", ""))))
var comboName = kvp.Name.ToLowerInvariant();
var desc = kvp.Description.ToLowerInvariant();
if (comboName == name1 || comboName == name2)
continue;
bool name1InCombo = comboName.Contains(name1);
bool name2InCombo = comboName.Contains(name2);
if (name1InCombo && name2InCombo)
{
return kvp.Value;
return kvp;
}
if (desc.Contains("combo") && desc.Contains(name1) && desc.Contains(name2))
{
return kvp;
}
}
return null;
}
/// <summary>
/// Sanitize card name for use in lookup key
/// </summary>
private static string SanitizeName(string name)
{
// Remove spaces and special characters, keep only alphanumeric
return new string(name.Where(char.IsLetterOrDigit).ToArray()).ToLowerInvariant();
}
/// <summary>
/// Check if two cards have a pre-made combination available
/// </summary>
@@ -0,0 +1,234 @@
using System.Collections.Generic;
namespace MagicalDeckbuilder.Game;
/// <summary>
/// Static database of all cards in the game with their Template IDs.
/// IDs 1-200: Base/Combinable cards
/// IDs 1000-2000: Combo/Created cards
/// </summary>
public static class CardDatabase
{
/// <summary>
/// All base cards that can be combined (IDs 1-22)
/// </summary>
public static readonly Dictionary<int, CardInfo> BaseCards = new()
{
{ 1, new CardInfo { Id = 1, Name = "Irradiated Blob", Element = "Radioactivity", Type = "Creature", ManaCost = 1, Health = 6, Power = 1, Rarity = "Common" } },
{ 2, new CardInfo { Id = 2, Name = "Confused Predator", Element = "Radioactivity", Type = "Creature", ManaCost = 1, Health = 3, Power = 3, Rarity = "Common" } },
{ 3, new CardInfo { Id = 3, Name = "Geiger Giant", Element = "Radioactivity", Type = "Creature", ManaCost = 5, Health = 10, Power = 4, Rarity = "Rare" } },
{ 4, new CardInfo { Id = 4, Name = "Fruiting Body", Element = "Fungus", Type = "Creature", ManaCost = 4, Health = 3, Power = 4, Rarity = "Common" } },
{ 5, new CardInfo { Id = 5, Name = "Mycelium Spore", Element = "Fungus", Type = "Creature", ManaCost = 2, Health = 1, Power = 1, Rarity = "Common" } },
{ 6, new CardInfo { Id = 6, Name = "Refuse Slinger", Element = "Toxin", Type = "Creature", ManaCost = 1, Health = 1, Power = 2, Rarity = "Common" } },
{ 7, new CardInfo { Id = 7, Name = "Pupae Patroller", Element = "Toxin", Type = "Creature", ManaCost = 3, Health = 2, Power = 3, Rarity = "Uncommon" } },
{ 8, new CardInfo { Id = 8, Name = "Brood Monarch", Element = "Toxin", Type = "Creature", ManaCost = 6, Health = 4, Power = 3, Rarity = "Rare" } },
{ 9, new CardInfo { Id = 9, Name = "Undead Stump", Element = "Fungus", Type = "Creature", ManaCost = 5, Health = 4, Power = 4, Rarity = "Uncommon" } },
{ 10, new CardInfo { Id = 10, Name = "Amalgamation", Element = "Flesh", Type = "Creature", ManaCost = 2, Health = 3, Power = 5, Rarity = "Common" } },
{ 11, new CardInfo { Id = 11, Name = "Heat Mote", Element = "Thermodynamics", Type = "Creature", ManaCost = 1, Health = 1, Power = 2, Rarity = "Common" } },
{ 12, new CardInfo { Id = 12, Name = "Cold Mote", Element = "Thermodynamics", Type = "Creature", ManaCost = 1, Health = 2, Power = 1, Rarity = "Common" } },
{ 13, new CardInfo { Id = 13, Name = "Anthropomorphized Meat", Element = "Food", Type = "Creature", ManaCost = 1, Health = 8, Power = 0, Rarity = "Common" } },
{ 14, new CardInfo { Id = 14, Name = "Jubilee Jester", Element = "Food", Type = "Creature", ManaCost = 5, Health = 6, Power = 6, Rarity = "Rare" } },
{ 15, new CardInfo { Id = 15, Name = "Questing Tendril", Element = "Eldritch", Type = "Creature", ManaCost = 1, Health = 2, Power = 1, Rarity = "Common" } },
{ 16, new CardInfo { Id = 16, Name = "Occult Dabbler", Element = "Eldritch", Type = "Creature", ManaCost = 2, Health = 2, Power = 2, Rarity = "Common" } },
{ 17, new CardInfo { Id = 17, Name = "Dwarf Star Spawn", Element = "Eldritch", Type = "Creature", ManaCost = 2, Health = 1, Power = 3, Rarity = "Common", IsQuestCard = true } },
{ 18, new CardInfo { Id = 18, Name = "Malformed Summon", Element = "Eldritch", Type = "Creature", ManaCost = 3, Health = 2, Power = 3, Rarity = "Common", IsQuestCard = true } },
{ 19, new CardInfo { Id = 19, Name = "Eater of Hope", Element = "Eldritch", Type = "Creature", ManaCost = 5, Health = 3, Power = 8, Rarity = "Uncommon", IsQuestCard = true } },
{ 20, new CardInfo { Id = 20, Name = "Greater Star Spawn", Element = "Eldritch", Type = "Creature", ManaCost = 5, Health = 3, Power = 4, Rarity = "Uncommon", IsQuestCard = true } },
{ 21, new CardInfo { Id = 21, Name = "Herald of Jaa'aird'thuun", Element = "Eldritch", Type = "Creature", ManaCost = 4, Health = 4, Power = 4, Rarity = "Rare", IsQuestCard = true } },
{ 22, new CardInfo { Id = 22, Name = "Jaa'aird'thuun", Element = "Eldritch", Type = "Creature", ManaCost = 7, Health = 8, Power = 8, Rarity = "Legendary", IsQuestCard = true } }
};
/// <summary>
/// All combo cards created by combining base cards (IDs 1000+)
/// </summary>
public static readonly Dictionary<int, ComboCardInfo> ComboCards = new()
{
// Original 8 combo cards
{ 1010, new ComboCardInfo { Id = 1010, Name = "Radioactive Heat", Recipe = "Irradiated Blob (1) + Heat Mote (11)", Element = "Radioactivity" } },
{ 1011, new ComboCardInfo { Id = 1011, Name = "Frozen Blob", Recipe = "Irradiated Blob (1) + Cold Mote (12)", Element = "Radioactivity" } },
{ 1012, new ComboCardInfo { Id = 1012, Name = "Thermal Balance", Recipe = "Heat Mote (11) + Cold Mote (12)", Element = "Thermodynamics" } },
{ 1013, new ComboCardInfo { Id = 1013, Name = "Double Heat", Recipe = "Heat Mote (11) + Heat Mote (11)", Element = "Thermodynamics" } },
{ 1014, new ComboCardInfo { Id = 1014, Name = "Double Cold", Recipe = "Cold Mote (12) + Cold Mote (12)", Element = "Thermodynamics" } },
{ 1015, new ComboCardInfo { Id = 1015, Name = "Radioactive Meat", Recipe = "Irradiated Blob (1) + Anthropomorphized Meat (13)", Element = "Flesh" } },
{ 1022, new ComboCardInfo { Id = 1022, Name = "Slightly Glowing Sponge Cakes", Recipe = "Anthropomorphized Meat (13) + Irradiated Blob (1)", Element = "Food" } },
{ 1111, new ComboCardInfo { Id = 1111, Name = "Tempature Mote", Recipe = "Heat Mote (11) + Cold Mote (12)", Element = "Thermodynamics" } },
// New combo cards from list
{ 1106, new ComboCardInfo { Id = 1106, Name = "Frozen Blob", Recipe = "Irradiated Blob (1) + Cold Mote (12)", Element = "Radioactivity" } },
{ 1107, new ComboCardInfo { Id = 1107, Name = "Fire Stump", Recipe = "Undead Stump (9) + Heat Mote (11)", Element = "Thermodynamics" } },
{ 1108, new ComboCardInfo { Id = 1108, Name = "Toxic Oracle", Recipe = "Occult Dabbler (16) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1109, new ComboCardInfo { Id = 1109, Name = "Star Eater", Recipe = "Eater of Hope (19) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1110, new ComboCardInfo { Id = 1110, Name = "Void Consumer", Recipe = "Eater of Hope (19) + Malformed Summon (18)", Element = "Eldritch" } },
{ 1112, new ComboCardInfo { Id = 1112, Name = "Royal Toxin", Recipe = "Brood Monarch (8) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1113, new ComboCardInfo { Id = 1113, Name = "Cold Meat", Recipe = "Anthropomorphized Meat (13) + Cold Mote (12)", Element = "Flesh" } },
{ 1114, new ComboCardInfo { Id = 1114, Name = "Fire Meat", Recipe = "Anthropomorphized Meat (13) + Heat Mote (11)", Element = "Flesh" } },
{ 1115, new ComboCardInfo { Id = 1115, Name = "Eldritch Meat", Recipe = "Anthropomorphized Meat (13) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1116, new ComboCardInfo { Id = 1116, Name = "Fire Mote", Recipe = "Heat Mote (11) + Heat Mote (11)", Element = "Thermodynamics" } },
{ 1117, new ComboCardInfo { Id = 1117, Name = "Cold Mote", Recipe = "Cold Mote (12) + Cold Mote (12)", Element = "Thermodynamics" } },
{ 1119, new ComboCardInfo { Id = 1119, Name = "Toxic Mote", Recipe = "Refuse Slinger (6) + Heat Mote (11)", Element = "Toxin" } },
{ 1120, new ComboCardInfo { Id = 1120, Name = "Radioactive Mote", Recipe = "Irradiated Blob (1) + Heat Mote (11)", Element = "Radioactivity" } },
{ 1121, new ComboCardInfo { Id = 1121, Name = "Fungal Mote", Recipe = "Mycelium Spore (5) + Heat Mote (11)", Element = "Fungus" } },
{ 1122, new ComboCardInfo { Id = 1122, Name = "Flesh Mote", Recipe = "Anthropomorphized Meat (13) + Heat Mote (11)", Element = "Flesh" } },
{ 1123, new ComboCardInfo { Id = 1123, Name = "Thermo Mote", Recipe = "Heat Mote (11) + Heat Mote (11)", Element = "Thermodynamics" } },
{ 1124, new ComboCardInfo { Id = 1124, Name = "Frost Jester", Recipe = "Jubilee Jester (14) + Cold Mote (12)", Element = "Food" } },
{ 1125, new ComboCardInfo { Id = 1125, Name = "Fire Jester", Recipe = "Jubilee Jester (14) + Heat Mote (11)", Element = "Food" } },
{ 1126, new ComboCardInfo { Id = 1126, Name = "Toxic Jester", Recipe = "Jubilee Jester (14) + Refuse Slinger (6)", Element = "Food" } },
{ 1127, new ComboCardInfo { Id = 1127, Name = "Radiant Jester", Recipe = "Jubilee Jester (14) + Irradiated Blob (1)", Element = "Food" } },
{ 1128, new ComboCardInfo { Id = 1128, Name = "Fire Tendril", Recipe = "Questing Tendril (15) + Heat Mote (11)", Element = "Eldritch" } },
{ 1129, new ComboCardInfo { Id = 1129, Name = "Cold Tendril", Recipe = "Questing Tendril (15) + Cold Mote (12)", Element = "Eldritch" } },
{ 1130, new ComboCardInfo { Id = 1130, Name = "Toxic Tendril", Recipe = "Questing Tendril (15) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1131, new ComboCardInfo { Id = 1131, Name = "Star Tendril", Recipe = "Questing Tendril (15) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1132, new ComboCardInfo { Id = 1132, Name = "Abyssal Tendril", Recipe = "Questing Tendril (15) + Jaaaird'thuun (22)", Element = "Eldritch" } },
{ 1133, new ComboCardInfo { Id = 1133, Name = "Fire Oracle", Recipe = "Occult Dabbler (16) + Heat Mote (11)", Element = "Eldritch" } },
{ 1135, new ComboCardInfo { Id = 1135, Name = "Star Mote", Recipe = "Heat Mote (11) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1136, new ComboCardInfo { Id = 1136, Name = "Cold Star Spawn", Recipe = "Cold Mote (12) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1137, new ComboCardInfo { Id = 1137, Name = "Fire Giant", Recipe = "Geiger Giant (3) + Heat Mote (11)", Element = "Thermodynamics" } },
{ 1139, new ComboCardInfo { Id = 1139, Name = "Toxic Blob", Recipe = "Irradiated Blob (1) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1140, new ComboCardInfo { Id = 1140, Name = "Fungal Blob", Recipe = "Irradiated Blob (1) + Mycelium Spore (5)", Element = "Fungus" } },
{ 1141, new ComboCardInfo { Id = 1141, Name = "Flesh Blob", Recipe = "Irradiated Blob (1) + Anthropomorphized Meat (13)", Element = "Flesh" } },
{ 1142, new ComboCardInfo { Id = 1142, Name = "Fire Predator", Recipe = "Confused Predator (2) + Heat Mote (11)", Element = "Thermodynamics" } },
{ 1143, new ComboCardInfo { Id = 1143, Name = "Cold Predator", Recipe = "Confused Predator (2) + Cold Mote (12)", Element = "Flesh" } },
{ 1145, new ComboCardInfo { Id = 1145, Name = "Royal Stump", Recipe = "Undead Stump (9) + Brood Monarch (8)", Element = "Fungus" } },
// Batch 2 combo cards (1080-1105)
{ 1080, new ComboCardInfo { Id = 1080, Name = "Glacial Beast", Recipe = "Geiger Giant (3) + Cold Mote (12)", Element = "Thermodynamics" } },
{ 1081, new ComboCardInfo { Id = 1081, Name = "Heat Hive", Recipe = "Brood Monarch (8) + Heat Mote (11)", Element = "Toxin" } },
{ 1082, new ComboCardInfo { Id = 1082, Name = "Void Mushroom", Recipe = "Mycelium Spore (5) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1083, new ComboCardInfo { Id = 1083, Name = "Radiant Stump", Recipe = "Undead Stump (9) + Heat Mote (11)", Element = "Fungus" } },
{ 1084, new ComboCardInfo { Id = 1084, Name = "Toxic Tendril", Recipe = "Questing Tendril (15) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1085, new ComboCardInfo { Id = 1085, Name = "Cold Tendril", Recipe = "Questing Tendril (15) + Cold Mote (12)", Element = "Eldritch" } },
{ 1086, new ComboCardInfo { Id = 1086, Name = "Star Monarch", Recipe = "Brood Monarch (8) + Malformed Summon (18)", Element = "Eldritch" } },
{ 1087, new ComboCardInfo { Id = 1087, Name = "Cold Hive", Recipe = "Pupae Patroller (7) + Cold Mote (12)", Element = "Toxin" } },
{ 1088, new ComboCardInfo { Id = 1088, Name = "Fungal Stoker", Recipe = "Mycelium Spore (5) + Heat Mote (11)", Element = "Fungus" } },
{ 1089, new ComboCardInfo { Id = 1089, Name = "Cold Refuse", Recipe = "Refuse Slinger (6) + Cold Mote (12)", Element = "Toxin" } },
{ 1090, new ComboCardInfo { Id = 1090, Name = "Abyssal Spawn", Recipe = "Dwarf Star Spawn (17) + Jaaaird'thuun (22)", Element = "Eldritch" } },
{ 1092, new ComboCardInfo { Id = 1092, Name = "Frost Jester", Recipe = "Jubilee Jester (14) + Geiger Giant (3)", Element = "Food" } },
{ 1093, new ComboCardInfo { Id = 1093, Name = "Plasma Jester", Recipe = "Jubilee Jester (14) + Heat Mote (11)", Element = "Food" } },
{ 1094, new ComboCardInfo { Id = 1094, Name = "Radiant Jester", Recipe = "Jubilee Jester (14) + Irradiated Blob (1)", Element = "Food" } },
{ 1095, new ComboCardInfo { Id = 1095, Name = "Void Geiger", Recipe = "Geiger Giant (3) + Malformed Summon (18)", Element = "Eldritch" } },
{ 1096, new ComboCardInfo { Id = 1096, Name = "Fungal Queen", Recipe = "Brood Monarch (8) + Fruiting Body (4)", Element = "Fungus" } },
{ 1097, new ComboCardInfo { Id = 1097, Name = "Cold Fruiting", Recipe = "Fruiting Body (4) + Cold Mote (12)", Element = "Fungus" } },
{ 1098, new ComboCardInfo { Id = 1098, Name = "Heat Fruiting", Recipe = "Fruiting Body (4) + Heat Mote (11)", Element = "Fungus" } },
{ 1099, new ComboCardInfo { Id = 1099, Name = "Cold Oracle", Recipe = "Occult Dabbler (16) + Cold Mote (12)", Element = "Eldritch" } },
{ 1100, new ComboCardInfo { Id = 1100, Name = "Dark Oracle", Recipe = "Occult Dabbler (16) + Jaaaird'thuun (22)", Element = "Eldritch" } },
{ 1101, new ComboCardInfo { Id = 1101, Name = "Cold Giant", Recipe = "Geiger Giant (3) + Cold Mote (12)", Element = "Thermodynamics" } },
{ 1102, new ComboCardInfo { Id = 1102, Name = "Toxic Giant", Recipe = "Geiger Giant (3) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1103, new ComboCardInfo { Id = 1103, Name = "Flesh Giant", Recipe = "Geiger Giant (3) + Anthropomorphized Meat (13)", Element = "Flesh" } },
{ 1104, new ComboCardInfo { Id = 1104, Name = "Star Prophet", Recipe = "Herald of Jaaaird'thuun (21) + Cold Mote (12)", Element = "Eldritch" } },
{ 1105, new ComboCardInfo { Id = 1105, Name = "Void Prophet", Recipe = "Herald of Jaaaird'thuun (21) + Heat Mote (11)", Element = "Eldritch" } },
// Batch 3 combo cards (1051-1079)
{ 1051, new ComboCardInfo { Id = 1051, Name = "Thermal Fruit", Recipe = "Fruiting Body (4) + Heat Mote (11)", Element = "Fungus" } },
{ 1052, new ComboCardInfo { Id = 1052, Name = "Frozen Fruit", Recipe = "Fruiting Body (4) + Cold Mote (12)", Element = "Fungus" } },
{ 1053, new ComboCardInfo { Id = 1053, Name = "Toxic Fruit", Recipe = "Fruiting Body (4) + Refuse Slinger (6)", Element = "Fungus" } },
{ 1054, new ComboCardInfo { Id = 1054, Name = "Royal Heat", Recipe = "Brood Monarch (8) + Heat Mote (11)", Element = "Toxin" } },
{ 1055, new ComboCardInfo { Id = 1055, Name = "Royal Cold", Recipe = "Brood Monarch (8) + Cold Mote (12)", Element = "Toxin" } },
{ 1056, new ComboCardInfo { Id = 1056, Name = "Spore Stump", Recipe = "Undead Stump (9) + Mycelium Spore (5)", Element = "Fungus" } },
{ 1057, new ComboCardInfo { Id = 1057, Name = "Eldritch Duo", Recipe = "Questing Tendril (15) + Occult Dabbler (16)", Element = "Eldritch" } },
{ 1058, new ComboCardInfo { Id = 1058, Name = "Star Oracle", Recipe = "Occult Dabbler (16) + Greater Star Spawn (20)", Element = "Eldritch" } },
{ 1059, new ComboCardInfo { Id = 1059, Name = "Void Spawn", Recipe = "Dwarf Star Spawn (17) + Malformed Summon (18)", Element = "Eldritch" } },
{ 1061, new ComboCardInfo { Id = 1061, Name = "Cold Meat", Recipe = "Anthropomorphized Meat (13) + Cold Mote (12)", Element = "Flesh" } },
{ 1062, new ComboCardInfo { Id = 1062, Name = "Rotting Meat", Recipe = "Anthropomorphized Meat (13) + Refuse Slinger (6)", Element = "Flesh" } },
{ 1063, new ComboCardInfo { Id = 1063, Name = "Flesh Patrol", Recipe = "Anthropomorphized Meat (13) + Pupae Patroller (7)", Element = "Flesh" } },
{ 1065, new ComboCardInfo { Id = 1065, Name = "Jester Tendril", Recipe = "Jubilee Jester (14) + Questing Tendril (15)", Element = "Eldritch" } },
{ 1066, new ComboCardInfo { Id = 1066, Name = "Jester Spore", Recipe = "Jubilee Jester (14) + Mycelium Spore (5)", Element = "Fungus" } },
{ 1067, new ComboCardInfo { Id = 1067, Name = "Giant Jester", Recipe = "Jubilee Jester (14) + Geiger Giant (3)", Element = "Food" } },
{ 1068, new ComboCardInfo { Id = 1068, Name = "Toxic Heap", Recipe = "Refuse Slinger (6) + Fruiting Body (4)", Element = "Toxin" } },
{ 1069, new ComboCardInfo { Id = 1069, Name = "Toxic Corpse", Recipe = "Refuse Slinger (6) + Undead Stump (9)", Element = "Toxin" } },
{ 1070, new ComboCardInfo { Id = 1070, Name = "Armored Insect", Recipe = "Pupae Patroller (7) + Geiger Giant (3)", Element = "Toxin" } },
{ 1072, new ComboCardInfo { Id = 1072, Name = "Fungal Guardian", Recipe = "Pupae Patroller (7) + Undead Stump (9)", Element = "Fungus" } },
{ 1074, new ComboCardInfo { Id = 1074, Name = "Warmongers", Recipe = "Pupae Patroller (7) + Heat Mote (11)", Element = "Toxin" } },
{ 1075, new ComboCardInfo { Id = 1075, Name = "Undead Giant", Recipe = "Undead Stump (9) + Geiger Giant (3)", Element = "Fungus" } },
{ 1076, new ComboCardInfo { Id = 1076, Name = "Heat Stump", Recipe = "Undead Stump (9) + Heat Mote (11)", Element = "Fungus" } },
{ 1077, new ComboCardInfo { Id = 1077, Name = "Void Jester", Recipe = "Jubilee Jester (14) + Malformed Summon (18)", Element = "Eldritch" } },
{ 1078, new ComboCardInfo { Id = 1078, Name = "Abomination", Recipe = "Amalgamation (10) + Pupae Patroller (7)", Element = "Flesh" } },
{ 1079, new ComboCardInfo { Id = 1079, Name = "Elder Spawn", Recipe = "Greater Star Spawn (20) + Herald of Jaaaird'thuun (21)", Element = "Eldritch" } },
// Batch 4 combo cards (1014-1050)
{ 1014, new ComboCardInfo { Id = 1014, Name = "Radioactive Predator", Recipe = "Irradiated Blob (1) + Refuse Slinger (6)", Element = "Radioactivity" } },
{ 1015, new ComboCardInfo { Id = 1015, Name = "Toxic Mote", Recipe = "Heat Mote (11) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1016, new ComboCardInfo { Id = 1016, Name = "Frost Amalgamation", Recipe = "Amalgamation (10) + Cold Mote (12)", Element = "Flesh" } },
{ 1017, new ComboCardInfo { Id = 1017, Name = "Fire Toxin", Recipe = "Confused Predator (2) + Heat Mote (11)", Element = "Toxin" } },
{ 1018, new ComboCardInfo { Id = 1018, Name = "Eldritch Flesh", Recipe = "Amalgamation (10) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1020, new ComboCardInfo { Id = 1020, Name = "Fungal Predator", Recipe = "Mycelium Spore (5) + Refuse Slinger (6)", Element = "Fungus" } },
{ 1023, new ComboCardInfo { Id = 1023, Name = "Radiant Flesh", Recipe = "Anthropomorphized Meat (13) + Heat Mote (11)", Element = "Flesh" } },
{ 1024, new ComboCardInfo { Id = 1024, Name = "Frozen Jester", Recipe = "Jubilee Jester (14) + Cold Mote (12)", Element = "Food" } },
{ 1025, new ComboCardInfo { Id = 1025, Name = "Spore Tendril", Recipe = "Questing Tendril (15) + Mycelium Spore (5)", Element = "Eldritch" } },
{ 1026, new ComboCardInfo { Id = 1026, Name = "Star Geiger", Recipe = "Geiger Giant (3) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1027, new ComboCardInfo { Id = 1027, Name = "Toxic Dabbler", Recipe = "Occult Dabbler (16) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1030, new ComboCardInfo { Id = 1030, Name = "Cold Star Spawn", Recipe = "Cold Mote (12) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1031, new ComboCardInfo { Id = 1031, Name = "Heat Star Spawn", Recipe = "Heat Mote (11) + Malformed Summon (18)", Element = "Eldritch" } },
{ 1035, new ComboCardInfo { Id = 1035, Name = "Biohazard", Recipe = "Refuse Slinger (6) + Pupae Patroller (7)", Element = "Toxin" } },
{ 1036, new ComboCardInfo { Id = 1036, Name = "Undead Hive", Recipe = "Undead Stump (9) + Brood Monarch (8)", Element = "Fungus" } },
{ 1037, new ComboCardInfo { Id = 1037, Name = "Giant Flesh", Recipe = "Amalgamation (10) + Geiger Giant (3)", Element = "Flesh" } },
{ 1042, new ComboCardInfo { Id = 1042, Name = "Spore Giant", Recipe = "Mycelium Spore (5) + Brood Monarch (8)", Element = "Fungus" } },
{ 1043, new ComboCardInfo { Id = 1043, Name = "Fire Blob", Recipe = "Irradiated Blob (1) + Heat Mote (11)", Element = "Radioactivity" } },
{ 1045, new ComboCardInfo { Id = 1045, Name = "Frozen Stump", Recipe = "Undead Stump (9) + Cold Mote (12)", Element = "Fungus" } },
{ 1046, new ComboCardInfo { Id = 1046, Name = "Star Brood", Recipe = "Brood Monarch (8) + Dwarf Star Spawn (17)", Element = "Eldritch" } },
{ 1047, new ComboCardInfo { Id = 1047, Name = "Toxic Mote", Recipe = "Cold Mote (12) + Refuse Slinger (6)", Element = "Toxin" } },
{ 1048, new ComboCardInfo { Id = 1048, Name = "Necrotic Giant", Recipe = "Geiger Giant (3) + Undead Stump (9)", Element = "Fungus" } },
{ 1049, new ComboCardInfo { Id = 1049, Name = "Flesh Tendril", Recipe = "Questing Tendril (15) + Anthropomorphized Meat (13)", Element = "Flesh" } },
{ 1050, new ComboCardInfo { Id = 1050, Name = "Void Consumer", Recipe = "Eater of Hope (19) + Malformed Summon (18)", Element = "Eldritch" } }
};
/// <summary>
/// Get a base card by ID
/// </summary>
public static CardInfo? GetBaseCard(int id)
{
return BaseCards.TryGetValue(id, out var card) ? card : null;
}
/// <summary>
/// Get a combo card by ID
/// </summary>
public static ComboCardInfo? GetComboCard(int id)
{
return ComboCards.TryGetValue(id, out var card) ? card : null;
}
/// <summary>
/// Get any card (base or combo) by ID
/// </summary>
public static object? GetCard(int id)
{
if (id < 1000)
return GetBaseCard(id);
return GetComboCard(id);
}
/// <summary>
/// Check if an ID belongs to a base card
/// </summary>
public static bool IsBaseCard(int id) => id > 0 && id < 1000;
/// <summary>
/// Check if an ID belongs to a combo card
/// </summary>
public static bool IsComboCard(int id) => id >= 1000 && id < 2000;
}
/// <summary>
/// Information about a base/combinable card
/// </summary>
public class CardInfo
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Element { get; set; } = "";
public string Type { get; set; } = "";
public int ManaCost { get; set; }
public int Health { get; set; }
public int Power { get; set; }
public string Rarity { get; set; } = "";
public bool IsCombinable { get; set; } = true;
public bool IsQuestCard { get; set; } = false;
}
/// <summary>
/// Information about a combo/created card
/// </summary>
public class ComboCardInfo
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Recipe { get; set; } = "";
public string Element { get; set; } = "";
public string Type { get; set; } = "Creature";
}
+31 -29
View File
@@ -64,8 +64,7 @@ public static class CardFactory
EffectValue = 2,
RequiresTarget = false
}
});
irradiatedBlob.IsCombinable = true;
}, 1);
deck.Add(irradiatedBlob);
// Confused Predator - CREATURE, MC:1, H:3, P:3, COMMON
@@ -82,7 +81,7 @@ public static class CardFactory
EffectValue = 1,
IsPassive = true
}
}));
}, 2)); // TemplateId: 2
// Geiger Giant - CREATURE, MC:5, H:10, P:4, RARE
deck.Add(CreateCreature("Geiger Giant", "You're not quite positive who Geiger is, but he sure is tall.\n[ABILITY: Slam - 4 MC, 6 DTA]",
@@ -98,7 +97,7 @@ public static class CardFactory
EffectValue = 6,
RequiresTarget = false
}
}));
}, 3));
// Mutually Assured Duplication - SPELL, MC:5, T:SE&PC, RARE
deck.Add(CreateSpell("Mutually Assured Duplication", "Wait, this is supposed to deter an attack??\n[ABILITY: Budding - Choose one enemy and friendly creature; duplicate these creatures to the closest unoccupied slot]",
@@ -158,7 +157,7 @@ public static class CardFactory
IsTemporary = true,
Duration = 2
}
}));
}, 10));
// Disembody - SPELL, MC:3, T:SEC, COMMON
deck.Add(CreateSpell("Disembody", "Grummaz went from screaming and struggling to quiet and calm in about 2 seconds.\n[ABILITY: Astral Eviction - Creature may no longer attack, or use abilities]",
@@ -240,12 +239,12 @@ public static class CardFactory
IsTemporary = true,
Duration = 3
}
}));
}, 6));
// Pupae Patroller - CREATURE, MC:3, H:2, P:3, UNCOMMON
// SKIP - requires larval creature card/tag (need to add larval creatures first)
deck.Add(CreateCreature("Pupae Patroller", "You're not sure how this guy fits in the insect heirarchy, but he sure seems happy. (this is a literal human being)\n[ABILITY: Prowl - 1 MC, +1P&+1H for a random larval creature]",
ElementType.Toxin, 3, 2, 3, 2));
ElementType.Toxin, 3, 2, 3, 2, false, null, 7));
// Brood Monarch - CREATURE, MC:6, H:4, P:3, RARE
deck.Add(CreateCreature("Brood Monarch", "Without a doubt, the biggest and grossest bug you've ever seen.\n[ABILITY: Larval Gestation - 4 MC, starts 3 turn timer, spawns 2 3H3P workers]",
@@ -263,7 +262,7 @@ public static class CardFactory
IsTemporary = true,
Duration = 3
}
}));
}, 8));
// Suspicious Mound - ARTIFACT, MC:4, UNCOMMON
deck.Add(CreateArtifact("Suspicious Mound", "People have learned to give these a wide berth, and not just because of the smell.\n[ABILITY: Overrun - 1MC, spawns 1 2H1P larval naiad (max 2 per turn)]",
@@ -303,7 +302,7 @@ public static class CardFactory
EffectValue = 1,
RequiresTarget = false
}
}));
}, 5));
// Fruiting Body - CREATURE, MC:4, H:3, P:4, COMMON
deck.Add(CreateCreature("Fruiting Body", "Some poor animal ate one too many mushrooms, and ended up becoming a mushroom.\n[ABILITY: Troop Thickening - 2 MCPT, +1H&+1P PT to all spores]",
@@ -319,7 +318,7 @@ public static class CardFactory
EffectValue = 1,
IsPassive = true
}
}));
}, 4));
// Undead Stump - CREATURE, MC:5, H:4, P:4, UNCOMMON
deck.Add(CreateCreature("Undead Stump", "The tree that once made up this stump is now dead, but to say the stump is lifeless is entirely inaccurate.\n[ABILITY: Germinate - 2MC, spawn 1 spore in the closest unoccupied creature slot]",
@@ -335,7 +334,7 @@ public static class CardFactory
EffectValue = 1,
RequiresTarget = false
}
}));
}, 9));
// Creeping Infection - SPELL, MC:3, T:SC, UNCOMMON
deck.Add(CreateSpell("Creeping Infection", "It enters through the skin and only gets worse with time.\n[ABILITY: It Begins - 2 DTT for 4 turns, DTT increases by 1 for each turn after the first]",
@@ -393,8 +392,7 @@ public static class CardFactory
EffectValue = 0,
IsPassive = true
}
});
heatMote.IsCombinable = true;
}, 11);
deck.Add(heatMote);
// Cold Mote - CREATURE, MC:1, H:2, P:1, COMMON
@@ -411,8 +409,7 @@ public static class CardFactory
EffectValue = 0,
IsPassive = true
}
});
coldMote.IsCombinable = true;
}, 12);
deck.Add(coldMote);
// Thermal Shock - SPELL, MC:3, T:SC, COMMON
@@ -489,8 +486,7 @@ public static class CardFactory
EffectValue = 0,
RequiresTarget = false
}
});
anthropomorphizedMeat.IsCombinable = true;
}, 13);
deck.Add(anthropomorphizedMeat);
// Jubilee Jester - CREATURE, MC:5, H:6, P:6, RARE
@@ -507,7 +503,7 @@ public static class CardFactory
EffectValue = 4,
RequiresTarget = false
}
}));
}, 14));
// Last Morsel - SPELL, MC:2, T:SC, UNCOMMON
deck.Add(CreateSpell("Last Morsel", "Yum, crunchy!\n[ABILITY: Are you going to finish that? - 3 DTT] - If this spell kills a creature, regain 1.5 times that many health points (rounded down)",
@@ -592,7 +588,7 @@ public static class CardFactory
EffectValue = 1,
RequiresTarget = false
}
}));
}, 15));
// Occult Dabbler - CREATURE, MC:2, H:2, P:2, COMMON
deck.Add(CreateCreature("Occult Dabbler", "\"You want me to do WHAT?\"\n[ABILITY: Fiddle With - 1 MC, double quest/search tokens generated this turn]",
@@ -608,7 +604,7 @@ public static class CardFactory
EffectValue = 2, // Double tokens
RequiresTarget = false
}
}));
}, 16));
// Call of the Deep - EVENT, MC:5, RARE, D:3T after effect
deck.Add(CreateEvent("Call of the Deep", "They all laughed, until the ritual worked.\n[ABILITY: Elaborate Rite - 5 MC, +2 MRPT for 2 turns] - Summons SOMETHING on the third turn",
@@ -652,7 +648,7 @@ public static class CardFactory
EffectValue = 4,
RequiresTarget = true
}
}));
}, 17));
// Malformed Summon - CREATURE, MC:3, C:25%, H:2, P:3, COMMON
tableCards.Add(CreateCreature("Malformed Summon", "Whatever this was before, the journey was clearly not kind.\n[ABILITY: Wheeze - 2MC, -2P DBTT]",
@@ -668,7 +664,7 @@ public static class CardFactory
EffectValue = 2,
RequiresTarget = true
}
}));
}, 18));
// Eater of Hope - CREATURE, MC:5, C:20%, H:3, P:8, UNCOMMON
tableCards.Add(CreateCreature("Eater of Hope", "Geez, do they not feed you or something?\n[ABILITY: Otherworldly Hunger - 4MC, Consume]",
@@ -684,7 +680,7 @@ public static class CardFactory
EffectValue = 0,
RequiresTarget = true
}
}));
}, 19));
// Greater Star Spawn - CREATURE, MC:5, C:17%, H:3, P:4, UNCOMMON
tableCards.Add(CreateCreature("Greater Star Spawn", "After growing large in an ear, they crawl forth to aid in bringing about their Lord.\n[ABILITY: Thrash - 3MC, 10DTT]",
@@ -700,7 +696,7 @@ public static class CardFactory
EffectValue = 10,
RequiresTarget = true
}
}));
}, 20));
// Herald of Jaa'aird'thuun - CREATURE, MC:4, C:10%, H:4, P:4, RARE
tableCards.Add(CreateCreature("Herald of Jaa'aird'thuun", "\"Do you have a moment to talk about our Lord and destroyer?\"\n[ABILITY: Proselytize - 3MC, add 2 Infection token/counters]",
@@ -716,7 +712,7 @@ public static class CardFactory
EffectValue = 2,
RequiresTarget = true
}
}));
}, 21));
// Jaa'aird'thuun - CREATURE, MC:7, C:2%, H:8, P:8, LEGENDARY
tableCards.Add(CreateCreature("Jaa'aird'thuun", "He's real!!! And really... hungry?\n[ABILITY: Gobble - 6 MC, 10 DTAEC]",
@@ -732,7 +728,7 @@ public static class CardFactory
EffectValue = 10,
RequiresTarget = false
}
}));
}, 22));
return tableCards;
}
@@ -743,7 +739,8 @@ public static class CardFactory
var deck = new List<Card>();
// Slightly Glowing Sponge Cakes - CREATURE, MC:3, RADxFOO, H:7, P:1, COMMON
// Combo of Anthropomorphized Meat + Irradiated Blob
// Combo of Anthropomorphized Meat(13) + Irradiated Blob(1)
// Combo TemplateId: 10000 + (1 * 1000) + 13 = 11013
// NOTE: This is a combo-only card - cannot be added to decks
var spongeCakes = CreateCreature("Slightly Glowing Sponge Cakes", "See? I knew the only things that would be left were sponge cakes and giant bugs. (combo anthropomorphized meat + irradiated blob)\n[ABILITY: Temptation - 1 MC, BTS +4P, DBTS -3H]",
ElementType.Radioactivity, 3, 7, 1, 1, false,
@@ -761,10 +758,12 @@ public static class CardFactory
});
spongeCakes.IsCombinable = true;
spongeCakes.IsComboOnly = true;
spongeCakes.TemplateId = 11013; // 10000 + (1*1000) + 13
deck.Add(spongeCakes);
// Tempature Mote - CREATURE, MC:3, THExTHE, H:2, P:2, COMMON
// Combo of Heat Mote + Cold Mote
// Combo of Heat Mote(11) + Cold Mote(12)
// Combo TemplateId: 10000 + (11 * 1000) + 12 = 12012
// NOTE: This is a combo-only card - cannot be added to decks
var tempMote = CreateCreature("Tempature Mote", "A cold snap just waiting to happen.\n[ABILITY: Tempature Gradient(both) - passive, doubles +P&+H buff values and durations]",
ElementType.Thermodynamics, 3, 2, 2, 1, false,
@@ -782,6 +781,7 @@ public static class CardFactory
});
tempMote.IsCombinable = true;
tempMote.IsComboOnly = true;
tempMote.TemplateId = 12012; // 10000 + (11*1000) + 12
deck.Add(tempMote);
return deck;
@@ -971,7 +971,7 @@ public static class CardFactory
private static Card CreateCreature(string name, string description,
ElementType element, int manaCost, int power, int health, int rarity = 1, bool isLegendary = false,
List<CardAbility>? abilities = null)
List<CardAbility>? abilities = null, int templateId = 0)
{
return new CreatureCard
{
@@ -983,6 +983,8 @@ public static class CardFactory
Health = health,
Rarity = rarity,
IsLegendary = isLegendary,
TemplateId = templateId,
IsCombinable = templateId > 0 && templateId < 1000,
Abilities = abilities ?? new List<CardAbility>(),
Effects = new List<CardEffect>
{
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("MagicalDeckbuilder.Shared")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f0b96ad03f0b23be73e6de74767c208f6b7756aa")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f7db1d0d598c5b8630c3e11ba0ed0912e32447d1")]
[assembly: System.Reflection.AssemblyProductAttribute("MagicalDeckbuilder.Shared")]
[assembly: System.Reflection.AssemblyTitleAttribute("MagicalDeckbuilder.Shared")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
@@ -1 +1 @@
1ddaddc2fae321d4d74da424f0e1537cf6791a6fd96dea468f06d21cc464409f
6b78924099cb68fe483ef4bcddc5b5d283aa74fb2c2589d4081e19fb7961b6a7
@@ -1 +1 @@
7094ed9ecb5ee7c30c5a3060335603fdf50f1d365efe6b177e1195a2f408e1d1
b278e861c1a8628ab259f6030168a03520298f979d38e6bbbb76c4018296aeac
@@ -1 +1 @@
{"documents":{"C:\\Users\\atw61\\OneDrive\\Desktop\\new\\*":"https://raw.githubusercontent.com/alexandria121/new/f0b96ad03f0b23be73e6de74767c208f6b7756aa/*"}}
{"documents":{"C:\\Users\\atw61\\OneDrive\\Desktop\\new\\*":"https://raw.githubusercontent.com/alexandria121/new/f7db1d0d598c5b8630c3e11ba0ed0912e32447d1/*"}}
+73
View File
@@ -0,0 +1,73 @@
// ============ NEW COMBO CARDS (6 cards, using valid ID formula) ============
// Combo 1: Irradiated Blob(1) + Heat Mote(10) = 1000 + (1*10) + 10 = 1010
var radioactiveHeat = CreateCreature("Radioactive Heat", "A bubbling mass of radioactive heat.\n[ABILITY: Meltdown - 3 MC, 5 DTT and +2P self-buff]",
ElementType.Radioactivity, 3, 4, 4, 2, false,
new List<CardAbility>
{
new CardAbility { Name = "Meltdown", Description = "5 DTT and +2P self-buff", ManaCost = 3, EffectType = EffectType.Buff, EffectValue = 2, RequiresTarget = false }
});
radioactiveHeat.IsCombinable = true;
radioactiveHeat.IsComboOnly = true;
radioactiveHeat.TemplateId = 1010;
deck.Add(radioactiveHeat);
// Combo 2: Irradiated Blob(1) + Cold Mote(11) = 1000 + (1*10) + 11 = 1011
var frozenBlob = CreateCreature("Frozen Blob", "A frozen irradiated blob.\n[ABILITY: Cold Radiation - 25% freeze attacker]",
ElementType.Radioactivity, 2, 4, 5, 1, false,
new List<CardAbility>
{
new CardAbility { Name = "Cold Radiation", Description = "25% freeze attacker", ManaCost = 0, EffectType = EffectType.Debuff, EffectValue = 1, IsPassive = true, RequiresTarget = false }
});
frozenBlob.IsCombinable = true;
frozenBlob.IsComboOnly = true;
frozenBlob.TemplateId = 1011;
deck.Add(frozenBlob);
// Combo 3: Heat Mote(10) + Cold Mote(11) = 1000 + (10*10) + 11 = 1111 (but 1111 is taken by Temp Mote - use 1012)
var thermalBalance = CreateCreature("Thermal Balance", "Balanced thermal energy.\n[ABILITY: Thermal Equilibrium - +2P and +2H]",
ElementType.Thermodynamics, 2, 4, 3, 1, false,
new List<CardAbility>
{
new CardAbility { Name = "Thermal Equilibrium", Description = "+2P and +2H", ManaCost = 0, EffectType = EffectType.Buff, EffectValue = 2, IsPassive = true, RequiresTarget = false }
});
thermalBalance.IsCombinable = true;
thermalBalance.IsComboOnly = true;
thermalBalance.TemplateId = 1012;
deck.Add(thermalBalance);
// Combo 4: Heat Mote(10) + Heat Mote(10) = 1000 + (10*10) + 10 = 1010 (duplicate - use 1013)
var doubleHeat = CreateCreature("Double Heat", "Double the heat energy.\n[ABILITY: Heat Amplification - 30% double stats]",
ElementType.Thermodynamics, 2, 3, 2, 1, false,
new List<CardAbility>
{
new CardAbility { Name = "Heat Amplification", Description = "30% double stats", ManaCost = 0, EffectType = EffectType.Buff, EffectValue = 2, IsPassive = true, RequiresTarget = false }
});
doubleHeat.IsCombinable = true;
doubleHeat.IsComboOnly = true;
doubleHeat.TemplateId = 1013;
deck.Add(doubleHeat);
// Combo 5: Cold Mote(11) + Cold Mote(11) = 1000 + (11*10) + 11 = 1121 (use 1014)
var doubleCold = CreateCreature("Double Cold", "Double the cold energy.\n[ABILITY: Cold Amplification - 30% double stats]",
ElementType.Thermodynamics, 2, 4, 2, 1, false,
new List<CardAbility>
{
new CardAbility { Name = "Cold Amplification", Description = "30% double stats", ManaCost = 0, EffectType = EffectType.Buff, EffectValue = 2, IsPassive = true, RequiresTarget = false }
});
doubleCold.IsCombinable = true;
doubleCold.IsComboOnly = true;
doubleCold.TemplateId = 1014;
deck.Add(doubleCold);
// Combo 6: Irradiated Blob(1) + Anthropomorphized Meat(12) = 1000 + (1*10) + 12 = 1012? No wait - (1*10)+12 = 22, 1000+22=1022. But 1022 is Sponge Cakes. Use 1015.
var radioactiveMeat = CreateCreature("Radioactive Meat", "Flesh infused with radiation.\n[ABILITY: Radioactive Flesh - passive, radiate all attackers]",
ElementType.Radioactivity, 2, 6, 6, 1, false,
new List<CardAbility>
{
new CardAbility { Name = "Radioactive Flesh", Description = "radiate all attackers", ManaCost = 0, EffectType = EffectType.Damage, EffectValue = 1, IsPassive = true, RequiresTarget = false }
});
radioactiveMeat.IsCombinable = true;
radioactiveMeat.IsComboOnly = true;
radioactiveMeat.TemplateId = 1015;
deck.Add(radioactiveMeat);
+66
View File
@@ -0,0 +1,66 @@
using System.IO;
using System.Text.Json;
namespace NewGame.UI;
/// <summary>
/// Application settings that persist across sessions
/// </summary>
public class AppSettings
{
private static readonly string SettingsFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"MagicalDeckbuilder",
"settings.json");
public int StartingHealth { get; set; } = 30;
private static AppSettings? _instance;
public static AppSettings Instance
{
get
{
if (_instance == null)
{
_instance = Load();
}
return _instance;
}
}
public static AppSettings Load()
{
try
{
if (File.Exists(SettingsFilePath))
{
var json = File.ReadAllText(SettingsFilePath);
return JsonSerializer.Deserialize<AppSettings>(json) ?? new AppSettings();
}
}
catch
{
// If loading fails, return defaults
}
return new AppSettings();
}
public void Save()
{
try
{
var directory = Path.GetDirectoryName(SettingsFilePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var json = JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(SettingsFilePath, json);
}
catch
{
// Silently fail if we can't save
}
}
}
+36 -11
View File
@@ -1,6 +1,7 @@
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Input;
using MagicalDeckbuilder.Cards;
using NewGame.UI.Views;
using MagicalDeckbuilder.Combining;
@@ -176,8 +177,8 @@ public class MainViewModel : ViewModelBase
_hasUnsavedDeckChanges = false;
}
// Navigate to deck builder
CurrentView = "DeckBuilder";
// Navigate to battle select to choose a battle
CurrentView = "BattleSelect";
}
private void LoadDeckIntoBuilder(SavedDeck deck)
@@ -1383,7 +1384,14 @@ public class MainViewModel : ViewModelBase
public bool CanCombine
{
get => _canCombine;
set => SetProperty(ref _canCombine, value);
set
{
if (SetProperty(ref _canCombine, value))
{
// Force WPF to re-evaluate all commands (fixes button not enabling)
CommandManager.InvalidateRequerySuggested();
}
}
}
public bool IsComboLocked
@@ -1654,12 +1662,14 @@ public class MainViewModel : ViewModelBase
OpponentDeck.DrawCards(4);
RefreshOpponentHand();
// Reset game state
PlayerHealth = 30;
PlayerMaxHealth = 30;
// Reset game state with player-configured starting health
var startingHealth = AppSettings.Instance.StartingHealth;
PlayerHealth = startingHealth;
PlayerMaxHealth = startingHealth;
PlayerMana = 2;
PlayerMaxMana = 10;
OpponentHealth = 30;
OpponentHealth = startingHealth;
OpponentMaxHealth = startingHealth;
OpponentMana = 2;
OpponentMaxMana = 10;
TurnCount = 1;
@@ -3005,13 +3015,16 @@ public class MainViewModel : ViewModelBase
if (card1ToRemove != null) PlayerHand.Remove(card1ToRemove);
if (card2ToRemove != null) PlayerHand.Remove(card2ToRemove);
ComboResult = new CardViewModel(newCard);
CustomCards.Add(ComboResult);
IsComboLocked = true;
CanCombine = false;
// Create a ViewModel for the new card and add to hand
var newCardVM = new CardViewModel(newCard);
PlayerHand.Add(newCardVM);
PlayerDeck.Hand.Add(newCard);
StatusMessage = $"Created {newCard.Name}!";
OnPropertyChanged(nameof(PlayerHand));
// Clear the combo slots after successfully combining
ClearCombo();
}
/// <summary>
@@ -3065,6 +3078,18 @@ public class MainViewModel : ViewModelBase
UpdateComboPreview();
}
public void RemoveFromComboSlot(int slot)
{
if (IsComboLocked) return;
if (slot == 1)
ComboCard1 = null;
else if (slot == 2)
ComboCard2 = null;
UpdateComboPreview();
}
private void UpdateComboPreview()
{
if (ComboCard1 == null || ComboCard2 == null)
+3 -2
View File
@@ -35,9 +35,10 @@
Command="{Binding ClearDeckCommand}"
Margin="10,0,0,0"
Padding="15,6"/>
<Button Content="Start Game"
<Button Content="Back to Menu"
Style="{StaticResource PrimaryButtonStyle}"
Command="{Binding InitializeGameCommand}"
Command="{Binding NavigateCommand}"
CommandParameter="Menu"
Margin="10,0,0,0"
Padding="15,6"/>
</StackPanel>
+8
View File
@@ -594,6 +594,10 @@
<TextBlock Text="{Binding ComboCard1.Name, TargetNullValue=---}" Foreground="White" FontSize="7" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center" MaxWidth="40" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<Button Content="X" FontSize="8" FontWeight="Bold" Foreground="White" Background="#CC0000" BorderThickness="0"
Width="14" Height="14" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-4,-4,0" Padding="0"
Cursor="Hand" Click="OnComboSlot1RemoveClick"
Visibility="{Binding ComboCard1, Converter={StaticResource NullToVisibilityConverter}, FallbackValue=Collapsed}"/>
</Grid>
</Border>
@@ -608,6 +612,10 @@
<TextBlock Text="{Binding ComboCard2.Name, TargetNullValue=---}" Foreground="White" FontSize="7" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center" MaxWidth="40" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
<Button Content="X" FontSize="8" FontWeight="Bold" Foreground="White" Background="#CC0000" BorderThickness="0"
Width="14" Height="14" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-4,-4,0" Padding="0"
Cursor="Hand" Click="OnComboSlot2RemoveClick"
Visibility="{Binding ComboCard2, Converter={StaticResource NullToVisibilityConverter}, FallbackValue=Collapsed}"/>
</Grid>
</Border>
</StackPanel>
+12
View File
@@ -243,6 +243,18 @@ public partial class GameView : UserControl
// Combo slots don't support drag out currently
}
private void OnComboSlot1RemoveClick(object sender, System.Windows.RoutedEventArgs e)
{
ViewModel?.RemoveFromComboSlot(1);
e.Handled = true;
}
private void OnComboSlot2RemoveClick(object sender, System.Windows.RoutedEventArgs e)
{
ViewModel?.RemoveFromComboSlot(2);
e.Handled = true;
}
private void OnComboResultDragEnter(object sender, DragEventArgs e)
{
if (sender is Border comboSlot && ViewModel?.IsComboLocked == true)
+20 -6
View File
@@ -18,12 +18,6 @@
<TextBlock Text="Settings"
Style="{StaticResource TitleStyle}"
Margin="0,0,15,30"/>
<TextBlock Text="Functionality coming soon!"
Foreground="{StaticResource TextSecondaryBrush}"
FontSize="14"
FontStyle="Italic"
VerticalAlignment="Center"
Margin="0,0,0,30"/>
</StackPanel>
<!-- Settings Options -->
@@ -183,6 +177,26 @@
Content="Show Tutorial Hints"
IsChecked="True"/>
</Grid>
<Grid Margin="0,15,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="StartingHealthLabel"
Text="Starting Health"
Foreground="{StaticResource TextSecondaryBrush}"
VerticalAlignment="Center"/>
<ComboBox x:Name="StartingHealthCombo"
Grid.Column="1"
Width="100"
HorizontalAlignment="Left"
SelectionChanged="StartingHealthCombo_SelectionChanged">
<ComboBoxItem Content="30" Tag="30" IsSelected="True"/>
<ComboBoxItem Content="50" Tag="50"/>
<ComboBoxItem Content="70" Tag="70"/>
</ComboBox>
</Grid>
</StackPanel>
</Border>
</StackPanel>
+29
View File
@@ -1,4 +1,6 @@
using System.Windows.Controls;
using System.Windows;
using NewGame.UI.ViewModels;
namespace NewGame.UI.Views;
@@ -7,5 +9,32 @@ public partial class SettingsView : UserControl
public SettingsView()
{
InitializeComponent();
LoadSettings();
}
private void LoadSettings()
{
// Set the dropdown to current setting value
var currentHealth = AppSettings.Instance.StartingHealth;
foreach (ComboBoxItem item in StartingHealthCombo.Items)
{
if (item.Tag is string tag && int.TryParse(tag, out int value) && value == currentHealth)
{
item.IsSelected = true;
break;
}
}
}
private void StartingHealthCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (StartingHealthCombo.SelectedItem is ComboBoxItem item && item.Tag is string tag)
{
if (int.TryParse(tag, out int health))
{
AppSettings.Instance.StartingHealth = health;
AppSettings.Instance.Save();
}
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("NewGame.UI")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f0b96ad03f0b23be73e6de74767c208f6b7756aa")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f7db1d0d598c5b8630c3e11ba0ed0912e32447d1")]
[assembly: System.Reflection.AssemblyProductAttribute("NewGame.UI")]
[assembly: System.Reflection.AssemblyTitleAttribute("NewGame.UI")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
@@ -1 +1 @@
4d6a608fd9add8622cd9f8ac759445ab64b698f21e01f56ae0384dba47bd21f5
f1693528d19ae061ce534d293972e238a182f27495fa5392bf1ecd6827d7f255
@@ -1 +1 @@
69bbccbc3428be626512f345ad4f88beec926c5703a9f301574e9070b9f50eb8
29160f55f2fbc01cb3ad50919bd7f21b6f9985a2b447e7658d489f7f9fff7809
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
{"documents":{"C:\\Users\\atw61\\OneDrive\\Desktop\\new\\*":"https://raw.githubusercontent.com/alexandria121/new/f0b96ad03f0b23be73e6de74767c208f6b7756aa/*"}}
{"documents":{"C:\\Users\\atw61\\OneDrive\\Desktop\\new\\*":"https://raw.githubusercontent.com/alexandria121/new/f7db1d0d598c5b8630c3e11ba0ed0912e32447d1/*"}}
@@ -12,7 +12,7 @@ TRACE;DEBUG;NET;NET9_0;NETCOREAPP;WINDOWS;WINDOWS7_0;NET5_0_OR_GREATER;NET6_0_OR
C:\Users\atw61\OneDrive\Desktop\new\NewGame.UI\App.xaml
15-1449266402
23-1598940802
242056503056
202364243512
Controls\CardZoomOverlay.xaml;MainWindow.xaml;Styles\BaseStyles.xaml;Views\BattleSelectView.xaml;Views\BrowserView.xaml;Views\DeckBuilderView.xaml;Views\DeckNameDialog.xaml;Views\DeckSelectionView.xaml;Views\ErrorWindow.xaml;Views\GalleryView.xaml;Views\GameEndWindow.xaml;Views\GameView.xaml;Views\MenuView.xaml;Views\SettingsView.xaml;Views\UnsavedChangesDialog.xaml;
@@ -1,4 +1,4 @@
#pragma checksum "..\..\..\..\Views\DeckBuilderView.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "5973C5A6558CB8EDB97951FFFEF36F8E978EB512"
#pragma checksum "..\..\..\..\Views\DeckBuilderView.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "A7D863CC839B06150B5647659C62E953CFBBEB0E"
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
@@ -73,13 +73,13 @@ namespace NewGame.UI.Views {
{
case 2:
#line 262 "..\..\..\..\Views\DeckBuilderView.xaml"
#line 263 "..\..\..\..\Views\DeckBuilderView.xaml"
((System.Windows.Controls.Border)(target)).Drop += new System.Windows.DragEventHandler(this.Deck_Drop);
#line default
#line hidden
#line 263 "..\..\..\..\Views\DeckBuilderView.xaml"
#line 264 "..\..\..\..\Views\DeckBuilderView.xaml"
((System.Windows.Controls.Border)(target)).DragOver += new System.Windows.DragEventHandler(this.Deck_DragOver);
#line default
@@ -100,13 +100,13 @@ namespace NewGame.UI.Views {
{
case 1:
#line 89 "..\..\..\..\Views\DeckBuilderView.xaml"
#line 90 "..\..\..\..\Views\DeckBuilderView.xaml"
((System.Windows.Controls.Border)(target)).MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(this.Card_MouseLeftButtonDown);
#line default
#line hidden
#line 90 "..\..\..\..\Views\DeckBuilderView.xaml"
#line 91 "..\..\..\..\Views\DeckBuilderView.xaml"
((System.Windows.Controls.Border)(target)).MouseMove += new System.Windows.Input.MouseEventHandler(this.Card_MouseMove);
#line default
@@ -114,7 +114,7 @@ namespace NewGame.UI.Views {
break;
case 3:
#line 295 "..\..\..\..\Views\DeckBuilderView.xaml"
#line 296 "..\..\..\..\Views\DeckBuilderView.xaml"
((System.Windows.Controls.Border)(target)).MouseRightButtonDown += new System.Windows.Input.MouseButtonEventHandler(this.DeckCard_RightClick);
#line default
@@ -1,4 +1,4 @@
#pragma checksum "..\..\..\..\Views\GameView.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "90FE191AABFB04429CC2634EA11E5D741FA94B1B"
#pragma checksum "..\..\..\..\Views\GameView.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "386C2B8B70E223F0F7E6CA5A081D4FA4D44C4368"
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
@@ -51,7 +51,7 @@ namespace NewGame.UI.Views {
#line hidden
#line 601 "..\..\..\..\Views\GameView.xaml"
#line 605 "..\..\..\..\Views\GameView.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.Border ComboSlot2;
@@ -59,7 +59,7 @@ namespace NewGame.UI.Views {
#line hidden
#line 638 "..\..\..\..\Views\GameView.xaml"
#line 646 "..\..\..\..\Views\GameView.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal NewGame.UI.Controls.CardZoomOverlay CardZoomPopup;
@@ -313,53 +313,69 @@ namespace NewGame.UI.Views {
#line hidden
return;
case 17:
this.ComboSlot2 = ((System.Windows.Controls.Border)(target));
#line 602 "..\..\..\..\Views\GameView.xaml"
this.ComboSlot2.Drop += new System.Windows.DragEventHandler(this.OnComboSlot2Drop);
#line default
#line hidden
#line 602 "..\..\..\..\Views\GameView.xaml"
this.ComboSlot2.DragEnter += new System.Windows.DragEventHandler(this.OnComboSlotDragEnter);
#line default
#line hidden
#line 602 "..\..\..\..\Views\GameView.xaml"
this.ComboSlot2.DragLeave += new System.Windows.DragEventHandler(this.OnComboSlotDragLeave);
#line default
#line hidden
#line 602 "..\..\..\..\Views\GameView.xaml"
this.ComboSlot2.MouseMove += new System.Windows.Input.MouseEventHandler(this.OnComboSlotMouseMove);
#line 599 "..\..\..\..\Views\GameView.xaml"
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.OnComboSlot1RemoveClick);
#line default
#line hidden
return;
case 18:
this.ComboSlot2 = ((System.Windows.Controls.Border)(target));
#line 619 "..\..\..\..\Views\GameView.xaml"
((System.Windows.Controls.Border)(target)).Drop += new System.Windows.DragEventHandler(this.OnComboResultDrop);
#line 606 "..\..\..\..\Views\GameView.xaml"
this.ComboSlot2.Drop += new System.Windows.DragEventHandler(this.OnComboSlot2Drop);
#line default
#line hidden
#line 619 "..\..\..\..\Views\GameView.xaml"
((System.Windows.Controls.Border)(target)).DragEnter += new System.Windows.DragEventHandler(this.OnComboResultDragEnter);
#line 606 "..\..\..\..\Views\GameView.xaml"
this.ComboSlot2.DragEnter += new System.Windows.DragEventHandler(this.OnComboSlotDragEnter);
#line default
#line hidden
#line 619 "..\..\..\..\Views\GameView.xaml"
((System.Windows.Controls.Border)(target)).DragLeave += new System.Windows.DragEventHandler(this.OnComboResultDragLeave);
#line 606 "..\..\..\..\Views\GameView.xaml"
this.ComboSlot2.DragLeave += new System.Windows.DragEventHandler(this.OnComboSlotDragLeave);
#line default
#line hidden
#line 606 "..\..\..\..\Views\GameView.xaml"
this.ComboSlot2.MouseMove += new System.Windows.Input.MouseEventHandler(this.OnComboSlotMouseMove);
#line default
#line hidden
return;
case 19:
#line 617 "..\..\..\..\Views\GameView.xaml"
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.OnComboSlot2RemoveClick);
#line default
#line hidden
return;
case 20:
#line 627 "..\..\..\..\Views\GameView.xaml"
((System.Windows.Controls.Border)(target)).Drop += new System.Windows.DragEventHandler(this.OnComboResultDrop);
#line default
#line hidden
#line 627 "..\..\..\..\Views\GameView.xaml"
((System.Windows.Controls.Border)(target)).DragEnter += new System.Windows.DragEventHandler(this.OnComboResultDragEnter);
#line default
#line hidden
#line 627 "..\..\..\..\Views\GameView.xaml"
((System.Windows.Controls.Border)(target)).DragLeave += new System.Windows.DragEventHandler(this.OnComboResultDragLeave);
#line default
#line hidden
return;
case 21:
this.CardZoomPopup = ((NewGame.UI.Controls.CardZoomOverlay)(target));
return;
}
Binary file not shown.
+17
View File
@@ -0,0 +1,17 @@
$lines = Get-Content 'c:\Users\atw61\OneDrive\Desktop\new\MagicalDeckbuilder.Shared\Game\CardFactory.cs'
$insertLines = Get-Content 'c:\Users\atw61\OneDrive\Desktop\new\NewComboCards.cs'
$output = @()
$found = $false
foreach ($line in $lines) {
if ($line -eq ' return deck;' -and -not $found) {
foreach ($il in $insertLines) {
$output += $il
}
$found = $true
}
$output += $line
}
Set-Content 'c:\Users\atw61\OneDrive\Desktop\new\MagicalDeckbuilder.Shared\Game\CardFactory.cs' $output
Write-Host "Done! Added 25 combo cards."
+12
View File
@@ -0,0 +1,12 @@
$content = Get-Content 'c:\Users\atw61\OneDrive\Desktop\new\MagicalDeckbuilder.Shared\Game\CardFactory.cs' -Raw
$matches = [regex]::Matches($content, 'ID: (1[0-9]{3})')
$usedIds = @()
foreach ($m in $matches) {
$usedIds += [int]$m.Groups[1].Value
}
$usedIds = $usedIds | Sort-Object -Unique
$allIds = 1000..1300
$availableIds = $allIds | Where-Object { $_ -notin $usedIds }
Write-Host "Available count: $($availableIds.Count)"
Write-Host "Available (first 35): $($availableIds[0..34] -join ', ')"