Finished V2. Attempted to start implementing site-control-count modifications, but I suspect what's there is inadequate.

This commit is contained in:
Christian 'ketura' McCarty
2024-06-03 02:15:01 -05:00
parent c2be403bc6
commit 7d90eae248
17 changed files with 390 additions and 133 deletions

View File

@@ -101,7 +101,7 @@
type: AddModifier
until: start(regroup)
modifier: {
type: FPCultureSpot
type: FPCultureCount
amount: -1
}
}

View File

@@ -32,7 +32,7 @@
{
type: modifier
modifier: {
type: FPCultureSpot
type: FPCultureCount
requires: {
type: canSpot
count: 3

View File

@@ -26,17 +26,34 @@
vitality: 3
site: 4
keywords: Twilight
/*requires: {
}
effects: [
{
type: ToPlay
requires: {
type: CanSpot
filter: twilight, minion
}
}
{
type: ModifyOwnCost
amount: {
type: ForEachBurden
multiplier: -1
}
}
]*/
{
type: Modifier
modifier: {
type: AddKeyword
keyword: damage+1
filter: self
requires: {
type: CanSpotBurdens
amount: 4
}
}
}
]
gametext: Twilight.<br>To play, spot a twilight minion.<br>This minion is twilight cost -1 for each burden you can spot.<br>While you can spot 4 burdens, this minion is damage +1.
lore: "'The Dead can't be really there! Is it some devilry hatched in the Dark Land?'"
promotext: ""
@@ -70,17 +87,36 @@
twilight: 1
type: Condition
keywords: Support Area
/*requires: {
}
effects: [
{
type: ToPlay
requires: {
type: CanSpot
filter: Wraith
}
}
{
type: Activated
phase: Regroup
cost: {
type: Discard
select: choose(twilight, minion)
count: 2
}
effect: {
type: Preventable
player: freeps
text: Would you like to wound the Ring-bearer to avoid adding a burden?
cost: {
type: Wound
select: choose(ring-bearer)
}
effect: {
type: AddBurdens
}
}
}
]*/
]
gametext: To play, spot a Wraith.<br>Regroup: Discard two twilight minions to add a burden. The Free Peoples player may wound the Ring-bearer to prevent this.
lore: "'They lie in all the pools, pale faces, deep deep under the dark water.'"
promotext: ""
@@ -114,20 +150,32 @@
twilight: 0
type: Event
timewords: Skirmish
/*requires: {
}
*/
effects: {
type: event
cost: {
type: SendMessage
text: Placeholder effect for development.
type: Exert
select: choose(Wraith)
},
effect: [
{
type: SendMessage
text: Placeholder effect for development.
type: ModifyStrength
amount: 2
select: choose(twilight, minion)
memorize: pumpedTwilight
}
{
type: AddTrigger
trigger: {
type: WinsSkirmish
filter: memory(pumpedTwilight)
}
cost: {
type: Exert
select: memory(pumpedTwilight)
}
effect: {
type: AddBurdens
}
}
]
}
@@ -164,17 +212,39 @@
twilight: 1
type: Condition
keywords: Support Area
/*requires: {
}
effects: [
{
type: ToPlay
requires: {
type: CanSpot
filter: Wraith
}
}
{
type: Modifier
modifier: {
type: CantTakeWounds
filter: twilight
requires: {
type: Not
requires: {
type: Phase
phase: Skirmish
}
}
}
}
]*/
{
type: Trigger
trigger: {
type: TakesOffRing
}
effect: {
type: Discard
select: self
}
}
]
gametext: To play, spot a Wraith.<br>While you can spot 3 twilight minions, twilight minions may not take wounds except during skirmishes.<br>When the ringbearer takes off the One Ring, discard this condition.
lore: "'You cannot reach them, you cannot touch them.'"
promotext: ""
@@ -212,17 +282,28 @@
vitality: 2
site: 6
keywords: Tracker
/*requires: {
}
effects: [
{
type: Modifier
modifier: {
type: ModifySiteNumber
filter: culture(sauron),orc
amount: -1
}
}
{
type: Activated
phase: Skirmish
cost: {
type: Discard
select: self
}
effect: {
type: CancelSkirmish
filter: culture(isengard), tracker
}
}
]*/
]
gametext: Tracker.<br>The site number of each [Sauron] orc is -1.<br>Skirmish: Discard this minion to cancel a skirmish involving an [isengard] tracker.
lore: "'We'll find a use for your legs before long. You'll wish you had got none before we get home.'"
promotext: ""
@@ -257,20 +338,17 @@
type: Event
timewords: Skirmish
keywords: Search
/*requires: {
}
*/
effects: {
type: event
cost: {
type: SendMessage
text: Placeholder effect for development.
},
effect: [
{
type: SendMessage
text: Placeholder effect for development.
type: ModifyStrength
amount: {
type: ForEachYouCanSpot
filter: search
limit: 5
}
select: choose(culture(sauron), tracker)
}
]
}
@@ -312,17 +390,36 @@
vitality: 3
site: 6
keywords: Tracker
/*requires: {
}
effects: [
{
type: modifier
modifier: {
type: modifySiteNumber
filter: culture(sauron),orc
amount: -3
}
}
{
type: Modifier
modifier: {
type: AddKeyword
keyword: damage+1
filter: culture(sauron),minion,siteNumber(0-1)
}
}
]*/
{
type: Activated
phase: Regroup
cost: {
type: Discard
select: choose(or(culture(sauron), culture(isengard)), tracker)
}
effect: {
type: PlayCardFromDiscard
select: choose(culture(sauron), condition)
}
}
]
gametext: Tracker.<br>The site number of each [sauron] orc is -3. <br>Each [sauron] orc with a site number of 1 or less is damage +1.<br>Regroup: Discard a [sauron] or [isengard] tracker to play a [sauron] condition from your discard pile.
lore: ""
promotext: ""
@@ -360,17 +457,22 @@
vitality: 3
site: 6
keywords: Tracker
/*requires: {
}
effects: [
{
type: Modifier
modifier: {
type: CantTakeWounds
filter: culture(sauron),minion,siteNumber(0-1)
requires: {
type: Not
requires: {
type: Phase
phase: Skirmish
}
}
}
}
{
}
]*/
]
gametext: Tracker. <br>Each [sauron] orc with a site number of 1 or less cannot take wounds except during the skirmish phase.
lore: "'Is Saruman the master or the Great Eye?' said the evil voice. 'We should go back at once to Lugbúrz.'"
promotext: ""
@@ -408,17 +510,16 @@
vitality: 2
site: 6
keywords: Tracker
/*requires: {
}
effects: [
{
type: Modifier
modifier: {
type: modifystrength
filter: self,siteNumber(0-1)
amount: 4
}
}
{
}
]*/
]
gametext: Tracker. <br>If this minion's site number is 1 or less, it is strength +4.
lore: "'You're as bad as the other rabble: the maggots and the apes of Lugbúrz.'"
promotext: ""

