diff --git a/Scripts/Board/CellView.cs b/Scripts/Board/CellView.cs
index faa5805..9c53714 100644
--- a/Scripts/Board/CellView.cs
+++ b/Scripts/Board/CellView.cs
@@ -134,4 +134,24 @@ public partial class CellView : Node2D
.SetEase(Tween.EaseType.Out).SetTrans(Tween.TransitionType.Cubic);
tween.TweenCallback(Callable.From(() => _highlight.Visible = false));
}
+
+ ///
+ /// Transformer conversion flash: bright copper pulse + scale punch.
+ /// Uses a distinct color from production so the two phases read apart.
+ ///
+ public void FlashTransform(float duration = 0.45f)
+ {
+ _highlight.Color = new Color(1f, 0.5f, 0.15f, 0.7f); // bright copper
+ _highlight.Visible = true;
+
+ var tween = CreateTween();
+ tween.SetParallel(true);
+ tween.TweenProperty(_highlight, "color", new Color(1f, 0.5f, 0.15f, 0f), duration)
+ .SetEase(Tween.EaseType.Out).SetTrans(Tween.TransitionType.Cubic);
+ tween.TweenProperty(_background, "scale", new Vector2(1.08f, 1.08f), duration * 0.45f)
+ .SetEase(Tween.EaseType.Out);
+ tween.Chain().TweenProperty(_background, "scale", Vector2.One, duration * 0.55f)
+ .SetEase(Tween.EaseType.InOut);
+ tween.Chain().TweenCallback(Callable.From(() => _highlight.Visible = false));
+ }
}
diff --git a/Scripts/Pieces/PieceView.cs b/Scripts/Pieces/PieceView.cs
index 68f61f6..57431e6 100644
--- a/Scripts/Pieces/PieceView.cs
+++ b/Scripts/Pieces/PieceView.cs
@@ -25,6 +25,9 @@ public partial class PieceView : Node2D
private static readonly Color QueenColor = new("#8E3D5A"); // deep burgundy
private static readonly Color WoodCargoColor = new("#A67C32");
private static readonly Color StoneCargoColor = new("#7A7A7A");
+ private static readonly Color ToolsCargoColor = new("#C87533");
+ private static readonly Color ArmsCargoColor = new("#8B0000");
+ private static readonly Color GoldCargoColor = new("#FFD700");
private static readonly Color ShadowColor = new Color(0, 0, 0, 0.18f);
public void Setup(int pieceId, PieceKind kind, Coords startCell, Coords endCell, BoardView boardView)
@@ -136,6 +139,9 @@ public partial class PieceView : Node2D
{
CargoType.Wood => WoodCargoColor,
CargoType.Stone => StoneCargoColor,
+ CargoType.Tools => ToolsCargoColor,
+ CargoType.Arms => ArmsCargoColor,
+ CargoType.Gold => GoldCargoColor,
_ => Colors.White
};
diff --git a/Scripts/Presentation/EventAnimator.cs b/Scripts/Presentation/EventAnimator.cs
index 141ed7b..f58889a 100644
--- a/Scripts/Presentation/EventAnimator.cs
+++ b/Scripts/Presentation/EventAnimator.cs
@@ -80,6 +80,7 @@ public partial class EventAnimator : Node
tween.SetParallel(false);
var produceEvents = new List();
+ var transformerEvents = new List();
var transferEvents = new List();
var moveEvents = new List();
var collisionEvents = new List();
@@ -92,7 +93,7 @@ public partial class EventAnimator : Node
switch (evt)
{
case TurnStartedEvent ts:
- FlushPhases(tween, produceEvents, transferEvents, moveEvents, collisionEvents);
+ FlushPhases(tween, produceEvents, transformerEvents, transferEvents, moveEvents, collisionEvents);
tween.TweenCallback(Callable.From(() => _controlBar.UpdateTurn(ts.TurnNumber)));
break;
@@ -101,8 +102,7 @@ public partial class EventAnimator : Node
break;
case CargoConvertedEvent converted:
- // Visual flash on transformer cell (treat like a produce event for animation)
- produceEvents.Add(new CargoProducedEvent(converted.TurnNumber, converted.TransformerCell, converted.OutputCargo));
+ transformerEvents.Add(converted);
break;
case CargoTransferredEvent:
@@ -119,7 +119,7 @@ public partial class EventAnimator : Node
break;
case MissionCompleteEvent:
- FlushPhases(tween, produceEvents, transferEvents, moveEvents, collisionEvents);
+ FlushPhases(tween, produceEvents, transformerEvents, transferEvents, moveEvents, collisionEvents);
tween.TweenCallback(Callable.From(() =>
{
SfxManager.Instance?.PlayVictory();
@@ -130,7 +130,7 @@ public partial class EventAnimator : Node
break;
case MissionStartedEvent:
- FlushPhases(tween, produceEvents, transferEvents, moveEvents, collisionEvents);
+ FlushPhases(tween, produceEvents, transformerEvents, transferEvents, moveEvents, collisionEvents);
tween.TweenCallback(Callable.From(() =>
{
EmitSignal(SignalName.MissionAdvanced);
@@ -142,7 +142,7 @@ public partial class EventAnimator : Node
break;
case TurnEndedEvent:
- FlushPhases(tween, produceEvents, transferEvents, moveEvents, collisionEvents);
+ FlushPhases(tween, produceEvents, transformerEvents, transferEvents, moveEvents, collisionEvents);
break;
default:
@@ -150,7 +150,7 @@ public partial class EventAnimator : Node
}
}
- FlushPhases(tween, produceEvents, transferEvents, moveEvents, collisionEvents);
+ FlushPhases(tween, produceEvents, transformerEvents, transferEvents, moveEvents, collisionEvents);
tween.TweenCallback(Callable.From(() =>
{
@@ -162,10 +162,29 @@ public partial class EventAnimator : Node
private void FlushPhases(
Tween tween,
List produceEvents,
+ List transformerEvents,
List transferEvents,
List moveEvents,
List collisionEvents)
{
+ // Phase 1a: Transformer conversions — copper flash, distinct from production
+ if (transformerEvents.Count > 0)
+ {
+ var captured = transformerEvents.ToList();
+ tween.TweenCallback(Callable.From(() =>
+ {
+ SfxManager.Instance?.PlayProduce();
+ foreach (var evt in captured)
+ {
+ var cell = _boardView.GetCellView(evt.TransformerCell);
+ cell?.FlashTransform(0.45f);
+ SpawnProduceParticles(evt.TransformerCell, evt.OutputCargo);
+ }
+ }));
+ tween.TweenInterval(0.45f);
+ transformerEvents.Clear();
+ }
+
// Phase 1: Produce — warm golden flash + particle burst
if (produceEvents.Count > 0)
{
diff --git a/docs/PLAN.md b/docs/PLAN.md
index a79e04f..8fe1d18 100644
--- a/docs/PLAN.md
+++ b/docs/PLAN.md
@@ -20,14 +20,12 @@ les surfaces d'interaction et d'animation.
Fou → Dame + 2 transformateurs). La vision GDD/plan prevoit une campagne
plus longue et un final orchestrant toutes les chaines.
-### 2.2 Demandes recurrentes (post-campagne 1)
-Pour forcer la preservation des automatisations entre missions :
-- Une demande consomme N unites par tour.
-- Si plus approvisionnee → etat "en penurie".
-- Condition : aucune demande en penurie pendant X tours consecutifs.
-
-Implique : nouveau champ sur `DemandState`, flag shortage, regle de victoire
-parametrable par mission. A valider via playtest avant d'ajouter au scope.
+### 2.2 Demandes recurrentes — wiring restant
+Le moteur gere le mode recurrent (`ConsumptionPerTurn`, `SustainTurns`,
+shortage tracking, events `DemandShortageStarted/Cleared`). Il reste :
+- Concevoir des missions utilisant le mode recurrent (post-campagne 1).
+- Visualisation UI du shortage (jauge buffer rouge, pulsation d'alerte).
+- Ajuster la condition de fin si un mix classique + recurrent coexiste.
---