diff --git a/MagicalDeckbuilder.Shared/CardDatabase.json b/MagicalDeckbuilder.Shared/CardDatabase.json new file mode 100644 index 0000000..d2f0b8b --- /dev/null +++ b/MagicalDeckbuilder.Shared/CardDatabase.json @@ -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" + ] +} \ No newline at end of file diff --git a/MagicalDeckbuilder.Shared/Cards/Card.cs b/MagicalDeckbuilder.Shared/Cards/Card.cs index a76a5c2..789a2f1 100644 --- a/MagicalDeckbuilder.Shared/Cards/Card.cs +++ b/MagicalDeckbuilder.Shared/Cards/Card.cs @@ -129,6 +129,12 @@ public enum WeaponTargetType public class Card { public string Id { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Template ID for lookups: 1-200 for base cards, 1000-2000 for combo cards + /// + 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 /// @@ -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, diff --git a/MagicalDeckbuilder.Shared/Combining/CardRecipeRegistry.cs b/MagicalDeckbuilder.Shared/Combining/CardRecipeRegistry.cs new file mode 100644 index 0000000..6e9d785 --- /dev/null +++ b/MagicalDeckbuilder.Shared/Combining/CardRecipeRegistry.cs @@ -0,0 +1,185 @@ +using Card = MagicalDeckbuilder.Cards.Card; +using MagicalDeckbuilder.Logging; + +namespace MagicalDeckbuilder.Combining; + +/// +/// Central registry for card combination recipes. +/// Defines all valid combinations between base cards. +/// Formula: combo_template_id = 10000 + (min_id * 1000) + max_id +/// +public class CardRecipeRegistry +{ + private static CardRecipeRegistry? _instance; + private static readonly object _lock = new(); + + /// + /// Singleton instance + /// + public static CardRecipeRegistry Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + _instance ??= new CardRecipeRegistry(); + } + } + return _instance; + } + } + + /// + /// Maps combo template ID to the source card template IDs + /// Key: comboTemplateId (int), Value: Tuple of (card1TemplateId, card2TemplateId) + /// + private readonly Dictionary _recipes = new(); + + /// + /// Reverse lookup: maps source card pair to combo template ID + /// + private readonly Dictionary<(int, int), int> _reverseLookup = new(); + + private CardRecipeRegistry() + { + InitializeDefaultRecipes(); + } + + /// + /// Calculate the combo template ID for two card template IDs + /// Formula: 10000 + (minId * 1000) + maxId + /// + 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; + } + + /// + /// Calculate the combo template ID for two cards + /// + public static int CalculateComboTemplateId(Card card1, Card card2) + { + return CalculateComboTemplateId(card1.TemplateId, card2.TemplateId); + } + + /// + /// Check if a combo recipe exists for two cards + /// + public bool HasRecipe(int templateId1, int templateId2) + { + var key = (Math.Min(templateId1, templateId2), Math.Max(templateId1, templateId2)); + return _reverseLookup.ContainsKey(key); + } + + /// + /// Check if a combo recipe exists for two cards + /// + public bool HasRecipe(Card card1, Card card2) + { + return HasRecipe(card1.TemplateId, card2.TemplateId); + } + + /// + /// Get the combo template ID for two cards, if a recipe exists + /// + 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; + } + + /// + /// Get the source card IDs for a combo template ID + /// + public (int Card1Id, int Card2Id)? GetSourceCards(int comboTemplateId) + { + return _recipes.TryGetValue(comboTemplateId, out var recipe) ? recipe : null; + } + + /// + /// Get all registered combo template IDs + /// + public IEnumerable GetAllComboTemplateIds() + { + return _recipes.Keys; + } + + /// + /// Register a new combination recipe + /// + 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}"); + } + + /// + /// Validate that a combo card's template ID matches its recipe + /// + public bool ValidateComboCard(int card1TemplateId, int card2TemplateId, int comboTemplateId) + { + var expectedComboId = CalculateComboTemplateId(card1TemplateId, card2TemplateId); + return expectedComboId == comboTemplateId; + } + + /// + /// Initialize default combination recipes + /// Only creature and blank cards can be combined. + /// Formula: comboId = 10000 + (minId * 1000) + maxId + /// + 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"); + } + + /// + /// Debug: Print all registered recipes + /// + 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}"); + } + } +} \ No newline at end of file diff --git a/MagicalDeckbuilder.Shared/Combining/CombinationLookup.cs b/MagicalDeckbuilder.Shared/Combining/CombinationLookup.cs index 0fa9cb7..1ffc8d6 100644 --- a/MagicalDeckbuilder.Shared/Combining/CombinationLookup.cs +++ b/MagicalDeckbuilder.Shared/Combining/CombinationLookup.cs @@ -3,29 +3,32 @@ using Card = MagicalDeckbuilder.Cards.Card; namespace MagicalDeckbuilder.Combining; /// -/// 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 /// public class CombinationLookup { - private readonly Dictionary _combinationCards = new(); - + private readonly Dictionary _comboCardsByTemplateId = new(); + /// /// Initialize with a collection of pre-made combination cards /// public void LoadCombinationCards(IEnumerable 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 /// 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); + } + + /// + /// Fallback: find combination by matching card names in combo card names + /// + 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; } - /// - /// Sanitize card name for use in lookup key - /// - private static string SanitizeName(string name) - { - // Remove spaces and special characters, keep only alphanumeric - return new string(name.Where(char.IsLetterOrDigit).ToArray()).ToLowerInvariant(); - } - /// /// Check if two cards have a pre-made combination available /// diff --git a/MagicalDeckbuilder.Shared/Game/CardDatabase.cs b/MagicalDeckbuilder.Shared/Game/CardDatabase.cs new file mode 100644 index 0000000..35103c1 --- /dev/null +++ b/MagicalDeckbuilder.Shared/Game/CardDatabase.cs @@ -0,0 +1,234 @@ +using System.Collections.Generic; + +namespace MagicalDeckbuilder.Game; + +/// +/// Static database of all cards in the game with their Template IDs. +/// IDs 1-200: Base/Combinable cards +/// IDs 1000-2000: Combo/Created cards +/// +public static class CardDatabase +{ + /// + /// All base cards that can be combined (IDs 1-22) + /// + public static readonly Dictionary 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 } } + }; + + /// + /// All combo cards created by combining base cards (IDs 1000+) + /// + public static readonly Dictionary 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" } } + }; + + /// + /// Get a base card by ID + /// + public static CardInfo? GetBaseCard(int id) + { + return BaseCards.TryGetValue(id, out var card) ? card : null; + } + + /// + /// Get a combo card by ID + /// + public static ComboCardInfo? GetComboCard(int id) + { + return ComboCards.TryGetValue(id, out var card) ? card : null; + } + + /// + /// Get any card (base or combo) by ID + /// + public static object? GetCard(int id) + { + if (id < 1000) + return GetBaseCard(id); + return GetComboCard(id); + } + + /// + /// Check if an ID belongs to a base card + /// + public static bool IsBaseCard(int id) => id > 0 && id < 1000; + + /// + /// Check if an ID belongs to a combo card + /// + public static bool IsComboCard(int id) => id >= 1000 && id < 2000; +} + +/// +/// Information about a base/combinable card +/// +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; +} + +/// +/// Information about a combo/created card +/// +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"; +} \ No newline at end of file diff --git a/MagicalDeckbuilder.Shared/Game/CardFactory.cs b/MagicalDeckbuilder.Shared/Game/CardFactory.cs index 8f17f60..1ed791c 100644 --- a/MagicalDeckbuilder.Shared/Game/CardFactory.cs +++ b/MagicalDeckbuilder.Shared/Game/CardFactory.cs @@ -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(); // 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? abilities = null) + List? 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(), Effects = new List { diff --git a/MagicalDeckbuilder.Shared/bin/Debug/net9.0-windows/MagicalDeckbuilder.Shared.dll b/MagicalDeckbuilder.Shared/bin/Debug/net9.0-windows/MagicalDeckbuilder.Shared.dll index 9bc5674..646d01f 100644 Binary files a/MagicalDeckbuilder.Shared/bin/Debug/net9.0-windows/MagicalDeckbuilder.Shared.dll and b/MagicalDeckbuilder.Shared/bin/Debug/net9.0-windows/MagicalDeckbuilder.Shared.dll differ diff --git a/MagicalDeckbuilder.Shared/bin/Debug/net9.0-windows/MagicalDeckbuilder.Shared.pdb b/MagicalDeckbuilder.Shared/bin/Debug/net9.0-windows/MagicalDeckbuilder.Shared.pdb index 3413794..98946ad 100644 Binary files a/MagicalDeckbuilder.Shared/bin/Debug/net9.0-windows/MagicalDeckbuilder.Shared.pdb and b/MagicalDeckbuilder.Shared/bin/Debug/net9.0-windows/MagicalDeckbuilder.Shared.pdb differ diff --git a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.AssemblyInfo.cs b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.AssemblyInfo.cs index 71053ac..7bc8dfe 100644 --- a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.AssemblyInfo.cs +++ b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.AssemblyInfo.cs @@ -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")] diff --git a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.AssemblyInfoInputs.cache b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.AssemblyInfoInputs.cache index 5367071..6c9ae05 100644 --- a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.AssemblyInfoInputs.cache +++ b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.AssemblyInfoInputs.cache @@ -1 +1 @@ -1ddaddc2fae321d4d74da424f0e1537cf6791a6fd96dea468f06d21cc464409f +6b78924099cb68fe483ef4bcddc5b5d283aa74fb2c2589d4081e19fb7961b6a7 diff --git a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.csproj.CoreCompileInputs.cache b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.csproj.CoreCompileInputs.cache index 02a6068..4771789 100644 --- a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.csproj.CoreCompileInputs.cache +++ b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -7094ed9ecb5ee7c30c5a3060335603fdf50f1d365efe6b177e1195a2f408e1d1 +b278e861c1a8628ab259f6030168a03520298f979d38e6bbbb76c4018296aeac diff --git a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.dll b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.dll index 9bc5674..646d01f 100644 Binary files a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.dll and b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.dll differ diff --git a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.pdb b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.pdb index 3413794..98946ad 100644 Binary files a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.pdb and b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.pdb differ diff --git a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.sourcelink.json b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.sourcelink.json index bbede61..0e32ee1 100644 --- a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.sourcelink.json +++ b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/MagicalDeckbuilder.Shared.sourcelink.json @@ -1 +1 @@ -{"documents":{"C:\\Users\\atw61\\OneDrive\\Desktop\\new\\*":"https://raw.githubusercontent.com/alexandria121/new/f0b96ad03f0b23be73e6de74767c208f6b7756aa/*"}} \ No newline at end of file +{"documents":{"C:\\Users\\atw61\\OneDrive\\Desktop\\new\\*":"https://raw.githubusercontent.com/alexandria121/new/f7db1d0d598c5b8630c3e11ba0ed0912e32447d1/*"}} \ No newline at end of file diff --git a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/ref/MagicalDeckbuilder.Shared.dll b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/ref/MagicalDeckbuilder.Shared.dll index f0511df..2280dce 100644 Binary files a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/ref/MagicalDeckbuilder.Shared.dll and b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/ref/MagicalDeckbuilder.Shared.dll differ diff --git a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/refint/MagicalDeckbuilder.Shared.dll b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/refint/MagicalDeckbuilder.Shared.dll index f0511df..2280dce 100644 Binary files a/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/refint/MagicalDeckbuilder.Shared.dll and b/MagicalDeckbuilder.Shared/obj/Debug/net9.0-windows/refint/MagicalDeckbuilder.Shared.dll differ diff --git a/NewComboCards.cs b/NewComboCards.cs new file mode 100644 index 0000000..77b3e74 --- /dev/null +++ b/NewComboCards.cs @@ -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 + { + 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 + { + 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 + { + 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 + { + 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 + { + 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 + { + 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); \ No newline at end of file diff --git a/NewGame.UI/AppSettings.cs b/NewGame.UI/AppSettings.cs new file mode 100644 index 0000000..0571d61 --- /dev/null +++ b/NewGame.UI/AppSettings.cs @@ -0,0 +1,66 @@ +using System.IO; +using System.Text.Json; + +namespace NewGame.UI; + +/// +/// Application settings that persist across sessions +/// +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(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 + } + } +} \ No newline at end of file diff --git a/NewGame.UI/ViewModels/MainViewModel.cs b/NewGame.UI/ViewModels/MainViewModel.cs index ec7eb18..67ebd75 100644 --- a/NewGame.UI/ViewModels/MainViewModel.cs +++ b/NewGame.UI/ViewModels/MainViewModel.cs @@ -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(); } /// @@ -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) diff --git a/NewGame.UI/Views/DeckBuilderView.xaml b/NewGame.UI/Views/DeckBuilderView.xaml index b2e6f0c..7e208ac 100644 --- a/NewGame.UI/Views/DeckBuilderView.xaml +++ b/NewGame.UI/Views/DeckBuilderView.xaml @@ -35,9 +35,10 @@ Command="{Binding ClearDeckCommand}" Margin="10,0,0,0" Padding="15,6"/> -