ported from perforce

This commit is contained in:
2026-04-19 00:43:27 +02:00
commit 6c0c33f5d4
700 changed files with 19735 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Data;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record AnalystModifier() : Modifier(EModifierKind.Robot, EModifierId.Analytic, EModifierDuration.Permanent)
{
protected override AnalystModifier CreateInstance() => new();
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
for (var i = 0; i < intents.Length; i++)
{
if (intents[i] is DeferGlitch deferGlitch && coreLoop.DeferGlitchCount == 0)
deferGlitch.EnergyCost += Balancing.Instance.AnalysisEnergyDelta;
}
}
public override string ToolTip => $"Reduces the energy cost for deferring a glitch by {Balancing.Instance.AnalysisEnergyDelta}";
}

View File

@@ -0,0 +1,116 @@
using RobotAndDonkey.Game.Cards;
using RobotAndDonkey.Game.Execution;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
using RobotAndDonkey.Game.Pois;
using RobotAndDonkey.Game.Utils;
using Interact = RobotAndDonkey.Game.Intents.Interact;
using Move = RobotAndDonkey.Game.Intents.Move;
using Rest = RobotAndDonkey.Game.Intents.Rest;
namespace RobotAndDonkey.Game.Modifiers;
public record CorruptCellModifier(EModifierDuration Duration) : CorruptModifierBase(EModifierKind.Cell, Duration)
{
protected override CorruptCellModifier CreateInstance() => new(Duration);
public override string ToolTip => "While in this area, instructions will have the opposite effect.";
}
public record CorruptCardModifier(EModifierDuration Duration) : CorruptModifierBase(EModifierKind.Card, Duration)
{
protected override CorruptCardModifier CreateInstance() => new(Duration);
public override string ToolTip => "The instruction will have the opposite effect.";
}
public abstract record CorruptModifierBase(EModifierKind Kind, EModifierDuration Duration) : Modifier(Kind, EModifierId.Corrupt, Duration)
{
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
if (coreLoop.RunPhase != ERunPhase.ExecuteProgram)
return;
foreach (var intent in intents)
{
switch (intent)
{
case Move m:
{
m.Direction = m.Direction.Opposite();
break;
}
case Turn t:
{
t.Delta = -t.Delta;
break;
}
case Rest rest when owner is Card card:
{
rest.DebuffSources.Add(this);
var board = coreLoop.Board;
var avatarCell = board.Cells.First(c => c.Poi is Avatar);
newIntents.Add(new Move(avatarCell, card, ((Avatar)avatarCell.Poi!).Direction.Opposite(), false, 1));
break;
}
case Interact interact:
{
var board = coreLoop.Board;
var avatarCell = board.Cells.First(c => c.Poi is Avatar);
var targetHex = avatarCell.Hex.GetNeighbour(interact.Direction);
var cellIndex = board.FindCellIndex(targetHex);
var cell = cellIndex >= 0 ? board.Cells[cellIndex] : null;
if (cell != null)
{
interact.DebuffSources.Add(this);
if (cell.Poi is Shed)
{
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoTarget, owner as Card, cell));
newIntents.Add(new ModifyCurrency(new(-interact.EnergyCost, 0, 0, 0, 0, 0)));
}
else if (cell.Poi is Crate crate)
{
var carrying = coreLoop.Currency.Carry;
newIntents.Add(new PickUp(cell, crate, -carrying, interact.EnergyCost));
}
else if (cell.Poi == null && coreLoop.HasDonkey && owner is Card card)
newIntents.Add(new DonkeyIntent(card, cell, interact.EnergyCost));
else if (cell.Poi is Tower tower)
newIntents.Add(new TowerIntent(cell, tower, true, interact.EnergyCost));
else
{
results.Add(new InvalidInstructionResult(requestId, EInvalidReason.NoTarget, owner as Card, cell));
newIntents.Add(new ModifyCurrency(new(-interact.EnergyCost, 0, 0, 0, 0, 0)));
}
}
break;
}
case ImmunizeCard { CanCorrupt: true } immunizeCard:
{
immunizeCard.DebuffState = false;
break;
}
case TowerIntent tower:
{
tower.Active = true;
break;
}
case ModifyCurrency { CanCorrupt: true, Delta: { } delta } modifyCurrency:
{
modifyCurrency.Delta = new(delta.Energy * -1, delta.MaxCarry * -1, delta.Carry * -1, delta.Delivery * -1, delta.TapeLength * -1, delta.HandSize * -1);
break;
}
case DetoxiumPrime detoxiumPrime:
{
detoxiumPrime.Heal = false;
break;
}
// #Next: handled in the cards that reference tape order (Repeat / Stabilize)
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Data;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record CourierOverspillModifier() : Modifier(EModifierKind.Robot, EModifierId.CourierOverspill, EModifierDuration.Permanent)
{
protected override CourierOverspillModifier CreateInstance() => new();
public override void After(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
var deliveryPresent = false;
foreach (var intent in intents)
{
if (intent is not ModifyCurrency modifyCurrency)
return;
if (modifyCurrency.Delta is not { Delivery: > 0 })
continue;
deliveryPresent = true;
break;
}
if (deliveryPresent)
newIntents.Add(new ModifyCurrency(new(Balancing.Instance.CourierEnergyReplenishOnDelivery, 0, 0, 0, 0, 0)));
}
public override string ToolTip => $"Replenishes {Balancing.Instance.CourierEnergyReplenishOnDelivery} energy with every delivery";
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Board;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record DroughtModifier(EModifierDuration Duration) : Modifier(EModifierKind.Cell, EModifierId.Drought, Duration)
{
protected override DroughtModifier CreateInstance() => new(Duration);
public override void OnAdded(Entity owner, List<Intent> newIntents)
{
if (owner is not Cell cell)
throw new NotSupportedException("Drought is only allowed in cells.");
cell.Type = cell.Type switch
{
ECellType.Fertile => ECellType.Grass,
ECellType.Grass => ECellType.Dry,
_ => throw new NotSupportedException("Drought only works on Fertile and Grass")
};
}
public override void OnRemoved(Entity owner, List<Intent> newIntents)
{
if (owner is not Cell cell)
throw new NotSupportedException("Drought is only allowed in cells.");
cell.Type = cell.Type switch
{
ECellType.Grass => ECellType.Fertile,
ECellType.Dry => ECellType.Grass,
_ => throw new NotSupportedException("Drought can only be removed from Dry and Grass")
};
}
public override string ToolTip => "Humidity was reduced from this land, reducing fertility and maybe drying out.";
}

View File

@@ -0,0 +1,24 @@
using RobotAndDonkey.Game.Cards;
using RobotAndDonkey.Game.Data;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record EffectiveModifier(EModifierDuration Duration) : Modifier(EModifierKind.Card, EModifierId.Effective, Duration)
{
protected override EffectiveModifier CreateInstance() => new(Duration);
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
if (coreLoop.Random.NextSingle() <= Balancing.Instance.EffectiveChance)
{
if (owner is Card card)
results.Add(new ModifyCardResult(requestId, card.DeepClone(), EModifierId.Effective, ECardLocation.Tape));
newIntents.AddRange(intents);
}
}
public override string ToolTip => $"Has a {Balancing.Instance.EffectiveChance * 100:N0}% chance of working twice";
}

View File

@@ -0,0 +1,12 @@
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record EfficientModifier(EModifierDuration Duration) : Modifier(EModifierKind.Card, EModifierId.Efficient, Duration)
{
protected override EfficientModifier CreateInstance() => new(Duration);
public override string ToolTip => "This instruction consumes no tape space";
}

View File

@@ -0,0 +1,48 @@
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record Entity()
{
protected Entity(Entity clone)
{
m_Modifiers = [];
foreach (var modifier in clone.Modifiers)
AddModifier(modifier.DeepClone(), []);
}
public void InsertModifier(Modifier modifier, List<Intent> newIntents)
{
if (m_Modifiers.Any(m => m.Id == modifier.Id))
throw new InvalidOperationException();
m_Modifiers.Insert(0, modifier);
modifier.OnAdded(this, newIntents);
}
public void AddModifier(Modifier modifier, List<Intent> newIntents)
{
if (m_Modifiers.Any(m => m.Id == modifier.Id))
throw new InvalidOperationException();
m_Modifiers.Add(modifier);
modifier.OnAdded(this, newIntents);
}
public void RemoveModifier(Modifier modifier, List<Intent> newIntents)
{
if (!m_Modifiers.Remove(modifier))
throw new InvalidOperationException();
modifier.OnRemoved(this, newIntents);
}
public override string ToString()
{
return "[" + string.Join(", ", m_Modifiers) + "]";
}
public IReadOnlyList<Modifier> Modifiers => m_Modifiers;
private readonly List<Modifier> m_Modifiers = [];
}

View File

@@ -0,0 +1,75 @@
using RobotAndDonkey.Game.Cards;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record GlobalImmunityModifier(EModifierDuration Duration) : Modifier(EModifierKind.Card, EModifierId.GlobalImmunity, Duration)
{
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
foreach (var (entity, location) in RunProgram.CollectEntities(coreLoop))
{
foreach (var existingModifier in entity.Modifiers)
{
if (existingModifier == this)
continue;
foreach (var (source, immuneModifier) in Modifiers)
{
if (immuneModifier == EModifierId.GlobalImmunity)
continue;
if (immuneModifier == EModifierId._Invalid || immuneModifier == existingModifier.Id)
{
if (existingModifier.DebuffSources.Add(source))
{
if (entity is Card card)
results.Add(new ModifyCardResult(requestId, card.DeepClone(), immuneModifier, location));
}
}
}
}
}
}
public override void After(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
foreach (var (entity, location) in RunProgram.CollectEntities(coreLoop))
{
foreach (var existingModifier in entity.Modifiers)
{
if (existingModifier == this)
continue;
foreach (var (source, immuneModifier) in Modifiers)
{
if (immuneModifier == EModifierId.GlobalImmunity)
continue;
if (immuneModifier == EModifierId._Invalid || immuneModifier == existingModifier.Id)
{
if (existingModifier.DebuffSources.Remove(source))
{
if (entity is Card card)
results.Add(new ModifyCardResult(requestId, card.DeepClone(), immuneModifier, location));
}
}
}
}
}
}
protected override GlobalImmunityModifier CreateInstance()
{
var clone = new GlobalImmunityModifier(Duration);
foreach (var tuple in Modifiers)
clone.Modifiers.Add(tuple);
return clone;
}
public readonly List<(Entity Source, EModifierId Modifier)> Modifiers = [];
public override string ToolTip => $"Immune against {string.Join(", ", Modifiers.Select(m => m.Modifier))}, {Duration}";
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Data;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record GravityModifier(EModifierDuration Duration) : Modifier(EModifierKind.Robot, EModifierId.Gravity, Duration)
{
protected override GravityModifier CreateInstance() => new(Duration);
public override void OnAdded(Entity owner, List<Intent> newIntents)
{
newIntents.Add(new ModifyCurrency(new(0, -Balancing.Instance.GravityMaxCarryPenalty, 0, 0, 0, 0)));
}
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
foreach (var intent in intents)
{
if (intent is Move)
intent.EnergyCost += Balancing.Instance.GravityExtraMoveCost;
}
}
public override void OnRemoved(Entity owner, List<Intent> newIntents)
{
newIntents.Add(new ModifyCurrency(new(0, Balancing.Instance.GravityMaxCarryPenalty, 0, 0, 0, 0)));
}
public override string ToolTip => $"Gravity: {Balancing.Instance.GravityMaxCarryPenalty} Max Carry; Move costs +{Balancing.Instance.GravityExtraMoveCost} energy (temporary).";
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Data;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record HeatWaveModifier(EModifierDuration Duration) : Modifier(EModifierKind.Robot, EModifierId.HeatWave, Duration)
{
protected override HeatWaveModifier CreateInstance() => new(Duration);
public override void OnAdded(Entity owner, List<Intent> newIntents)
{
newIntents.Add(new ModifyCurrency(new(-Balancing.Instance.HeatWaveEnergyPenalty, 0, 0, 0, 0, 0)));
}
public override void OnRemoved(Entity owner, List<Intent> newIntents)
{
newIntents.Add(new ModifyCurrency(new(Balancing.Instance.HeatWaveEnergyPenalty, 0, 0, 0, 0, 0)));
}
public override string ToolTip => $"Heat wave: immediate {Balancing.Instance.HeatWaveEnergyPenalty} Energy (temporary).";
}

View File

@@ -0,0 +1,43 @@
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public abstract record Modifier(EModifierKind Kind, EModifierId Id, EModifierDuration Duration)
{
public Modifier DeepClone()
{
var result = CreateInstance();
foreach (var debuffSource in DebuffSources)
result.DebuffSources.Add(debuffSource);
return result;
}
protected abstract Modifier CreateInstance();
public virtual void OnAdded(Entity owner, List<Intent> newIntents)
{
}
public virtual void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
}
public virtual void After(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
}
public virtual void OnRemoved(Entity owner, List<Intent> newIntents)
{
}
public override string ToString()
{
return "<" + $"{Id}, {Duration}" + (DebuffSources.Count > 0 ? ", debuffed" : "") + ">";
}
public HashSet<Entity> DebuffSources { get; } = [];
public abstract string ToolTip { get; }
}

