ported from perforce
This commit is contained in:
27
RobotAndDonkey.Game/Intents/AcceptGlitch.cs
Normal file
27
RobotAndDonkey.Game/Intents/AcceptGlitch.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class AcceptGlitch(Card card) : CardIntent(card)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
Card.CreateIntents(null, coreLoop, requestId, newIntents, results);
|
||||
results.Add(new RunCardResult(requestId, Card));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (Card != coreLoop.GlitchDeck[coreLoop.NextGlitch])
|
||||
return false;
|
||||
|
||||
return base.IsValid(coreLoop);
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => $"Accept {Card.Name}, " + base.ToString();
|
||||
}
|
||||
34
RobotAndDonkey.Game/Intents/BuyPatch.cs
Normal file
34
RobotAndDonkey.Game/Intents/BuyPatch.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class BuyPatch(Card card, bool free) : CardIntent(card, free ? 0 : card.ShopCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.Shop.Remove(Card);
|
||||
coreLoop.PatchDeck.Add(Card);
|
||||
results.Add(new ShopResult(requestId, coreLoop.Shop.ToArray()));
|
||||
results.Add(new DeckResult(requestId, coreLoop.PatchDeck.ToArray()));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!Free && !coreLoop.Shop.Contains(Card))
|
||||
return false;
|
||||
|
||||
if (Free && !coreLoop.BoosterPack.Contains(Card))
|
||||
return false;
|
||||
|
||||
return base.IsValid(coreLoop);
|
||||
}
|
||||
|
||||
public bool Free { get; } = free;
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => $"Buy {Card.Name}, " + base.ToString();
|
||||
}
|
||||
8
RobotAndDonkey.Game/Intents/CardCostIntent.cs
Normal file
8
RobotAndDonkey.Game/Intents/CardCostIntent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class CardCostIntent(Card card, int energyCost) : CardIntent(card, energyCost)
|
||||
{
|
||||
public override string ToString() => $"Card cost for {Card.Name}, " + base.ToString();
|
||||
}
|
||||
8
RobotAndDonkey.Game/Intents/CardIntent.cs
Normal file
8
RobotAndDonkey.Game/Intents/CardIntent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public abstract class CardIntent(Card card, int energyCost = 0) : Intent(energyCost)
|
||||
{
|
||||
public Card Card { get; } = card;
|
||||
}
|
||||
21
RobotAndDonkey.Game/Intents/DeferGlitch.cs
Normal file
21
RobotAndDonkey.Game/Intents/DeferGlitch.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class DeferGlitch(int energyCost) : Intent(energyCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.DeferGlitchCount += 1;
|
||||
results.Add(new DeferGlitchCountResult(requestId, coreLoop.DeferGlitchCount));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => "Defer glitch, " + base.ToString();
|
||||
}
|
||||
29
RobotAndDonkey.Game/Intents/DestroyCard.cs
Normal file
29
RobotAndDonkey.Game/Intents/DestroyCard.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class DestroyCard(Card card) : CardIntent(card)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.RemoveProgramCard(Card);
|
||||
var handCards = coreLoop.GetHandCards();
|
||||
coreLoop.PatchDeck.AddRange(handCards);
|
||||
foreach (var handCard in handCards)
|
||||
{
|
||||
coreLoop.RemoveProgramCard(handCard);
|
||||
}
|
||||
coreLoop.ClearTapeSelection();
|
||||
|
||||
results.Add(new ProgramRowResult(requestId, coreLoop.ProgramRow.ToArray(), coreLoop.TapeCardIds));
|
||||
results.Add(new DeckResult(requestId, coreLoop.PatchDeck.ToArray()));
|
||||
results.Add(new HandResult(requestId, coreLoop.Hand.ToArray()));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => $"Destroy {Card.Name}, " + base.ToString();
|
||||
}
|
||||
64
RobotAndDonkey.Game/Intents/DetoxiumPrime.cs
Normal file
64
RobotAndDonkey.Game/Intents/DetoxiumPrime.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Pois;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class DetoxiumPrime(Cell cell, Card card, EDirection direction) : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
var neighbour = cell.Hex.GetNeighbour(Direction);
|
||||
var neighbourIndex = coreLoop.Board.FindCellIndex(neighbour);
|
||||
if (neighbourIndex < 0)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.OutOfBounds, card, cell));
|
||||
}
|
||||
else
|
||||
{
|
||||
var neighbourCell = coreLoop.Board.Cells[neighbourIndex];
|
||||
var newType = (neighbourCell.Type, Heal) switch
|
||||
{
|
||||
(ECellType.Blocked, true) => ECellType.Mud,
|
||||
(ECellType.Rocky, true) => ECellType.Grass,
|
||||
(ECellType.Mud, true) => ECellType.Grass,
|
||||
(ECellType.Dry, true) => ECellType.Grass,
|
||||
(ECellType.Grass, true) => ECellType.Fertile,
|
||||
(ECellType.Grass, false) => coreLoop.Random.Next(3) switch
|
||||
{
|
||||
0 => ECellType.Dry,
|
||||
1 => ECellType.Mud,
|
||||
2 => ECellType.Rocky,
|
||||
_ => throw new UnreachableException()
|
||||
},
|
||||
(ECellType.Fertile, false) => ECellType.Grass,
|
||||
(ECellType.Mud, false) => ECellType.Blocked,
|
||||
_ => (ECellType)(-1)
|
||||
};
|
||||
if (newType < 0)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoTarget, card, neighbourCell));
|
||||
}
|
||||
else
|
||||
{
|
||||
neighbourCell.Type = newType;
|
||||
results.Add(new CellTypeResult(requestId, new(coreLoop.Board), neighbourCell.Hex));
|
||||
}
|
||||
}
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public EDirection Direction { get; set; } = direction;
|
||||
|
||||
public bool Heal { get; set; } = true;
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop) => base.IsValid(coreLoop) && cell.Poi is Avatar;
|
||||
|
||||
public override string ToString() => $"Detox {Direction}, Heal={Heal}, " + base.ToString();
|
||||
}
|
||||
75
RobotAndDonkey.Game/Intents/Discard.cs
Normal file
75
RobotAndDonkey.Game/Intents/Discard.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class Discard(Guid[] cardIds, int energyCost) : Intent(energyCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
var pool = coreLoop.ProgramRow.ToDictionary(card => card.CardId);
|
||||
var discardCards = new List<Card>(cardIds.Length);
|
||||
foreach (var cardId in cardIds)
|
||||
{
|
||||
if (pool.TryGetValue(cardId, out var card))
|
||||
discardCards.Add(card);
|
||||
}
|
||||
|
||||
var discardSet = discardCards.ToHashSet();
|
||||
var newProgramRow = new List<Card>(coreLoop.ProgramRow.Count);
|
||||
foreach (var card in coreLoop.ProgramRow)
|
||||
{
|
||||
if (!discardSet.Contains(card))
|
||||
{
|
||||
newProgramRow.Add(card);
|
||||
continue;
|
||||
}
|
||||
|
||||
coreLoop.Discard.Add(card);
|
||||
if (coreLoop.PatchDeck.Count > 0)
|
||||
{
|
||||
var newCard = coreLoop.PatchDeck[0];
|
||||
coreLoop.PatchDeck.RemoveAt(0);
|
||||
newProgramRow.Add(newCard);
|
||||
}
|
||||
}
|
||||
|
||||
var remainingTapeIds = coreLoop.TapeCardIds.Where(id => !cardIds.Contains(id)).ToHashSet();
|
||||
coreLoop.SetProgramRow(newProgramRow, remainingTapeIds);
|
||||
|
||||
results.Add(new CurrencyResult(requestId, coreLoop.Currency));
|
||||
results.Add(new ProgramRowResult(requestId, coreLoop.ProgramRow.ToArray(), coreLoop.TapeCardIds));
|
||||
results.Add(new HandResult(requestId, coreLoop.Hand.ToArray()));
|
||||
results.Add(new TapeResult(requestId, coreLoop.Tape.ToArray()));
|
||||
results.Add(new DeckResult(requestId, coreLoop.PatchDeck.ToArray()));
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!base.IsValid(coreLoop))
|
||||
return false;
|
||||
|
||||
if (cardIds.Length == 0)
|
||||
return false;
|
||||
|
||||
var uniqueIds = cardIds.ToHashSet();
|
||||
if (uniqueIds.Count != cardIds.Length)
|
||||
return false;
|
||||
|
||||
foreach (var cardId in uniqueIds)
|
||||
{
|
||||
if (!coreLoop.TapeCardIds.Contains(cardId))
|
||||
return false;
|
||||
}
|
||||
|
||||
return coreLoop.RunPhase == ERunPhase.ExecuteProgram;
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => $"Discard {string.Join(", ", cardIds.Select(id => id.ToString()))}, " + base.ToString();
|
||||
}
|
||||
37
RobotAndDonkey.Game/Intents/DonkeyIntent.cs
Normal file
37
RobotAndDonkey.Game/Intents/DonkeyIntent.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Data;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Pois;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class DonkeyIntent(Card card, Cell donkeyCell, int energyCost) : Intent(energyCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
if (coreLoop.HasDonkey && donkeyCell.Poi == null)
|
||||
{
|
||||
coreLoop.HasDonkey = false;
|
||||
donkeyCell.Poi = new Donkey();
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), donkeyCell.Poi, donkeyCell.Hex));
|
||||
newIntents.Add(new ModifyCurrency(new(0, -Balancing.Instance.DonkeyMaxCarryBonus, 0, 0, 0, 0)));
|
||||
}
|
||||
else if (!coreLoop.HasDonkey && donkeyCell.Poi is Donkey donkey)
|
||||
{
|
||||
coreLoop.HasDonkey = true;
|
||||
donkeyCell.Poi = null;
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), donkey, donkeyCell.Hex));
|
||||
newIntents.Add(new ModifyCurrency(new(0, Balancing.Instance.DonkeyMaxCarryBonus, 0, 0, 0, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoTarget, card, donkeyCell));
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => "Donkey, " + base.ToString();
|
||||
}
|
||||
33
RobotAndDonkey.Game/Intents/EnterBufferOverflow.cs
Normal file
33
RobotAndDonkey.Game/Intents/EnterBufferOverflow.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class EnterBufferOverflow(int energyCost) : Intent(energyCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.CanBufferOverflow = false;
|
||||
coreLoop.RunPhase = ERunPhase.BufferOverflow;
|
||||
results.Add(new NoMoreBufferOverflowResult(requestId));
|
||||
results.Add(new RunPhaseResult(requestId, coreLoop.RunPhase));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!base.IsValid(coreLoop))
|
||||
return false;
|
||||
|
||||
return coreLoop is { CanBufferOverflow: true, RunPhase: ERunPhase.Improve };
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => "Enter buffer overflow, " + base.ToString();
|
||||
}
|
||||
32
RobotAndDonkey.Game/Intents/EnterGamble.cs
Normal file
32
RobotAndDonkey.Game/Intents/EnterGamble.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using RobotAndDonkey.Game.Data;
|
||||
using RobotAndDonkey.Game.Execution;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using System.Numerics;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class EnterGamble(int energyCost) : Intent(energyCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.CanGamble = false;
|
||||
coreLoop.BoosterPack = [..CoreLoop.CreatePatchDeck(ref coreLoop.Random, coreLoop.Difficulty, Balancing.Instance.GambleBoosterSize, true)];
|
||||
coreLoop.RunPhase = ERunPhase.Gamble;
|
||||
results.Add(new NoMoreGamblingResult(requestId));
|
||||
results.Add(new RunPhaseResult(requestId, coreLoop.RunPhase));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!base.IsValid(coreLoop))
|
||||
return false;
|
||||
|
||||
return coreLoop is { CanGamble: true, RunPhase: ERunPhase.Improve };
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => "Enter gamble, " + base.ToString();
|
||||
}
|
||||
32
RobotAndDonkey.Game/Intents/EnterPreview.cs
Normal file
32
RobotAndDonkey.Game/Intents/EnterPreview.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.Utils;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class EnterPreview : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.ShuffleDeck();
|
||||
coreLoop.DrawHand();
|
||||
coreLoop.ProgramCount = coreLoop.Robot.ProgramCount;
|
||||
coreLoop.ClearTapeSelection();
|
||||
|
||||
var sortedHand = CardExtensions.SortForHand(coreLoop.GetHandCards());
|
||||
coreLoop.SetProgramRow(sortedHand, Array.Empty<Guid>());
|
||||
|
||||
results.Add(new ProgramRowResult(requestId, coreLoop.ProgramRow.ToArray(), coreLoop.TapeCardIds));
|
||||
results.Add(new HandResult(requestId, coreLoop.Hand.ToArray()));
|
||||
results.Add(new TapeResult(requestId, coreLoop.Tape.ToArray()));
|
||||
results.Add(new DeckResult(requestId, coreLoop.PatchDeck.ToArray()));
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => "Enter preview, " + base.ToString();
|
||||
}
|
||||
78
RobotAndDonkey.Game/Intents/ImmunizeCard.cs
Normal file
78
RobotAndDonkey.Game/Intents/ImmunizeCard.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Modifiers;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class ImmunizeCard(Card source, Card target, EModifierId modifierId, EModifierDuration duration, bool canCorrupt = true) : CardIntent(target)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
if (DebuffState)
|
||||
{
|
||||
if (Card.Modifiers.OfType<GlobalImmunityModifier>().FirstOrDefault() is {} existingImmunity)
|
||||
{
|
||||
if (!existingImmunity.Modifiers.Any(m => m.Modifier == ModifierId && m.Source == source))
|
||||
existingImmunity.Modifiers.Add((source, ModifierId));
|
||||
}
|
||||
else
|
||||
{
|
||||
var newImmunity = new GlobalImmunityModifier(Duration);
|
||||
newImmunity.Modifiers.Add((source, ModifierId));
|
||||
Card.InsertModifier(newImmunity, newIntents);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var modifier in Card.Modifiers.ToArray())
|
||||
{
|
||||
if (modifier is GlobalImmunityModifier giModifier)
|
||||
{
|
||||
for (var i = giModifier.Modifiers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var immunity = giModifier.Modifiers[i];
|
||||
if (immunity.Modifier == ModifierId)
|
||||
{
|
||||
giModifier.Modifiers.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (giModifier.Modifiers.Count == 0)
|
||||
Card.RemoveModifier(modifier, newIntents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results.Add(new ModifyCardResult(requestId, Card.DeepClone(), EModifierId.GlobalImmunity, ECardLocation.Tape));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
return base.IsValid(coreLoop) &&
|
||||
ModifierId is
|
||||
EModifierId.Corrupt or
|
||||
EModifierId.Unreliable or
|
||||
EModifierId.RaceCondition or
|
||||
EModifierId.Throttled or
|
||||
EModifierId.Effective or
|
||||
EModifierId.Optimized or
|
||||
EModifierId.Efficient or
|
||||
EModifierId.Persistent or
|
||||
EModifierId._Invalid;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Immunize {Card.Name} from {source.Name} against {ModifierId}, {Duration}, DebuffState={DebuffState}, CanCorrupt={CanCorrupt}, " + base.ToString();
|
||||
}
|
||||
|
||||
public EModifierId ModifierId { get; set; } = modifierId;
|
||||
|
||||
public bool DebuffState { get; set; } = true;
|
||||
|
||||
public bool CanCorrupt { get; set; } = canCorrupt;
|
||||
|
||||
public EModifierDuration Duration { get; set; } = duration;
|
||||
}
|
||||
33
RobotAndDonkey.Game/Intents/Intent.cs
Normal file
33
RobotAndDonkey.Game/Intents/Intent.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Modifiers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public abstract class Intent(int energyCost = 0)
|
||||
{
|
||||
public virtual void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
if (EnergyCost != 0)
|
||||
{
|
||||
coreLoop.Currency.Energy -= EnergyCost;
|
||||
results.Add(new CurrencyResult(requestId, coreLoop.Currency));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
return DebuffSources.Count == 0 && coreLoop.Currency.Energy >= Math.Max(0, EnergyCost);
|
||||
}
|
||||
|
||||
public int EnergyCost { get; set; } = energyCost;
|
||||
|
||||
public HashSet<Modifier> DebuffSources { get; } = [];
|
||||
|
||||
public virtual bool Immune { get; }
|
||||
|
||||
public override string ToString() => $"Costs {EnergyCost}" + (DebuffSources.Count > 0 ? ", debuffed" : "");
|
||||
}
|
||||
90
RobotAndDonkey.Game/Intents/Interact.cs
Normal file
90
RobotAndDonkey.Game/Intents/Interact.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Data;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Pois;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class Interact(Cell cell, Card card, EDirection direction) : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
if (cell.Type == ECellType.Dry)
|
||||
{
|
||||
EnergyCost += Balancing.Instance.DryInteractEnergyMalus;
|
||||
}
|
||||
|
||||
var neighbour = cell.Hex.GetNeighbour(Direction);
|
||||
var neighbourIndex = coreLoop.Board.FindCellIndex(neighbour);
|
||||
if (neighbourIndex < 0)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NotFound, card, cell));
|
||||
}
|
||||
else
|
||||
{
|
||||
var neighbourCell = coreLoop.Board.Cells[neighbourIndex];
|
||||
if (neighbourCell.Poi is Crate crate)
|
||||
{
|
||||
var amount = Math.Min(crate.Amount, coreLoop.Currency.MaxCarry - coreLoop.Currency.Carry);
|
||||
if (amount <= 0)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoAmount, card, cell));
|
||||
}
|
||||
else
|
||||
{
|
||||
newIntents.Add(new PickUp(neighbourCell, crate, amount, 0));
|
||||
}
|
||||
}
|
||||
else if (neighbourCell.Poi is Shed shed)
|
||||
{
|
||||
var carrying = coreLoop.Currency.Carry;
|
||||
if (carrying <= 0)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoAmount, card, cell));
|
||||
}
|
||||
else
|
||||
{
|
||||
var toDeliver = Math.Min(carrying, shed.Remaining);
|
||||
var overspill = Math.Max(0, carrying - toDeliver);
|
||||
|
||||
if (toDeliver > 0)
|
||||
{
|
||||
shed.Received += toDeliver;
|
||||
coreLoop.Currency.Carry -= toDeliver;
|
||||
coreLoop.Currency.Delivery += toDeliver;
|
||||
coreLoop.Overspill += overspill;
|
||||
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), shed, neighbourCell.Hex));
|
||||
results.Add(new CurrencyResult(requestId, coreLoop.Currency));
|
||||
results.Add(new ModifyRobotResult(requestId));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (neighbourCell.Poi is Donkey)
|
||||
{
|
||||
newIntents.Add(new DonkeyIntent(card, neighbourCell, 0));
|
||||
}
|
||||
else if (neighbourCell.Poi is Tower tower)
|
||||
{
|
||||
newIntents.Add(new TowerIntent(neighbourCell, tower, false, 0));
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), tower, cell.Hex));
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoTarget, card, cell));
|
||||
}
|
||||
}
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public EDirection Direction { get; set; } = direction;
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop) => base.IsValid(coreLoop) && cell.Poi is Avatar;
|
||||
|
||||
public override string ToString() => $"Interact with {Direction} from {cell}, " + base.ToString();
|
||||
}
|
||||
49
RobotAndDonkey.Game/Intents/ModifyCard.cs
Normal file
49
RobotAndDonkey.Game/Intents/ModifyCard.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Modifiers;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class ModifyCard(Card card, EModifierId modifierId, EModifierDuration duration, ECardLocation location) : CardIntent(card)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
Card.AddModifier(Create(ModifierId, Duration), newIntents);
|
||||
results.Add(new ModifyCardResult(requestId, Card.DeepClone(), ModifierId, location));
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
return base.IsValid(coreLoop) &&
|
||||
ModifierId is EModifierId.Corrupt or EModifierId.Unreliable or EModifierId.RaceCondition or EModifierId.Throttled or EModifierId.Effective or EModifierId.Optimized or EModifierId.Efficient or EModifierId.Persistent &&
|
||||
Card.Modifiers.All(m => m.Id != ModifierId);
|
||||
}
|
||||
|
||||
public static Modifier Create(EModifierId id, EModifierDuration duration)
|
||||
{
|
||||
return id switch
|
||||
{
|
||||
EModifierId.Corrupt => new CorruptCardModifier(duration),
|
||||
EModifierId.Unreliable => new UnreliableCardModifier(duration),
|
||||
EModifierId.RaceCondition => new RaceConditionModifier(duration),
|
||||
EModifierId.Throttled => new ThrottledModifier(duration),
|
||||
EModifierId.Effective => new EffectiveModifier(duration),
|
||||
EModifierId.Optimized => new OptimizedModifier(duration),
|
||||
EModifierId.Efficient => new EfficientModifier(duration),
|
||||
EModifierId.Persistent => new PersistentModifier(duration),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public EModifierId ModifierId { get; set;} = modifierId;
|
||||
|
||||
public EModifierDuration Duration { get; set;} = duration;
|
||||
|
||||
public override string ToString() => $"Modify {Card.Name} with {ModifierId}, {Duration}, " + base.ToString();
|
||||
}
|
||||
38
RobotAndDonkey.Game/Intents/ModifyCell.cs
Normal file
38
RobotAndDonkey.Game/Intents/ModifyCell.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Modifiers;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class ModifyCell(Cell cell, EModifierId modifierId, EModifierDuration duration) : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
Cell.AddModifier(ModifierId switch
|
||||
{
|
||||
EModifierId.Rain => new RainModifier(Duration),
|
||||
EModifierId.Drought => new DroughtModifier(Duration),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
}, newIntents);
|
||||
results.Add(new ModifyCellResult(requestId, new(coreLoop.Board), ModifierId, Cell.Hex));
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
return base.IsValid(coreLoop) && ModifierId is EModifierId.Rain or EModifierId.Drought && Cell.Modifiers.All(m => m.Id != ModifierId);
|
||||
}
|
||||
|
||||
public EModifierId ModifierId { get; set; } = modifierId;
|
||||
|
||||
public EModifierDuration Duration { get; set; } = duration;
|
||||
|
||||
public Cell Cell { get; set; } = cell;
|
||||
|
||||
public override string ToString() => $"Modify {Cell.Hex} with {ModifierId}, {Duration}, " + base.ToString();
|
||||
}
|
||||
40
RobotAndDonkey.Game/Intents/ModifyCurrency.cs
Normal file
40
RobotAndDonkey.Game/Intents/ModifyCurrency.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Modifiers;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class ModifyCurrency(Currency delta, bool canCorrupt = false) : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.Currency.Energy += Delta.Energy;
|
||||
coreLoop.Currency.MaxCarry += Delta.MaxCarry;
|
||||
coreLoop.Currency.Carry += Delta.Carry;
|
||||
coreLoop.Currency.Delivery += Delta.Delivery;
|
||||
coreLoop.Currency.TapeLength += Delta.TapeLength;
|
||||
coreLoop.Currency.HandSize += Delta.HandSize;
|
||||
results.Add(new CurrencyResult(requestId, coreLoop.Currency));
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
return base.IsValid(coreLoop) &&
|
||||
coreLoop.Currency.Energy + Delta.Energy >= 0 &&
|
||||
coreLoop.Currency.MaxCarry + Delta.MaxCarry >= 0 &&
|
||||
coreLoop.Currency.Carry + Delta.Carry >= 0 &&
|
||||
coreLoop.Currency.Delivery + Delta.Delivery >= 0 &&
|
||||
coreLoop.Currency.TapeLength + Delta.TapeLength >= 0 &&
|
||||
coreLoop.Currency.HandSize + Delta.HandSize >= 0;
|
||||
}
|
||||
|
||||
public Currency Delta { get; set; } = delta;
|
||||
|
||||
public bool CanCorrupt { get; set; } = canCorrupt;
|
||||
|
||||
public override string ToString() => $"Currency delta: {Delta}, CanCorrupt={CanCorrupt} " + base.ToString();
|
||||
}
|
||||
35
RobotAndDonkey.Game/Intents/ModifyRobot.cs
Normal file
35
RobotAndDonkey.Game/Intents/ModifyRobot.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Modifiers;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class ModifyRobot(EModifierId modifierId, EModifierDuration duration) : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.Robot.AddModifier(ModifierId switch
|
||||
{
|
||||
EModifierId.Pest => new PestModifier(Duration),
|
||||
EModifierId.Gravity => new GravityModifier(Duration),
|
||||
EModifierId.HeatWave => new HeatWaveModifier(Duration),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
}, newIntents);
|
||||
results.Add(new ModifyRobotResult(requestId));
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
return base.IsValid(coreLoop) &&
|
||||
ModifierId is EModifierId.Pest or EModifierId.Gravity or EModifierId.HeatWave &&
|
||||
coreLoop.Robot.Modifiers.All(m => m.Id != ModifierId);
|
||||
}
|
||||
|
||||
public EModifierId ModifierId { get; set; } = modifierId;
|
||||
|
||||
public EModifierDuration Duration { get; set; } = duration;
|
||||
|
||||
public override string ToString() => $"Modify robot with {ModifierId}, {Duration}, " + base.ToString();
|
||||
}
|
||||
84
RobotAndDonkey.Game/Intents/Move.cs
Normal file
84
RobotAndDonkey.Game/Intents/Move.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Data;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Pois;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class Move(Cell cell, Card card, EDirection direction, bool ignoreBlocking, int amount) : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
if (cell.Type == ECellType.Mud)
|
||||
{
|
||||
newIntents.Add(new ModifyCurrency(new(-Balancing.Instance.MudMoveEnergyCost, 0, 0, 0, 0, 0)));
|
||||
}
|
||||
|
||||
var neighbour = cell.Hex;
|
||||
for (int i = 0; i < Amount; ++i)
|
||||
neighbour = neighbour.GetNeighbour(Direction);
|
||||
|
||||
var neighbourIndex = coreLoop.Board.FindCellIndex(neighbour);
|
||||
if (neighbourIndex < 0)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.OutOfBounds, card, cell));
|
||||
}
|
||||
else
|
||||
{
|
||||
var neighbourCell = coreLoop.Board.Cells[neighbourIndex];
|
||||
if (!ignoreBlocking && (neighbourCell.Type == ECellType.Blocked || neighbourCell.Poi != null))
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.Blocked, card, neighbourCell));
|
||||
}
|
||||
else
|
||||
{
|
||||
var avatar = (Avatar)cell.Poi!;
|
||||
cell.Poi = null;
|
||||
neighbourCell.Poi = avatar;
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), null, cell.Hex));
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), avatar, neighbour));
|
||||
coreLoop.PathLength += Amount;
|
||||
coreLoop.CellsVisited.Add(cell.Hex);
|
||||
coreLoop.CellsVisited.Add(neighbour);
|
||||
if (coreLoop.Currency.Energy == 0)
|
||||
{
|
||||
coreLoop.EnergyWasted += 1;
|
||||
}
|
||||
|
||||
foreach (var modifier in neighbourCell.Modifiers)
|
||||
{
|
||||
if (modifier.DebuffSources.Count > 0)
|
||||
continue;
|
||||
|
||||
switch (modifier.Id)
|
||||
{
|
||||
case EModifierId.Corrupt:
|
||||
case EModifierId.Unreliable:
|
||||
case EModifierId.RaceCondition:
|
||||
{
|
||||
var tape = coreLoop.GetTapeCards();
|
||||
var currentIndex = tape.IndexOf(card);
|
||||
for (int i = currentIndex + 1; i < tape.Count; ++i)
|
||||
newIntents.Add(new ModifyCard(tape[i], modifier.Id, EModifierDuration.Temporary, ECardLocation.Tape));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public EDirection Direction { get; set; } = direction;
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop) => base.IsValid(coreLoop) && cell.Poi is Avatar;
|
||||
|
||||
public int Amount { get; set; } = amount;
|
||||
|
||||
public override string ToString() => $"Move towards {Direction} by {Amount}, " + base.ToString();
|
||||
}
|
||||
73
RobotAndDonkey.Game/Intents/MoveCards.cs
Normal file
73
RobotAndDonkey.Game/Intents/MoveCards.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Data;
|
||||
using RobotAndDonkey.Game.Execution;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class MoveCards(Guid[] orderedCardIds, Guid[] tapeCardIds) : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
var pool = coreLoop.ProgramRow.ToDictionary(card => card.CardId);
|
||||
var orderedCards = new List<Card>(orderedCardIds.Length);
|
||||
foreach (var cardId in orderedCardIds)
|
||||
{
|
||||
if (pool.TryGetValue(cardId, out var card))
|
||||
orderedCards.Add(card);
|
||||
}
|
||||
|
||||
var tapeSet = tapeCardIds.ToHashSet();
|
||||
var tapeCards = orderedCards.Where(card => tapeSet.Contains(card.CardId)).ToList();
|
||||
var handCards = orderedCards.Where(card => !tapeSet.Contains(card.CardId)).ToList();
|
||||
|
||||
var occupiedHandSpace = handCards.Select(c => c.OccupiedSpace).Sum();
|
||||
if (occupiedHandSpace > coreLoop.Currency.HandSize)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoSpace, null, null));
|
||||
return;
|
||||
}
|
||||
|
||||
var occupiedTapeSpace = tapeCards.Select(c => c.OccupiedSpace).Sum();
|
||||
if (occupiedTapeSpace > coreLoop.Currency.TapeLength)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoSpace, null, null));
|
||||
return;
|
||||
}
|
||||
|
||||
coreLoop.SetProgramRow(orderedCards, tapeSet);
|
||||
|
||||
results.Add(new ProgramRowResult(requestId, coreLoop.ProgramRow.ToArray(), coreLoop.TapeCardIds));
|
||||
results.Add(new HandResult(requestId, handCards.ToArray()));
|
||||
results.Add(new TapeResult(requestId, tapeCards.ToArray()));
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!base.IsValid(coreLoop))
|
||||
return false;
|
||||
|
||||
var inputSet = orderedCardIds.ToHashSet();
|
||||
if (inputSet.Count != orderedCardIds.Length)
|
||||
return false;
|
||||
|
||||
var coreLoopSet = coreLoop.ProgramRow.Select(card => card.CardId).ToHashSet();
|
||||
if (!coreLoopSet.SetEquals(inputSet))
|
||||
return false;
|
||||
|
||||
foreach (var cardId in tapeCardIds)
|
||||
{
|
||||
if (!inputSet.Contains(cardId))
|
||||
return false;
|
||||
}
|
||||
|
||||
return coreLoop.RunPhase == ERunPhase.ExecuteProgram;
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => $"Rearrange, Ordered={string.Join(", ", orderedCardIds.Select(c => c.ToString()))}, Tape={string.Join(", ", tapeCardIds.Select(c => c.ToString()))}, " + base.ToString();
|
||||
}
|
||||
29
RobotAndDonkey.Game/Intents/NextGlitch.cs
Normal file
29
RobotAndDonkey.Game/Intents/NextGlitch.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RobotAndDonkey.Game.Execution;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class NextGlitch : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.NextGlitch = (coreLoop.NextGlitch + 1) % coreLoop.GlitchDeck.Length;
|
||||
results.Add(new NextGlitchResult(requestId, coreLoop.NextGlitch));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!base.IsValid(coreLoop))
|
||||
return false;
|
||||
|
||||
return coreLoop.RunPhase == ERunPhase.DrawGlitch;
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => "Next glitch, " + base.ToString();
|
||||
}
|
||||
32
RobotAndDonkey.Game/Intents/NextPhase.cs
Normal file
32
RobotAndDonkey.Game/Intents/NextPhase.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using RobotAndDonkey.Game.Execution;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class NextPhase(ERunPhase phase) : Intent()
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.RunPhase = Phase;
|
||||
results.Add(new RunPhaseResult(requestId, coreLoop.RunPhase));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!base.IsValid(coreLoop))
|
||||
return false;
|
||||
|
||||
if (coreLoop.RunPhase == Phase)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public ERunPhase Phase { get; } = phase;
|
||||
|
||||
public override string ToString() => $"Next phase: {Phase}, " + base.ToString();
|
||||
}
|
||||
37
RobotAndDonkey.Game/Intents/PickUp.cs
Normal file
37
RobotAndDonkey.Game/Intents/PickUp.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Cards.Patches;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Pois;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class PickUp(Cell neighbourCell, Crate crate, int amount, int energyCost) : Intent(energyCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
if (Amount == 0)
|
||||
{
|
||||
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoAmount, null, neighbourCell));
|
||||
}
|
||||
else
|
||||
{
|
||||
coreLoop.Currency.Carry += Amount;
|
||||
crate.Amount -= Amount;
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), crate, neighbourCell.Hex));
|
||||
results.Add(new CurrencyResult(requestId, coreLoop.Currency));
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
return base.IsValid(coreLoop) && Amount <= coreLoop.Currency.MaxCarry - coreLoop.Currency.Carry;
|
||||
}
|
||||
|
||||
public int Amount { get; set; } = amount;
|
||||
|
||||
public override string ToString() => $"Pick up {Amount}, " + base.ToString();
|
||||
}
|
||||
28
RobotAndDonkey.Game/Intents/Reroll.cs
Normal file
28
RobotAndDonkey.Game/Intents/Reroll.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RobotAndDonkey.Game.Execution;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class Reroll(int energyCost) : Intent(energyCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
coreLoop.RerollPatchDeck();
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!base.IsValid(coreLoop))
|
||||
return false;
|
||||
|
||||
return coreLoop.RunPhase == ERunPhase.Improve;
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => "Reroll, " + base.ToString();
|
||||
}
|
||||
24
RobotAndDonkey.Game/Intents/Rest.cs
Normal file
24
RobotAndDonkey.Game/Intents/Rest.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Data;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class Rest(Cell cell, int restoreEnergyAmount) : Intent(-restoreEnergyAmount)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
if (cell.Type == ECellType.Fertile)
|
||||
{
|
||||
EnergyCost -= Balancing.Instance.FertileRestEnergyReplenish;
|
||||
}
|
||||
else if (cell.Type == ECellType.Dry)
|
||||
{
|
||||
EnergyCost = Math.Min(0, EnergyCost + Balancing.Instance.DryRestEnergyMalus);
|
||||
}
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override string ToString() => "Rest, " + base.ToString();
|
||||
}
|
||||
178
RobotAndDonkey.Game/Intents/RunProgram.cs
Normal file
178
RobotAndDonkey.Game/Intents/RunProgram.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Cards;
|
||||
using RobotAndDonkey.Game.Data;
|
||||
using RobotAndDonkey.Game.Execution;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Modifiers;
|
||||
using RobotAndDonkey.Game.Pois;
|
||||
using RobotAndDonkey.Game.Robots;
|
||||
using RobotAndDonkey.Game.Utils;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class RunProgram : Intent
|
||||
{
|
||||
public static IEnumerable<(Entity Entity, ECardLocation Location)> CollectEntities(CoreLoop coreLoop)
|
||||
{
|
||||
yield return (coreLoop.Robot, ECardLocation.Robot);
|
||||
|
||||
foreach (var card in coreLoop.PatchDeck)
|
||||
yield return (card, ECardLocation.Deck);
|
||||
|
||||
foreach (var card in coreLoop.Hand)
|
||||
yield return (card, ECardLocation.Hand);
|
||||
|
||||
foreach (var card in coreLoop.Tape)
|
||||
yield return (card, ECardLocation.Tape);
|
||||
|
||||
foreach (var card in coreLoop.Discard)
|
||||
yield return (card, ECardLocation.Discard);
|
||||
|
||||
foreach (var card in coreLoop.Board.Cells)
|
||||
yield return (card, ECardLocation.Board);
|
||||
}
|
||||
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
var board = coreLoop.Board;
|
||||
|
||||
coreLoop.ProgramsExecuted += 1;
|
||||
results.Add(new ProgramResult(requestId, coreLoop.ProgramsExecuted));
|
||||
|
||||
var modifierStack = new ModifierStack();
|
||||
modifierStack.Push(coreLoop.Robot);
|
||||
|
||||
var tapeCards = coreLoop.GetTapeCards();
|
||||
var minCount = Math.Min(tapeCards.Count, coreLoop.Currency.TapeLength);
|
||||
|
||||
for (var i = 0; i < minCount; i++)
|
||||
{
|
||||
var card = tapeCards[i];
|
||||
if (!card.Modifiers.Any(m => m is { Id: EModifierId.RaceCondition, DebuffSources.Count: 0 }))
|
||||
continue;
|
||||
|
||||
if (coreLoop.Random.NextSingle() > Balancing.Instance.RaceConditionChance)
|
||||
continue;
|
||||
|
||||
var targetIndex = CardExtensions.NextIndexConsideringCorruption(i, coreLoop);
|
||||
if (targetIndex >= 0 && targetIndex < tapeCards.Count)
|
||||
{
|
||||
results.Add(new ModifyCardResult(requestId, tapeCards[targetIndex].DeepClone(), EModifierId.RaceCondition, ECardLocation.Tape));
|
||||
if (coreLoop.SwapTapeCards(i, targetIndex))
|
||||
{
|
||||
(tapeCards[i], tapeCards[targetIndex]) = (tapeCards[targetIndex], tapeCards[i]);
|
||||
results.Add(new ProgramRowResult(requestId, coreLoop.ProgramRow.ToArray(), coreLoop.TapeCardIds));
|
||||
results.Add(new TapeResult(requestId, tapeCards.ToArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tapeCards = coreLoop.GetTapeCards();
|
||||
minCount = Math.Min(tapeCards.Count, coreLoop.Currency.TapeLength);
|
||||
|
||||
for (var i = 0; i < minCount; i++)
|
||||
{
|
||||
var card = tapeCards[i];
|
||||
var avatarCell = board.Cells.Single(c => c.Poi is Avatar);
|
||||
modifierStack.Push(avatarCell);
|
||||
modifierStack.Push(card);
|
||||
|
||||
results.Add(new RunCardResult(requestId, card));
|
||||
var intents = new List<Intent>();
|
||||
card.CreateIntents(avatarCell, coreLoop, requestId, intents, results);
|
||||
modifierStack.Execute(requestId, coreLoop, intents, results, false, true);
|
||||
coreLoop.InstructionsUsed += 1;
|
||||
|
||||
modifierStack.Pop();
|
||||
modifierStack.Pop();
|
||||
}
|
||||
|
||||
modifierStack.Pop();
|
||||
|
||||
static IEnumerable<(Entity Owner, Modifier Modifier, ECardLocation Location)> CollectModifiers(CoreLoop coreLoop, EModifierDuration duration)
|
||||
{
|
||||
foreach (var (entity, location) in CollectEntities(coreLoop))
|
||||
{
|
||||
foreach (var modifier in entity.Modifiers)
|
||||
{
|
||||
if (modifier.Duration == duration)
|
||||
yield return (entity, modifier, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveModifier(Entity entity, Modifier modifier, ECardLocation location)
|
||||
{
|
||||
entity.RemoveModifier(modifier, newIntents);
|
||||
if (entity is Cell cell)
|
||||
results.Add(new ModifyCellResult(requestId, new(coreLoop.Board), modifier.Id, cell.Hex));
|
||||
if (entity is Card card)
|
||||
results.Add(new ModifyCardResult(requestId, card.DeepClone(), modifier.Id, location));
|
||||
if (entity is Robot)
|
||||
results.Add(new ModifyRobotResult(requestId));
|
||||
}
|
||||
|
||||
foreach (var (entity, modifier, location) in CollectModifiers(coreLoop, EModifierDuration.ShortTerm).ToArray())
|
||||
RemoveModifier(entity, modifier, location);
|
||||
|
||||
tapeCards = coreLoop.GetTapeCards();
|
||||
foreach (var card in tapeCards)
|
||||
{
|
||||
var isPersistent = card.Modifiers.Any(m => m is PersistentModifier && m.DebuffSources.Count == 0);
|
||||
|
||||
if (!isPersistent)
|
||||
{
|
||||
coreLoop.Discard.Add(card);
|
||||
coreLoop.RemoveProgramCard(card);
|
||||
}
|
||||
}
|
||||
coreLoop.ClearTapeSelection();
|
||||
coreLoop.ProgramCount -= 1;
|
||||
|
||||
results.Add(new ProgramRowResult(requestId, coreLoop.ProgramRow.ToArray(), coreLoop.TapeCardIds));
|
||||
results.Add(new CurrencyResult(requestId, coreLoop.Currency));
|
||||
results.Add(new HandResult(requestId, coreLoop.Hand.ToArray()));
|
||||
results.Add(new DiscardResult(requestId, coreLoop.Discard.ToArray()));
|
||||
results.Add(new ProgramResult(requestId, coreLoop.ProgramCount));
|
||||
results.Add(new TapeResult(requestId, coreLoop.Tape.ToArray()));
|
||||
results.Add(new DeckResult(requestId, coreLoop.PatchDeck.ToArray()));
|
||||
|
||||
var victory = coreLoop.Board.Cells.Select(c => c.Poi as Shed).Where(s => s != null).Sum(s => s!.Remaining) == 0;
|
||||
if (coreLoop.ProgramCount == 0 || coreLoop.PatchDeck.Count == 0 || coreLoop.Currency.Energy <= 0 || victory)
|
||||
{
|
||||
coreLoop.Currency.Energy += Balancing.Instance.EndOfProgramEnergyReplenish;
|
||||
coreLoop.RunPhase = ERunPhase.Scoring;
|
||||
|
||||
foreach (var (entity, modifier, location) in CollectModifiers(coreLoop, EModifierDuration.Temporary).ToArray())
|
||||
{
|
||||
RemoveModifier(entity, modifier, location);
|
||||
}
|
||||
|
||||
results.Add(new HandResult(requestId, coreLoop.Hand.ToArray()));
|
||||
results.Add(new DiscardResult(requestId, coreLoop.Discard.ToArray()));
|
||||
results.Add(new TapeResult(requestId, coreLoop.Tape.ToArray()));
|
||||
results.Add(new DeckResult(requestId, coreLoop.PatchDeck.ToArray()));
|
||||
|
||||
results.Add(new RunPhaseResult(requestId, coreLoop.RunPhase));
|
||||
}
|
||||
else
|
||||
{
|
||||
coreLoop.DrawHand();
|
||||
}
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (!base.IsValid(coreLoop))
|
||||
return false;
|
||||
|
||||
return coreLoop.RunPhase == ERunPhase.ExecuteProgram;
|
||||
}
|
||||
|
||||
public override bool Immune => true;
|
||||
|
||||
public override string ToString() => "Run program, " + base.ToString();
|
||||
}
|
||||
77
RobotAndDonkey.Game/Intents/TowerIntent.cs
Normal file
77
RobotAndDonkey.Game/Intents/TowerIntent.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Pois;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class TowerIntent(Cell cell, Tower tower, bool active, int energyCost) : Intent(energyCost)
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
var board = coreLoop.Board;
|
||||
|
||||
foreach (var modifier in Cell.Modifiers)
|
||||
{
|
||||
if (modifier.Id != EModifierId.Unreliable)
|
||||
continue;
|
||||
|
||||
if (Active)
|
||||
{
|
||||
modifier.DebuffSources.Remove(Cell);
|
||||
}
|
||||
else
|
||||
{
|
||||
modifier.DebuffSources.Add(Cell);
|
||||
}
|
||||
}
|
||||
|
||||
Tower.Active = Active;
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), Tower, Cell.Hex));
|
||||
|
||||
for (var direction = 0; direction < 6; direction++)
|
||||
{
|
||||
var neighbourHex = Cell.Hex.GetNeighbour((EDirection)direction);
|
||||
var neighbourIndex = board.FindCellIndex(neighbourHex);
|
||||
if (neighbourIndex < 0)
|
||||
continue;
|
||||
|
||||
var neighbourCell = board.Cells[neighbourIndex];
|
||||
foreach (var modifier in neighbourCell.Modifiers)
|
||||
{
|
||||
if (modifier.Id != EModifierId.Unreliable)
|
||||
continue;
|
||||
|
||||
if (Active)
|
||||
{
|
||||
modifier.DebuffSources.Remove(Cell);
|
||||
}
|
||||
else
|
||||
{
|
||||
modifier.DebuffSources.Add(Cell);
|
||||
}
|
||||
results.Add(new ModifyCellResult(requestId, new(coreLoop.Board), EModifierId.Unreliable, neighbourCell.Hex));
|
||||
}
|
||||
}
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop)
|
||||
{
|
||||
if (Tower.Active == Active)
|
||||
return false;
|
||||
|
||||
return base.IsValid(coreLoop);
|
||||
}
|
||||
|
||||
public Cell Cell { get; } = cell;
|
||||
|
||||
public Tower Tower { get; } = tower;
|
||||
|
||||
public bool Active { get; set; } = active;
|
||||
|
||||
public override string ToString() => $"Tower at {Cell.Hex}, Active={Active}, " + base.ToString();
|
||||
}
|
||||
27
RobotAndDonkey.Game/Intents/Turn.cs
Normal file
27
RobotAndDonkey.Game/Intents/Turn.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using RobotAndDonkey.Game.Board;
|
||||
using RobotAndDonkey.Game.Execution.Results;
|
||||
using RobotAndDonkey.Game.GameState;
|
||||
using RobotAndDonkey.Game.Pois;
|
||||
|
||||
namespace RobotAndDonkey.Game.Intents;
|
||||
|
||||
public class Turn(Cell cell, int delta) : Intent
|
||||
{
|
||||
public override void Run(Guid requestId, CoreLoop coreLoop, List<Intent> newIntents, List<Result> results)
|
||||
{
|
||||
var avatar = (Avatar)cell.Poi!;
|
||||
var newDirection = ((int)avatar.Direction + Delta) % 6;
|
||||
if (newDirection < 0)
|
||||
newDirection += 6;
|
||||
avatar.Direction = (EDirection)newDirection;
|
||||
results.Add(new PoiResult(requestId, new(coreLoop.Board), avatar, cell.Hex));
|
||||
|
||||
base.Run(requestId, coreLoop, newIntents, results);
|
||||
}
|
||||
|
||||
public override bool IsValid(CoreLoop coreLoop) => base.IsValid(coreLoop) && cell.Poi is Avatar;
|
||||
|
||||
public int Delta { get; set; } = delta;
|
||||
|
||||
public override string ToString() => $"Turn by {Delta}, " + base.ToString();
|
||||
}
|
||||
Reference in New Issue
Block a user