View File

@@ -23,17 +23,38 @@
block: Towers
direction: Left
keywords: Mountain
/*requires: {
}
effects: [
{
type: Trigger
trigger: {
type: MovesFrom
filter: self
}
requires: {
type: Phase
phase: Regroup
}
effect: {
type: ForEachPlayer
effect: {
type: Optional
text: Would you like to discard 2 cards to draw 2?
effect: {
type: CostToEffect
cost: {
type: Discard
select: choose(any)
count: 2
}
effect: {
type: DrawCards
count: 2
}
}
}
}
}
{
}
]*/
]
gametext: Mountain. When the Fellowship moves from here during the regroup phase, each player may discard 2 cards to draw 2 cards.
lore: ""
promotext: ""
@@ -68,17 +89,20 @@
block: Towers
direction: Left
keywords: Sanctuary
/*requires: {
}
effects: [
{
type: Activated
phase: Fellowship
cost: {
type: PlayCardFromHand
select: choose(valiant,character)
}
effect: {
type: DrawCards
count: 1
}
}
{
}
]*/
]
gametext: Sanctuary. Fellowship: Play a valiant character to draw a card.
lore: ""
promotext: ""
@@ -113,17 +137,16 @@
block: Towers
direction: Left
keywords: Plains
/*requires: {
}
effects: [
{
type: Modifier
modifier: {
type: ModifyCost
filter: mount
amount: -1
}
}
{
}
]*/
]
gametext: Plains. Each mount is twilight cost -1.
lore: ""
promotext: ""
@@ -158,17 +181,21 @@
block: Towers
direction: Left
keywords: Battleground
/*requires: {
}
# Things that need changed for this to be worth a damn:
# - Add ForEachSiteYouControl value
# - Add ForEachSiteOpponentControls value
# - Alter ControlsSite and OpponentDoesNotControlSite requirements
# - Redirect every use of ForEachYouCanSpot with a controlled site filter to use the above
# -
effects: [
{
type: modifier
modifier: {
type: SiteControlCount
amount: +1
}
}
{
}
]*/
]
gametext: Battleground. While at this site, the count of sites the shadow player controls is +1.
lore: ""
promotext: ""
@@ -203,17 +230,32 @@
block: Towers
direction: Left
keywords: Sanctuary
/*requires: {
}
effects: [
{
type: Activated
phase: Fellowship
requires: {
type: PerPhaseLimit
limit: 1
}
cost: {
type: Exert
select: choose(unbound, Hobbit)
}
effect: [
{
type: PlayCardFromDrawDeck
select: choose(or(pipe,pipeweed))
shuffle: true
showAll: true
}
{
type: IncrementPerPhaseLimit
limit: 1
}
]
}
{
}
]*/
]
gametext: Sanctuary. Fellowship: Exert an unbound Hobbit to play a pipe or pipeweed from your draw deck (limit 1).
lore: ""
promotext: ""
@@ -248,17 +290,21 @@
block: Towers
direction: Left
keywords: Battleground
/*requires: {
}
effects: [
{
type: Modifier
modifier: {
type: AddKeyword
keyword: damage+1
filter: character
requires: {
type: CanSpot
filter: companion
count: 6
}
}
}
{
}
]*/
]
gametext: Battleground. While you can spot 6 companions, each character is damage +1.
lore: ""
promotext: ""
@@ -292,17 +338,26 @@
site: 8
block: Towers
direction: Left
/*requires: {
}
effects: [
{
type: Trigger
trigger: {
type: MovesTo
filter: self
}
effect: {
type: ForEachPlayer
effect: {
type: Optional
text: Would you like to discard a non-artifact card in your opponent's support area?
effect: {
type: Discard
select: choose(not(artifact),not(your),zone(SUPPORT))
}
}
}
}
{
}
]*/
]
gametext: When the fellowship moves to this site, each player may discard a card (except an artifact) from their opponent's support area.
lore: ""
promotext: ""
@@ -336,17 +391,20 @@
site: 9
block: Towers
direction: Left
/*requires: {
}
effects: [
{
type: Activated
phase: Shadow
cost: {
type: Exert
select: choose(minion)
}
effect: {
type: PlayCardFromDiscard
select: choose(side(shadow), possession)
}
}
{
}
]*/
]
gametext: Shadow: Exert a minion to play a Shadow possession from your discard pile.
lore: ""
promotext: ""

