public override IEffectDescription Generate() { DrawEffectDescription desc = new DrawEffectDescription(); desc.drawModifier = ProceduralUtils.GetRandomValue <DrawModifier>(random, model); // Find the bounds of card amounts int max = ProceduralUtils.GetUpperBound(desc, ref desc.amount, MIN_CARDS, MAX_CARDS, maxAllocatedBudget); int min = ProceduralUtils.GetLowerBound(desc, ref desc.amount, MIN_CARDS, max, minAllocatedBudget); Assert.IsTrue(max >= min); desc.amount = random.Next(min, max); // Attempt to narrow down the qualifier pool SortedSet <QualifierType> allowableQualifiers = CardEnums.GetValidFlags <QualifierType>(EffectType.DRAW_CARDS); QualifierType qualifier = ProceduralUtils.GetRandomValue(random, model, allowableQualifiers); if (qualifier != QualifierType.NONE) { IProceduralQualifierGenerator qualifierGen = ProceduralUtils.GetProceduralGenerator(qualifier); qualifierGen.SetupParameters(random, model, minAllocatedBudget / desc.PowerLevel(), maxAllocatedBudget / desc.PowerLevel()); desc.cardQualifier = qualifierGen.Generate(); } return(desc); }
public void TestClubs() { // Arrange CardEnums cardColor = CardEnums.Spades; string clubs = "clubs"; // Act CardEnums.TryParse(clubs, true, out cardColor); // Assert Assert.AreEqual(CardEnums.Clubs, cardColor); }
public Alignment GetAlignment() { return(CardEnums.CombineAlignments(targettingType.GetAlignment(), effectType.GetAlignment())); }
static private bool GenerateCardEffect(System.Random random, IHistogram model, CreatureModelIndex creatureModels, CardEffectDescription effectDesc, EffectType effect, double minBudget, double maxBudget, bool positive) { IProceduralEffectGenerator effectGen = ProceduralUtils.GetProceduralGenerator(effect); effectGen.SetupParameters(random, model, creatureModels, minBudget, maxBudget); effectDesc.effectType = effectGen.Generate(); // Adjust budgets minBudget /= effectDesc.effectType.PowerLevel(); maxBudget /= effectDesc.effectType.PowerLevel(); if (minBudget > maxBudget) { double temp = minBudget; minBudget = maxBudget; maxBudget = temp; } // Always allow for default targetting (multiplier 1.0x) if (maxBudget < 1.0) { maxBudget = 1.0; } TargetType targetType = TargetType.CREATURES; SortedSet <TargetType> validTargets = CardEnums.GetValidFlags <TargetType>(effect); SortedSet <TargettingType> allowableTargetting = new SortedSet <TargettingType>(); SortedSet <QualifierType> allowableQualifiers = new SortedSet <QualifierType>(); while (validTargets.Count > 0 && allowableTargetting.Count == 0) { targetType = ProceduralUtils.GetRandomValue(random, model, validTargets); validTargets.Remove(targetType); switch (effectDesc.effectType.GetAlignment()) { case Alignment.POSITIVE: if (positive) { allowableTargetting = ProceduralUtils.GetTargettingByAlignment(Alignment.POSITIVE); allowableQualifiers = ProceduralUtils.GetQualifiersByAlignment(Alignment.NEUTRAL); allowableQualifiers.UnionWith(ProceduralUtils.GetQualifiersByAlignment(Alignment.POSITIVE)); allowableQualifiers.IntersectWith(CardEnums.GetValidFlags <QualifierType>(targetType)); if (allowableQualifiers.Count > 0) { allowableTargetting.UnionWith(ProceduralUtils.GetTargettingByAlignment(Alignment.NEUTRAL)); } } else { allowableTargetting = ProceduralUtils.GetTargettingByAlignment(Alignment.NEGATIVE); allowableQualifiers = ProceduralUtils.GetQualifiersByAlignment(Alignment.NEGATIVE); allowableQualifiers.IntersectWith(CardEnums.GetValidFlags <QualifierType>(targetType)); if (allowableQualifiers.Count > 0) { allowableTargetting.UnionWith(ProceduralUtils.GetTargettingByAlignment(Alignment.NEUTRAL)); } } break; case Alignment.NEGATIVE: if (positive) { allowableTargetting = ProceduralUtils.GetTargettingByAlignment(Alignment.NEGATIVE); allowableQualifiers = ProceduralUtils.GetQualifiersByAlignment(Alignment.NEUTRAL); allowableQualifiers.UnionWith(ProceduralUtils.GetQualifiersByAlignment(Alignment.NEGATIVE)); allowableQualifiers.IntersectWith(CardEnums.GetValidFlags <QualifierType>(targetType)); if (allowableQualifiers.Count > 0) { allowableTargetting.UnionWith(ProceduralUtils.GetTargettingByAlignment(Alignment.NEUTRAL)); } } else { allowableTargetting = ProceduralUtils.GetTargettingByAlignment(Alignment.POSITIVE); allowableQualifiers = ProceduralUtils.GetQualifiersByAlignment(Alignment.POSITIVE); allowableQualifiers.IntersectWith(CardEnums.GetValidFlags <QualifierType>(targetType)); if (allowableQualifiers.Count > 0) { allowableTargetting.UnionWith(ProceduralUtils.GetTargettingByAlignment(Alignment.NEUTRAL)); } } break; default: if (positive) { allowableTargetting = new SortedSet <TargettingType>((TargettingType[])Enum.GetValues(typeof(TargettingType))); allowableQualifiers = new SortedSet <QualifierType>((QualifierType[])Enum.GetValues(typeof(QualifierType))); allowableQualifiers.IntersectWith(CardEnums.GetValidFlags <QualifierType>(targetType)); } else { allowableTargetting = new SortedSet <TargettingType>(); } break; } allowableTargetting.IntersectWith(CardEnums.GetValidFlags <TargettingType>(targetType)); allowableTargetting.IntersectWith(CardEnums.GetValidFlags <TargettingType>(effectDesc.triggerCondition)); // Special case // Up to can never be a downside because you can choose 0 targets if (!positive) { allowableTargetting.Remove(TargettingType.UP_TO_TARGET); allowableTargetting.Remove(TargettingType.UP_TO_TARGET_ALLY); allowableTargetting.Remove(TargettingType.UP_TO_TARGET_ENEMY); } } // Could not find any valid targetting to achieve the desired alignment if (allowableTargetting.Count == 0) { SortedSet <TargetType> targets = CardEnums.GetValidFlags <TargetType>(effect); Debug.Log("Wasn't able to generate targets for effect <" + effect.ToString() + ">"); return(false); } // Attempt to narrow down the targetting pool SortedSet <TargettingType> targettingWithinBudget = new SortedSet <TargettingType>(allowableTargetting.Intersect(ProceduralUtils.GetTargettingWithinBudget(maxBudget))); if (targettingWithinBudget.Count > 0) { allowableTargetting = targettingWithinBudget; } else { Debug.Log("Unable to narrow down targetting types for <" + effect.ToString() + ", " + targetType.ToString() + "> for budget " + maxBudget); } TargettingType targettingType = ProceduralUtils.GetRandomValue(random, model, allowableTargetting); IProceduralTargettingGenerator targettingGen = ProceduralUtils.GetProceduralGenerator(targettingType); targettingGen.SetupParameters(targetType, random, model, minBudget, maxBudget); effectDesc.targettingType = targettingGen.Generate(); // Adjust budgets minBudget /= effectDesc.targettingType.PowerLevel(); maxBudget /= effectDesc.targettingType.PowerLevel(); if (minBudget > maxBudget) { double temp = minBudget; minBudget = maxBudget; maxBudget = temp; } if (effectDesc.targettingType is IQualifiableTargettingDescription qualifiable) { // Generate a possible qualifier // Attempt to narrow down the qualifier pool SortedSet <QualifierType> qualifiersWithinBudget = new SortedSet <QualifierType>(allowableQualifiers.Intersect(ProceduralUtils.GetQualifiersWithinBudget(maxBudget))); if (targettingWithinBudget.Count > 0) { allowableQualifiers = qualifiersWithinBudget; } else { Debug.Log("Unable to narrow down qualifier types for <" + effect.ToString() + ", " + targetType.ToString() + "> for budget " + maxBudget); } QualifierType qualifier = ProceduralUtils.GetRandomValue(random, model, allowableQualifiers); if (qualifier != QualifierType.NONE) { IProceduralQualifierGenerator qualifierGen = ProceduralUtils.GetProceduralGenerator(qualifier); qualifierGen.SetupParameters(random, model, minBudget, maxBudget); qualifiable.qualifier = qualifierGen.Generate(); } } return(true); }
static private void GenerateCardEffects(System.Random random, IHistogram model, CreatureModelIndex creatureModels, CardDescription cardDesc, double powerBudget, double powerMargin, double powerLimit) { // A trap card only has one trigger condition if (cardDesc.cardType == CardType.TRAP) { ProceduralUtils.GetRandomValue(random, model, CardEnums.GetValidFlags <TriggerCondition>(CardType.TRAP)); } int effectCount = 0; List <TriggerCondition> triggerBlacklist = new List <TriggerCondition>(); while ((cardDesc.PowerLevel() + PowerBudget.FLAT_EFFECT_COST) < powerBudget && effectCount < EffectConstants.MAX_CARD_EFFECTS) { // Generate effects CardEffectDescription cardEffect = new CardEffectDescription(); // Create effects with a power level ranging between using max limit of budget, or uniformly distributing the min budget double maxAllowableBudget = powerLimit - cardDesc.PowerLevel() - PowerBudget.FLAT_EFFECT_COST; double minAllowableBudget = (powerBudget - cardDesc.PowerLevel() - PowerBudget.FLAT_EFFECT_COST) / (EffectConstants.MAX_CARD_EFFECTS - effectCount); if (cardDesc.cardType == CardType.SPELL || (cardDesc.cardType == CardType.TRAP && effectCount > 0)) { // After the first condition of a trap or after casting a spell all further effects of // the card should just resolve so trigger cond is NONE cardEffect.triggerCondition = TriggerCondition.NONE; // If NONE has been blacklisted that means that there are no candidates for the remaining budget if (triggerBlacklist.Contains(TriggerCondition.NONE)) { //Debug.Log("No effects are valid for budget " + maxAllowableBudget); break; } } else { if (ProceduralUtils.FlagsExist(triggerBlacklist, CardEnums.GetValidFlags <TriggerCondition>(cardDesc.cardType))) { cardEffect.triggerCondition = ProceduralUtils.GetRandomValueExcluding(random, model, triggerBlacklist, CardEnums.GetValidFlags <TriggerCondition>(cardDesc.cardType)); } else { // No triggers available mean each trigger type has been blacklisted //Debug.Log("No effects are valid for budget " + maxAllowableBudget); break; } } double triggerBudgetModifier = PowerBudget.GetTriggerModifier(cardEffect.triggerCondition, cardDesc); maxAllowableBudget /= triggerBudgetModifier; minAllowableBudget /= triggerBudgetModifier; SortedSet <EffectType> candidatesWithinBudget = ProceduralUtils.GetEffectsWithinBudget(maxAllowableBudget); if (candidatesWithinBudget.Count == 0) { triggerBlacklist.Add(cardEffect.triggerCondition); Debug.Log("No effects are valid for budget " + maxAllowableBudget); continue; } bool validEffect = false; SortedSet <EffectType> effectCandidates = new SortedSet <EffectType>(CardEnums.GetValidFlags <EffectType>(cardEffect.triggerCondition).Intersect(candidatesWithinBudget)); effectCandidates.Remove(EffectType.NONE); while (!validEffect && effectCandidates.Count > 0) { EffectType effectType = ProceduralUtils.GetRandomValueExcluding(random, model, new EffectType[] { EffectType.NONE }, effectCandidates); // This means that there isn't an effect that meets this condition if (effectType == EffectType.NONE) { // Add to black list so we don't get multiple effects that trigger on same condition if (cardEffect.triggerCondition != TriggerCondition.NONE) { triggerBlacklist.Add(cardEffect.triggerCondition); } break; } validEffect = GenerateCardEffect(random, model, creatureModels, cardEffect, effectType, minAllowableBudget, maxAllowableBudget, true); } // This means that there isn't an effect that meets this condition if (validEffect) { cardDesc.cardEffects.Add(cardEffect); effectCount++; } else { Debug.Log("No valid effect could be generated for trigger <" + cardEffect.triggerCondition.ToString() + "> with budget " + maxAllowableBudget); } // Add to black list so we don't get multiple effects that trigger on same condition if (cardEffect.triggerCondition != TriggerCondition.NONE) { triggerBlacklist.Add(cardEffect.triggerCondition); } } // Generate a draw back while (cardDesc.PowerLevel() > powerMargin) { // TODO: Drawback should be one of, additional cost, negative effect, or negative modification to existing effect // For now just add additional negative effect CardEffectDescription cardEffect = new CardEffectDescription(); double maxAllowableBudget = (cardDesc.PowerLevel() - powerBudget - PowerBudget.FLAT_EFFECT_COST) / PowerBudget.DOWNSIDE_WEIGHT; double minAllowableBudget = (cardDesc.PowerLevel() - powerMargin - PowerBudget.FLAT_EFFECT_COST) / PowerBudget.DOWNSIDE_WEIGHT; if (cardDesc.cardType == CardType.SPELL || (cardDesc.cardType == CardType.TRAP && effectCount > 0)) { // After the first condition of a trap or after casting a spell all further effects of // the card should just resolve so trigger cond is NONE cardEffect.triggerCondition = TriggerCondition.NONE; // If NONE has been blacklisted that means that there are no candidates for the remaining budget if (triggerBlacklist.Contains(TriggerCondition.NONE)) { Debug.Log("No effects are valid for budget " + maxAllowableBudget); break; } } else { if (ProceduralUtils.FlagsExist(triggerBlacklist, CardEnums.GetValidFlags <TriggerCondition>(cardDesc.cardType))) { cardEffect.triggerCondition = ProceduralUtils.GetRandomValueExcluding(random, model, triggerBlacklist, CardEnums.GetValidFlags <TriggerCondition>(cardDesc.cardType)); } else { // No triggers available mean each trigger type has been blacklisted Debug.Log("No effects are valid for budget " + maxAllowableBudget); break; } } double triggerBudgetModifier = PowerBudget.GetTriggerModifier(cardEffect.triggerCondition, cardDesc); maxAllowableBudget /= triggerBudgetModifier; minAllowableBudget /= triggerBudgetModifier; SortedSet <EffectType> candidatesWithinBudget = ProceduralUtils.GetEffectsWithinBudget(maxAllowableBudget); if (candidatesWithinBudget.Count == 0) { triggerBlacklist.Add(cardEffect.triggerCondition); Debug.Log("No effects are valid for budget " + maxAllowableBudget); continue; } bool validEffect = false; SortedSet <EffectType> effectCandidates = new SortedSet <EffectType>(CardEnums.GetValidFlags <EffectType>(cardEffect.triggerCondition).Intersect(candidatesWithinBudget)); effectCandidates.Remove(EffectType.NONE); while (!validEffect && effectCandidates.Count > 0) { EffectType effectType = ProceduralUtils.GetRandomValueExcluding(random, model, new EffectType[] { EffectType.NONE }, effectCandidates); // This means that there isn't an effect that meets this condition if (effectType == EffectType.NONE) { // Add to black list so we don't get multiple effects that trigger on same condition if (cardEffect.triggerCondition != TriggerCondition.NONE) { triggerBlacklist.Add(cardEffect.triggerCondition); } break; } validEffect = GenerateCardEffect(random, model, creatureModels, cardEffect, effectType, minAllowableBudget, maxAllowableBudget, false); } // This means that there isn't an effect that meets this condition if (validEffect) { cardDesc.cardEffects.Add(cardEffect); break; } else { Debug.Log("No valid effect could be generated for trigger <" + cardEffect.triggerCondition.ToString() + "> with budget " + -maxAllowableBudget); } // Add to black list so we don't get multiple effects that trigger on same condition if (cardEffect.triggerCondition != TriggerCondition.NONE) { triggerBlacklist.Add(cardEffect.triggerCondition); } } }