View File

@@ -0,0 +1,79 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public class ModifierStack
{
public void Push(Entity entity)
{
m_Stack.Push((entity, entity.Modifiers));
}
public void Pop()
{
_ = m_Stack.Pop();
}
private IEnumerable<(Entity Owner, Modifier Modifier)> GetModifiers()
{
foreach (var (owner, modifiers) in m_Stack)
{
foreach (var modifier in modifiers)
{
if (modifier.DebuffSources.Count == 0)
yield return (owner, modifier);
}
}
}
public void Execute(Guid requestId, CoreLoop coreLoop, List<Intent> intents, List<Result> results, bool force, bool verbose)
{
var newIntents = new List<Intent>();
var index = 0;
do
{
var currentIntents = intents[index..];
var modifiableIntents = currentIntents.Where(i => !i.Immune).ToArray();
foreach (var (owner, modifier) in GetModifiers())
{
if (modifier.DebuffSources.Count == 0)
{
if (verbose)
Debug.WriteLine($"Before {modifier}");
modifier.Before(requestId, modifiableIntents, coreLoop, owner, newIntents, results);
}
}
foreach (var intent in currentIntents)
{
var isValid = intent.IsValid(coreLoop);
if (force || isValid)
{
if (verbose)
Debug.WriteLine($"Run {(isValid ? "Valid" : "Invalid")} intent: {intent}");
intent.Run(requestId, coreLoop, newIntents, results);
}
}
foreach (var (owner, modifier) in GetModifiers())
{
if (modifier.DebuffSources.Count == 0)
{
if (verbose)
Debug.WriteLine($"After {modifier}");
modifier.After(requestId, modifiableIntents, coreLoop, owner, newIntents, results);
}
}
index = intents.Count;
intents.AddRange(newIntents);
newIntents.Clear();
} while (index < intents.Count);
}
private readonly Stack<(Entity Owner, IReadOnlyList<Modifier> Modifiers)> m_Stack = [];
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Execution;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record OptimizedModifier(EModifierDuration Duration) : Modifier(EModifierKind.Card, EModifierId.Optimized, Duration)
{
protected override OptimizedModifier CreateInstance() => new(Duration);
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
foreach (var intent in intents)
{
if (intent is CardCostIntent cardCostIntent)
cardCostIntent.EnergyCost -= 1;
}
}
public override string ToolTip => "Energy cost reduced by 1";
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record PersistentModifier(EModifierDuration Duration) : Modifier(EModifierKind.Card, EModifierId.Persistent, Duration)
{
protected override PersistentModifier CreateInstance() => new(Duration);
public override string ToolTip => "This instruction is not discarded after execution.";
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Data;
using RobotAndDonkey.Game.Execution;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record PestModifier(EModifierDuration Duration) : Modifier(EModifierKind.Robot, EModifierId.Pest, Duration)
{
protected override PestModifier CreateInstance() => new(Duration);
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
foreach (var intent in intents)
{
if (intent is ModifyCurrency { Delta.Delivery: > 0 } modifyCurrency)
{
var original = modifyCurrency.Delta;
var reduced = (int)MathF.Floor(original.Delivery * Balancing.Instance.PestDeliveryMultiplier);
modifyCurrency.Delta = original with { Delivery = reduced };
}
}
}
public override string ToolTip => $"Deliveries are reduced to {Balancing.Instance.PestDeliveryMultiplier * 100:N0}% (temporary).";
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Cards;
using RobotAndDonkey.Game.Data;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
using RobotAndDonkey.Game.Utils;
namespace RobotAndDonkey.Game.Modifiers;
public record RaceConditionModifier(EModifierDuration Duration) : Modifier(EModifierKind.Card, EModifierId.RaceCondition, Duration)
{
protected override RaceConditionModifier CreateInstance() => new(Duration);
public override string ToolTip => $"Has a {Balancing.Instance.RaceConditionChance * 100:N0}% chance of switching places with the next card instead of executing.";
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Board;
using RobotAndDonkey.Game.Execution;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record RainModifier(EModifierDuration Duration) : Modifier(EModifierKind.Cell, EModifierId.Rain, Duration)
{
protected override RainModifier CreateInstance() => new(Duration);
public override void OnAdded(Entity owner, List<Intent> newIntents)
{
if (owner is not Cell cell)
throw new NotSupportedException("Rain is only allowed in cells.");
cell.Type = cell.Type switch
{
ECellType.Grass => ECellType.Mud,
ECellType.Rocky => ECellType.Dry,
_ => throw new NotSupportedException("Rain only works on Rocky and Grass")
};
}
public override void OnRemoved(Entity owner, List<Intent> newIntents)
{
if (owner is not Cell cell)
throw new NotSupportedException("Rain is only allowed in cells.");
cell.Type = cell.Type switch
{
ECellType.Mud => ECellType.Grass,
ECellType.Dry => ECellType.Rocky,
_ => throw new NotSupportedException("Rain can only be removed from Dry and Mud")
};
}
public override string ToolTip => "Humidity was reduced from this land, reducing fertility and maybe drying out.";
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RobotAndDonkey.Game.Board;
using RobotAndDonkey.Game.Data;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
using RobotAndDonkey.Game.Pois;
namespace RobotAndDonkey.Game.Modifiers;
public record RangerFertileRestModifier() : Modifier(EModifierKind.Robot, EModifierId.RangerFertileRest, EModifierDuration.Permanent)
{
protected override RangerFertileRestModifier CreateInstance() => new();
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
var avatarCell = coreLoop.Board.Cells.First(c => c.Poi is Avatar);
if (avatarCell.Type != ECellType.Fertile)
return;
foreach (var intent in intents)
{
if (intent is ModifyCurrency { Delta.Energy: > 0 })
{
newIntents.Add(new ModifyCurrency(new(Balancing.Instance.RangerFertileRestEnergyDelta, 0, 0, 0, 0, 0)));
break;
}
}
}
public override string ToolTip => $"Replenishes an extra {Balancing.Instance.RangerFertileRestEnergyDelta} energy when resting on fertile ground";
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using RobotAndDonkey.Game.Execution;
using RobotAndDonkey.Game.Execution.Results;
using RobotAndDonkey.Game.GameState;
using RobotAndDonkey.Game.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record ThrottledModifier(EModifierDuration Duration) : Modifier(EModifierKind.Card, EModifierId.Throttled, Duration)
{
protected override ThrottledModifier CreateInstance() => new(Duration);
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
foreach (var intent in intents)
{
if (intent is CardCostIntent cardCostIntent)
cardCostIntent.EnergyCost += 1;
}
}
public override string ToolTip => "Increases the energy cost by 1";
}

View File

@@ -0,0 +1,49 @@
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.Intents;
namespace RobotAndDonkey.Game.Modifiers;
public record UnreliableCardModifier(EModifierDuration Duration) : UnreliableModifierBase(EModifierKind.Card, Duration)
{
protected override UnreliableCardModifier CreateInstance() => new(Duration);
public override string ToolTip => $"Has a {Balancing.Instance.UnreliableChance * 100:N0}% chance of doing nothing";
}
public record UnreliableCellModifier(EModifierDuration Duration) : UnreliableModifierBase(EModifierKind.Cell, Duration)
{
protected override UnreliableCellModifier CreateInstance() => new(Duration);
public override string ToolTip => $"While in this area, instructions have a {Balancing.Instance.UnreliableChance * 100:N0}% chance of doing nothing";
}
public abstract record UnreliableModifierBase(EModifierKind Kind, EModifierDuration Duration) : Modifier(Kind, EModifierId.Unreliable, Duration)
{
public override void Before(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
if (coreLoop.RunPhase != ERunPhase.ExecuteProgram)
return;
if (coreLoop.Random.NextSingle() <= Balancing.Instance.UnreliableChance)
{
if (owner is Card card)
results.Add(new ModifyCardResult(requestId, card.DeepClone(), EModifierId.Unreliable, ECardLocation.Tape));
if (owner is Cell cell)
results.Add(new ModifyCellResult(requestId, new(coreLoop.Board), EModifierId.Unreliable, cell.Hex));
foreach (var intent in intents)
intent.DebuffSources.Add(this);
}
}
public override void After(Guid requestId, ReadOnlySpan<Intent> intents, CoreLoop coreLoop, Entity owner, List<Intent> newIntents, List<Result> results)
{
foreach (var intent in intents)
intent.DebuffSources.Remove(this);
}
}