View File

@@ -469,7 +469,14 @@ public class ValueResolver {
Filterable filterable = filterableSource.getFilterable(actionContext);
return (game, cardAffected) -> GameUtils.getSpottableRacesCount(game, filterable);
});
} else if (type.equalsIgnoreCase("forEachStacked")) {
} else if (type.equalsIgnoreCase("forEachSiteYouControl")) {
FieldUtils.validateAllowedFields(object, "over", "limit", "multiplier", "divider");
return new SmartValueSource(environment, object,
actionContext -> {
String performingPlayer = actionContext.getPerformingPlayer();
return (game, cardAffected) -> GameUtils.getSpottableControlledSitesCount(game, performingPlayer);
});
} else if (type.equalsIgnoreCase("forEachStacked")) {
FieldUtils.validateAllowedFields(object, "on", "filter", "over", "limit", "multiplier", "divider");
final String on = FieldUtils.getString(object.get("on"), "on");
final String filter = FieldUtils.getString(object.get("filter"), "filter", "any");

View File

@@ -6,7 +6,7 @@ import com.gempukku.lotro.cards.build.field.effect.appender.resolver.ValueResolv
import com.gempukku.lotro.logic.modifiers.FPCulturesSpotCountModifier;
import org.json.simple.JSONObject;
public class FPCultureSpot implements ModifierSourceProducer {
public class FPCultureCount implements ModifierSourceProducer {
@Override
public ModifierSource getModifierSource(JSONObject object, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
FieldUtils.validateAllowedFields(object, "requires", "amount");

View File

@@ -59,7 +59,7 @@ public class ModifierSourceFactory {
modifierProducers.put("disablegametext", new DisableGameText());
modifierProducers.put("doesnotaddtoarcherytotal", new DoesNotAddToArcheryTotal());
modifierProducers.put("extracosttoplay", new ExtraCostToPlay());
modifierProducers.put("fpculturespot", new FPCultureSpot());
modifierProducers.put("fpculturecount", new FPCultureCount());
modifierProducers.put("hastomoveifable", new AddModifierFlag(ModifierFlag.HAS_TO_MOVE_IF_POSSIBLE));
modifierProducers.put("itemclassspot", new ItemClassSpot());
modifierProducers.put("modifyarcherytotal", new ModifyArcheryTotal());
@@ -82,6 +82,7 @@ public class ModifierSourceFactory {
modifierProducers.put("ringtextisinactive", new AddModifierFlag(ModifierFlag.RING_TEXT_INACTIVE));
modifierProducers.put("sarumanfirstsentenceinactive", new AddModifierFlag(ModifierFlag.SARUMAN_FIRST_SENTENCE_INACTIVE));
modifierProducers.put("shadowhasinitiative", new ShadowHasInitiative());
modifierProducers.put("sitecontrolcount", new SiteControlCount());
modifierProducers.put("skipphase", new SkipPhase());
modifierProducers.put("skirmishesresolvedinorderbyfirstshadowplayer", new AddModifierFlag(ModifierFlag.SKIRMISH_ORDER_BY_FIRST_SHADOW_PLAYER));
modifierProducers.put("transferforfree", new AddModifierFlag(ModifierFlag.TRANSFERS_FOR_FREE));

View File

@@ -0,0 +1,25 @@
package com.gempukku.lotro.cards.build.field.effect.modifier;
import com.gempukku.lotro.cards.build.*;
import com.gempukku.lotro.cards.build.field.FieldUtils;
import com.gempukku.lotro.cards.build.field.effect.appender.resolver.ValueResolver;
import com.gempukku.lotro.logic.modifiers.FPCulturesSpotCountModifier;
import com.gempukku.lotro.logic.modifiers.RaceSpotModifier;
import com.gempukku.lotro.logic.modifiers.SiteControlCountModifier;
import org.json.simple.JSONObject;
public class SiteControlCount implements ModifierSourceProducer {
@Override
public ModifierSource getModifierSource(JSONObject object, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
FieldUtils.validateAllowedFields(object, "amount", "requires");
final JSONObject[] conditionArray = FieldUtils.getObjectArray(object.get("requires"), "requires");
final ValueSource amount = ValueResolver.resolveEvaluator(object.get("amount"), environment);
final Requirement[] requirements = environment.getRequirementFactory().getRequirements(conditionArray, environment);
return actionContext -> new SiteControlCountModifier(actionContext.getSource(),
RequirementCondition.createCondition(requirements, actionContext),
amount.getEvaluator(actionContext));
}
}

View File

@@ -0,0 +1,18 @@
package com.gempukku.lotro.cards.build.field.effect.requirement;
import com.gempukku.lotro.cards.build.CardGenerationEnvironment;
import com.gempukku.lotro.cards.build.InvalidCardDefinitionException;
import com.gempukku.lotro.cards.build.Requirement;
import com.gempukku.lotro.cards.build.field.FieldUtils;
import com.gempukku.lotro.logic.GameUtils;
import org.json.simple.JSONObject;
public class CanSpotControlledSites implements RequirementProducer {
@Override
public Requirement getPlayRequirement(JSONObject object, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
FieldUtils.validateAllowedFields(object, "amount");
final int amount = FieldUtils.getInteger(object.get("amount"), "amount");
return (actionContext) -> GameUtils.getSpottableControlledSitesCount(actionContext.getGame(), actionContext.getPerformingPlayer()) >= amount;
}
}

View File

@@ -30,6 +30,7 @@ public class RequirementFactory {
requirementProducers.put("cantspot", new CantSpot());
requirementProducers.put("canspotburdens", new CanSpotBurdens());
requirementProducers.put("cantspotburdens", new CantSpotBurdens());
requirementProducers.put("canspotcontrolledsites", new CanSpotControlledSites());
requirementProducers.put("canspotculturetokens", new CanSpotCultureTokens());
requirementProducers.put("canspotfpcultures", new CanSpotFPCultures());
requirementProducers.put("cantspotfpcultures", new CantSpotFPCultures());

View File

@@ -319,4 +319,8 @@ public class GameUtils {
public static int getSpottableShadowCulturesCount(LotroGame game, String playerId) {
return game.getModifiersQuerying().getNumberOfSpottableShadowCultures(game, playerId);
}
public static int getSpottableControlledSitesCount(LotroGame game, String playerId) {
return game.getModifiersQuerying().getNumberOfSpottableControlledSites(game, playerId);
}
}

View File

@@ -385,6 +385,9 @@ public abstract class AbstractModifier implements Modifier {
return 0;
}
@Override
public int getSiteControlledSpotCountModifier(LotroGame game, String playerId) { return 0; }
@Override
public int getSanctuaryHealModifier(LotroGame game) {
return 0;

View File

@@ -194,6 +194,11 @@ public class DelegateModifier implements Modifier {
return delegate.getFPCulturesSpotCountModifier(game, playerId);
}
@Override
public int getSiteControlledSpotCountModifier(LotroGame game, String playerId) {
return delegate.getSiteControlledSpotCountModifier(game, playerId);
}
@Override
public Evaluator getFpSkirmishStrengthOverrideEvaluator(LotroGame game, PhysicalCard fpCharacter) {
return delegate.getFpSkirmishStrengthOverrideEvaluator(game, fpCharacter);

View File

@@ -156,6 +156,8 @@ public interface Modifier {
int getFPCulturesSpotCountModifier(LotroGame game, String playerId);
int getSiteControlledSpotCountModifier(LotroGame game, String playerId);
int getSanctuaryHealModifier(LotroGame game);
int getPotentialDiscount(LotroGame game, PhysicalCard discountCard);

View File

@@ -1014,6 +1014,17 @@ public class ModifiersLogic implements ModifiersEnvironment, ModifiersQuerying {
return result;
}
@Override
public int getNumberOfSpottableControlledSites(LotroGame game, String playerId) {
int result = Filters.filterActive(game, Filters.siteControlled(playerId)).size();
for (Modifier modifier : getModifiers(game, ModifierEffect.SPOT_MODIFIER)) {
result += modifier.getSiteControlledSpotCountModifier(game, playerId);
}
return result;
}
private boolean canPlayerSpotCulture(LotroGame game, String playerId, Culture culture) {
for (Modifier modifier : getModifiers(game, ModifierEffect.SPOT_MODIFIER))
if (!modifier.canSpotCulture(game, culture, playerId))

View File

@@ -148,6 +148,8 @@ public interface ModifiersQuerying {
int getNumberOfSpottableShadowCultures(LotroGame game, String playerId);
int getNumberOfSpottableControlledSites(LotroGame game, String playerId);
int getSpotBonus(LotroGame game, Filterable filter);
boolean hasFlagActive(LotroGame game, ModifierFlag modifierFlag);

View File

@@ -0,0 +1,19 @@
package com.gempukku.lotro.logic.modifiers;
import com.gempukku.lotro.game.PhysicalCard;
import com.gempukku.lotro.game.state.LotroGame;
import com.gempukku.lotro.logic.modifiers.evaluator.Evaluator;
public class SiteControlCountModifier extends AbstractModifier {
private final Evaluator _valueEvaluator;
public SiteControlCountModifier(PhysicalCard source, Condition condition, Evaluator value) {
super(source, "Modifies Shadow controlled site count", null, condition, ModifierEffect.SPOT_MODIFIER);
_valueEvaluator = value;
}
@Override
public int getSiteControlledSpotCountModifier(LotroGame game, String playerId) {
return _valueEvaluator.evaluateExpression(game, null);
}
}