async Task BringerPushNInvaders(Space source, int countToPush, params TokenClass[] healthyInvaders) { // We can't track which original invader is was killed, so let the user choose. // TokenCountDictionary tokens = ctx.Target(source).Tokens; TokenCountDictionary tokens = ctx.GameState.Tokens[source]; Token[] CalcInvaderTypes() => tokens.OfAnyType(healthyInvaders); var invaders = CalcInvaderTypes(); while (0 < countToPush && 0 < invaders.Length) { var invader = await ctx.Decision(Select.TokenFrom1Space.TokenToPush(source, countToPush, invaders, Present.Always)); if (invader == null) { break; } var destination = await ctx.Decision(new Select.Space( "Push " + invader.ToString() + " to", source.Adjacent.Where(s => ctx.Target(s).IsInPlay(Invader.Explorer)) , Present.Always )); await tokens.MoveTo(invader, destination); --countToPush; invaders = CalcInvaderTypes(); } }
public bool RunAfterGrowthResult => true; // might receive additional presence public override async Task ActivateAsync(SelfCtx ctx) { var src = await ctx.Decision(Select.DeployedPresence.All("Move presence from:", ctx.Self, Present.Always)); var dstOptions = src.Range(Range).Where(s => !s.IsOcean); var dst = await ctx.Decision(Select.Space.ForAdjacent("Move preseence to:", src, Select.AdjacentDirection.Outgoing, dstOptions, Present.Always)); ctx.Presence.Move(src, dst); }
public override async Task ActivateAsync(SelfCtx ctx) { var options = ctx.Self.Presence.Spaces.SelectMany(p => p.Range(2)).Distinct(); var to = await ctx.Decision(new Select.Space("Gather beast to", options, Present.Always)); await ctx.Target(to).GatherUpTo(1, TokenType.Beast); }
// ! Can't used normal PlacePresence, because it must be range-1, range 0 not allowed. public override async Task ActivateAsync(SelfCtx ctx) { var options = ctx.Self.Presence.Spaces.First().Adjacent; var space = await ctx.Decision(new Select.Space("Add presence to", options, Present.Always)); ctx.Presence.PlaceOn(space); }
public override async Task ActivateAsync(SelfCtx ctx) { List <Space> gatherSpaces = ctx.Self.Presence.Spaces .Where(p => p.IsCoastal) .Select(p => p.Adjacent.Single(o => o.IsOcean)) .Distinct() .ToList(); while (0 < gatherSpaces.Count) { Space currentTarget = gatherSpaces[0]; Space source = await ctx.Decision(new Select.Space( $"Select source of Presence to Gather into {currentTarget}" , currentTarget.Adjacent .Where(ctx.Self.Presence.Spaces.Contains) .ToArray() , Present.Always )); // apply... ctx.Presence.Move(source, currentTarget); // next gatherSpaces.RemoveAt(0); } // while }
public static async Task ActAsync(SelfCtx ctx) { // Your presence may count as badlands and beast. int PresenceAsToken(GameState _, Space space) => ctx.Self.Presence.CountOn(space); ctx.GameState.Tokens.RegisterDynamic(PresenceAsToken, TokenType.Badlands, false); ctx.GameState.Tokens.RegisterDynamic(PresenceAsToken, TokenType.Beast, false); // (Decide per presence, per action) ... Not doing this bit exactly, both are always present, but can't be destroyed. // your presence cannot move. // !!! not implemented // if you have 2 plant 3 animal: if (await ctx.YouHave("2 plant,3 animal")) { // 2 fear and ctx.AddFear(2); // 2 damamge in one of your lands. var space = await ctx.Decision(new Select.Space("2 damage", ctx.Self.Presence.Spaces, Present.Always)); if (space != null) { await ctx.Target(space).DamageInvaders(2); } } }
public override async Task <object> GetTargetCtx(string powerName, SelfCtx ctx, TargettingFrom _) { Spirit target = ctx.GameState.Spirits.Length == 1 ? ctx.Self : await ctx.Decision(new Select.Spirit(powerName, ctx.GameState.Spirits)); return(ctx.TargetSpirit(target)); }
public async Task <(Space, Space)> PushUpTo1() { // Select source var source = await ctx.Decision(Select.DeployedPresence.ToPush(ctx.Self)); if (source == null) { return(null, null); } var sourceCtx = ctx.Target(source); // Select destination var destination = await sourceCtx.Decision(Select.Space.PushPresence(sourceCtx.Space, sourceCtx.Adjacent, Present.Always)); Move(source, destination); return(source, destination); }
public override async Task ActivateAsync(SelfCtx ctx) { var gameState = ctx.GameState; var options = gameState.Island.AllSpaces.Where(space => gameState.Tokens[space].Beasts.Any); var space = await ctx.Decision(new Select.Space("Add presence to", options, Present.Always)); ctx.Presence.PlaceOn(space); }
static public async Task ActAsync(SelfCtx ctx) { if (ctx.Self is not FracturedDaysSplitTheSky frac) { return; } // Cost to Use: 3 Time if (frac.Time < 3) { return; } await frac.SpendTime(3); // Move 1 of your presence to a different land with your presence. var src = await ctx.Decision(Select.DeployedPresence.All("Move presence from:", ctx.Self, Present.Always)); var dstOptions = ctx.Self.Presence.Spaces.Where(s => s != src); var dst = await ctx.Decision(Select.Space.ForAdjacent("Move preseence to:", src, Select.AdjacentDirection.Outgoing, dstOptions, Present.Always)); ctx.Presence.Move(src, dst); if (src.Board == dst.Board) { return; } // On the board moved from: During the Invader Phase, Resolve Invader and "Each board / Each land..." Actions one fewer time. foreach (var space in src.Board.Spaces) { ctx.GameState.Skip1Build(space); ctx.GameState.SkipExplore(space); ctx.GameState.SkipRavage(space); } // On the board moved to: During the Invader Phase, Resolve Invader and "Each board / Each Land..." Actions one more time. foreach (var space in src.Board.Spaces) { ctx.GameState.Add1Build(space); ctx.GameState.AddExplore(space); ctx.GameState.AddRavage(space); } }
public bool RunAfterGrowthResult => true; // depends on Presence location which might change during growth public override async Task ActivateAsync(SelfCtx ctx) { var dahanOptions = ctx.Self.Presence.Spaces .SelectMany(space => ctx.Target(space).Dahan.Keys.Select(t => new SpaceToken(space, t))); var source = await ctx.Decision(new Select.TokenFromManySpaces("Select dahan to push from land", dahanOptions, Present.Done)); if (source == null) { return; } await new TokenPusher(ctx.Target(source.Space)).PushToken(source.Token); }
static public async Task ActAsync(SelfCtx ctx) { if (ctx.Self is not FracturedDaysSplitTheSky frac) { return; } // Cost to Use: N Time, and Spirits jointly pay N Energy (where N = # of players). int cost = ctx.GameState.Spirits.Length; if (frac.Time < cost) { return; } // !!! Implementing this as 1 spirit, but once we have >1 player, switch to all spirits can pay if (frac.Energy < cost) { return; } frac.Energy -= cost; await frac.SpendTime(cost); // Swap the top card of the Invader Deck with a card in the Invader discard that is withing 1 Invader Stage of it. var deck = ctx.GameState.InvaderDeck; var newCard = deck.Explore.First(); int stageOfTopCard = newCard.InvaderStage; var options = deck.Discards.Where(d => System.Math.Abs(d.InvaderStage - stageOfTopCard) <= 1).ToArray(); // You can't swap cards that don't exist. if (options.Length == 0) { return; } var oldCard = await ctx.Decision(new Select.TypedDecision <IInvaderCard>( "Select card to return to top of Invader deck", options, Present.Always )); // Replace New with Old deck.Explore[0] = oldCard; // Replace Old with New deck.Discards.Remove(oldCard); deck.Discards.Add(newCard); // face down // The Discarded card stays face-down. // !!! deck / card has no knowledge of face-up/face-down, if spirit plays this card again, they will see the 'face-down' card }
class Setup_PlacePresenceOnMountain : GrowthActionFactory { // Similar to SharpFang initialization public override async Task ActivateAsync(SelfCtx ctx) { // Put 1 presence on your starting board in a mountain of your choice. var options = ctx.AllSpaces.Where(space => space.IsMountain); var space = await ctx.Decision(new Select.Space("Add presence to", options, Present.Always)); ctx.Presence.PlaceOn(space); // Push all dahan from that land. var targetCtx = ctx.Target(space); if (targetCtx.Dahan.Any) { await targetCtx.PushDahan(targetCtx.Dahan.Count); } }
static async Task DistributeTokens(SelfCtx ctx, Space space, Space other, GameState gs) { // Distribute Tokens (All of them are considered moved.) // !!! not counting unmoved tokens as moved li var srcTokens = gs.Tokens[space]; while (srcTokens.Keys.Any()) { var tokenToMove = await ctx.Decision(Select.TokenFrom1Space.TokenToPush(space, 100, srcTokens.Keys.ToArray(), Present.Done)); if (tokenToMove == null) { break; } await srcTokens.MoveTo(tokenToMove, other); } }
static async Task TakeActionInUpToNLands(SelfCtx ctx, int adjCount, IEnumerable <Space> spaces, ActionOption <TargetSpaceCtx> action) { List <Space> options = spaces.ToList(); while (adjCount-- > 0 && options.Count > 0) { var space = await ctx.Decision(new Select.Space($"{action.Description} ({adjCount + 1} remaining)", options, Present.Done)); if (space == null) { break; } await action.Execute(ctx.Target(space)); options.Remove(space); } }
public override async Task ActivateAsync(SelfCtx ctx) { if (ctx.Self.Presence.Placed.Count == 1) { return; // don't let them switch their last presence to a beast } var options = ctx.Self.Presence.Spaces; var space = await ctx.Decision(new Select.DeployedPresence("Select presence to replace with beast", options, Present.Done)); // let them change their minds if (space == null) { return; } await ctx.Presence.RemoveFrom(space); await ctx.GameState.Tokens[space].Beasts.Add(1); }
public override async Task ActivateAsync(SelfCtx ctx) { List <Space> pushSpaces = ctx.Self.Presence.Spaces .Where(p => p.IsOcean) .Distinct() .ToList(); while (0 < pushSpaces.Count) { var currentSource = pushSpaces[0]; // #pushpresence var destination = await ctx.Decision(Select.Space.PushPresence(currentSource, currentSource.Adjacent, Present.Always)); // apply... ctx.Presence.Move(currentSource, destination); // next pushSpaces.RemoveAt(0); } }
static async Task PushExplorerIntoSpaceWithMoreTownsOrCities(SelfCtx ctx, Dictionary <Space, int> buildingCounts) { Space[] GetNeighborWithMoreBuildings(Space s) => s.Adjacent.Where(n => buildingCounts[n] > buildingCounts[s]).ToArray(); bool HasNeighborWithMoreBuildings(Space s) => GetNeighborWithMoreBuildings(s).Any(); // Select Source var sourceOptions = ctx.GameState.Island.AllSpaces .Where(s => ctx.GameState.Tokens[s].Has(Invader.Explorer) && HasNeighborWithMoreBuildings(s)) .ToArray(); if (sourceOptions.Length == 0) { return; } Space source = await ctx.Decision(new Select.Space("Fear: Select land to push explorer from into more towns/cities", sourceOptions, Present.Done)); if (source == null) { return; // continue => next spirit, break/return => no more spirits } // Push await ctx.Target(source).PushUpTo(1, Invader.Explorer); }
public override async Task ActivateAsync(SelfCtx ctx) { var space = await ctx.Decision(new Select.Space("Select land to apply 2 Damage.", ctx.Self.Presence.Spaces, Present.Always)); await ctx.Target(space).DamageInvaders(2); }