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,597 @@
using Godot;
using RobotAndDonkey.Game.Cards;
using RobotAndDonkey.Game.Cards.Patches;
using RobotAndDonkey.Game.Modifiers;
using System;
using System.Collections.Generic;
using System.Linq;
using RobotAndDonkey.Game;
public partial class CardControl : Control
{
public override void _Ready()
{
m_ArtTextureRect = GetNode<TextureRect>("%ArtTextureRect");
m_ModifiersContainer = GetNode<HBoxContainer>("%ModifiersContainer");
m_BorderPanel = GetNode<Panel>("%Frame");
m_Background = GetNode<Panel>("%Background");
m_Label = GetNode<Label>("%Label");
MouseEntered += OnMouseEntered;
MouseExited += OnMouseExited;
PivotOffset = Size / 2;
Scale = new(1, 1);
Modulate = new(1, 1, 1);
m_DesiredPosition = Position;
UpdateState();
SnapToDesired();
if (ModifierIconScene == null)
GD.PushWarning($"{nameof(CardControl)} on node '{Name}' has no ModifierIconScene assigned. No modifier icons will be shown.");
if (m_Card == null)
{
m_Card = new DetoxiumPrime();
m_Card.AddModifier(new UnreliableCardModifier(EModifierDuration.Permanent), []);
m_Card.AddModifier(new EffectiveModifier(EModifierDuration.Permanent), []);
m_Card.AddModifier(new RaceConditionModifier(EModifierDuration.Permanent), []);
m_Card.AddModifier(new OptimizedModifier(EModifierDuration.Permanent), []);
}
Refresh();
}
public override void _Process(double delta)
{
UpdateAnimatedState(delta);
}
private void OnMouseEntered()
{
if (m_Dragging || GetViewport().GuiIsDragging())
return;
if (!CanDrag && !CanSelect || Disabled)
return;
if (ShouldHoverSelect())
Selected = true;
m_Hover = true;
ZIndex = 1;
UpdateState();
}
private bool ShouldHoverSelect()
{
if (!CanSelect || Disabled || m_CardRow == null)
return false;
if (!m_CardRow.SupportsMultiSelect)
return false;
if (!Input.IsMouseButtonPressed(MouseButton.Left))
return false;
if (CardRow.IsCardPressActive)
return false;
return true;
}
private void OnMouseExited()
{
m_PressedLeftDown = false;
if (m_Dragging)
return;
if (!CanDrag && !CanSelect || Disabled)
return;
m_DropBefore = null;
m_Hover = false;
ZIndex = 0;
UpdateState();
}
private void UpdateState()
{
Vector2 newPivot = Size / 2;
Vector2 newScale = new Vector2(1, 1);
Color newModulate = new Color(1, 1, 1, 1);
if (Selected)
{
newPivot.Y = Size.Y;
newScale = new(1.25f, 1.25f);
}
if (m_Hover)
{
newScale = new(1.25f, 1.25f);
newModulate = new(1.25f, 1.25f, 1.25f);
}
if (m_DropBefore == false)
{
newPivot.X = Size.X;
newScale = new(1.25f, 1.25f);
newModulate.A = 0.5f;
}
else if (m_DropBefore == true)
{
newPivot.X = 0;
newScale = new(1.25f, 1.25f);
newModulate.A = 0.5f;
}
if (m_Dragging)
{
newModulate.A = 0.25f;
}
if (m_DesiredModulate == newModulate && m_DesiredScale == newScale && m_DesiredPivotOffset == newPivot)
return;
m_DesiredPivotOffset = newPivot;
m_DesiredScale = newScale;
m_DesiredModulate = newModulate;
EmitSignal(SignalName.CardStateChanged, this);
}
public void Configure(Card card, CardRow cardRow)
{
m_Card = card;
m_CardRow = cardRow;
Refresh();
UpdateState();
}
public void Refresh()
{
RefreshRarity();
RefreshArtTexture();
SetDescription(m_Card.Name, m_Card.ToolTip);
SetModifiers(m_Card.Modifiers);
}
private void RefreshArtTexture()
{
SetArtTexture(GD.Load<Texture2D>(m_Card.Id switch
{
ECard.Bitflip => "res://images/bitflip.png",
ECard.ShortCircuit => "res://images/short-circuit.png",
ECard.Slipstream => "res://images/slipstream.png",
ECard.Latency => "res://images/latency.png",
ECard.Rain => "res://images/rain.png",
ECard.Drought => "res://images/drought.png",
ECard.Pest => "res://images/pest.png",
ECard.Gravity => "res://images/gravity.png",
ECard.HeatWave => "res://images/heat-wave.png",
ECard.SolarFlare => "res://images/solar-flare.png",
ECard.LightningStorm => "res://images/lightning-storm.png",
ECard.MeteorStorm => "res://images/meteor-storm.png",
ECard.WindStorm => "res://images/wind-storm.png",
ECard.Move => "res://images/move.png",
ECard.TurnLeft => "res://images/turn-left.png",
ECard.TurnRight => "res://images/turn-right.png",
ECard.Interact => "res://images/interact.png",
ECard.NoOp => "res://images/no-op.png",
ECard.Potentiate => "res://images/potentiate.png",
ECard.Optimize => "res://images/optimize.png",
ECard.Streamline => "res://images/streamline.png",
ECard.Persist => "res://images/persist.png",
ECard.Remember => "res://images/remember.png",
ECard.Reason => "res://images/reason.png",
ECard.DetoxiumPrime => "res://images/detoxium-prime.png",
ECard.FlyingDisk => "res://images/flying-disk.png",
ECard.AluminumHat => "res://images/aluminum-hat.png",
ECard.EMField => "res://images/em-field.png",
ECard.AtomicClock => "res://images/atomic-clock.png",
ECard.Jump => "res://images/jump.png",
ECard.Repeat => "res://images/repeat.png",
ECard.Rest => "res://images/rest.png",
ECard.Stabilize => "res://images/stabilize.png",
_ => throw new ArgumentOutOfRangeException()
}));
}
private void RefreshRarity()
{
var color = m_Card.Rarity switch
{
ERarity.Common => new(1, 1, 1),
ERarity.Magic => new(0.3f, 1, 0.3f),
ERarity.Uncommon => new(0.4f, 0.6f, 1.5f),
ERarity.Rare => new(0.8f, 0.3f, 1),
ERarity.Legendary => new Color(1, 0.5f, 0.2f),
_ => throw new ArgumentOutOfRangeException()
};
SetRarity(color);
}
public void SetRarity(Color color)
{
if (m_BorderPanel == null || m_Background == null)
return;
m_BorderPanel.Modulate = color;
m_Background.Modulate = color;
}
public void SetArtTexture(Texture2D texture)
{
if (m_ArtTextureRect == null)
return;
m_ArtTextureRect.Texture = texture;
}
public void SetDescription(string displayName, string description)
{
if (m_Label != null)
m_Label.Text = displayName;
var header = string.IsNullOrEmpty(displayName) ? string.Empty : displayName.Trim();
var body = string.IsNullOrEmpty(description) ? string.Empty : description.Trim();
if (!string.IsNullOrEmpty(header) && !string.IsNullOrEmpty(body))
TooltipText = $"{header}\n{body}";
else if (!string.IsNullOrEmpty(header))
TooltipText = header;
else
TooltipText = body;
}
public void SetModifiers(IReadOnlyList<Modifier> modifiers)
{
if (m_ModifiersContainer == null)
return;
foreach (var child in m_ModifiersContainer.GetChildren())
{
child.QueueFree();
}
foreach (var modifier in modifiers)
{
if (modifier.DebuffSources.Count > 0)
continue;
var icon = ModifierIconScene.Instantiate<ModifierIcon>();
icon.Configure(modifier);
m_ModifiersContainer.AddChild(icon);
}
}
public void AnimateCard(Tween tween)
{
}
public void SetDesiredPosition(Vector2 desiredPosition)
{
m_DesiredPosition = desiredPosition;
}
public void SnapToDesired()
{
Position = m_DesiredPosition;
PivotOffset = m_DesiredPivotOffset;
Scale = m_DesiredScale;
Modulate = m_DesiredModulate;
}
private void UpdateAnimatedState(double delta)
{
var weight = GetBlendWeight(delta);
if (weight >= 1f)
{
SnapToDesired();
return;
}
Position = Position.Lerp(m_DesiredPosition, weight);
PivotOffset = PivotOffset.Lerp(m_DesiredPivotOffset, weight);
Scale = Scale.Lerp(m_DesiredScale, weight);
Modulate = Modulate.Lerp(m_DesiredModulate, weight);
}
private float GetBlendWeight(double delta)
{
var duration = GetAnimationDuration() * 0.05f;
if (duration <= 0f)
return 1f;
var weight = 1f - Mathf.Exp(-(float)delta / duration);
return Mathf.Clamp(weight, 0f, 1f);
}
private float GetAnimationDuration()
{
if (m_CardRow != null)
return m_CardRow.AnimationDuration;
return 0.15f;
}
public override void _GuiInput(InputEvent @event)
{
if (@event is not InputEventMouseButton mouseButton || mouseButton.ButtonIndex != MouseButton.Left)
return;
AcceptEvent();
if (Disabled)
return;
if (mouseButton.Pressed)
{
m_PressedLeftDown = true;
CardRow.NotifyCardPress(true);
}
else
{
if (m_PressedLeftDown && CanSelect)
Selected = !Selected;
m_PressedLeftDown = false;
CardRow.NotifyCardPress(false);
}
}
public override Variant _GetDragData(Vector2 atPosition)
{
if (!CanDrag || Disabled)
return default;
m_Dragging = true;
UpdateState();
UpdateDragPreviewForDropTarget(null);
return this;
}
public override void _Notification(int what)
{
switch ((long)what)
{
case NotificationDragBegin:
m_DropBefore = null;
UpdateState();
break;
case NotificationDragEnd:
m_Dragging = false;
m_Hover = false;
m_DropBefore = null;
m_DragPreviewRoot = null;
m_DragPreviewCards = null;
UpdateState();
break;
}
}
public override bool _CanDropData(Vector2 atPosition, Variant data)
{
if (!CanDrag || Disabled)
return false;
var dataObject = data.AsGodotObject();
if (dataObject is not CardControl sourceControl || sourceControl == this)
return false;
if (GetParent<CardRow>() is { } cardRow)
{
if (!cardRow._CanDropData(atPosition, data))
return false;
}
sourceControl.UpdateDragPreviewForDropTarget(this);
var size = Size.X / 2;
var before = atPosition.X < size;
if (before != m_DropBefore)
{
GD.Print($"Can drop before={before} {m_Card.Id}");
m_DropBefore = before;
UpdateState();
}
return true;
}
public void UpdateDragPreviewForDropTarget(CardControl targetControl)
{
var draggedCards = GetDraggedCardsForPreview();
if (ShouldForceSinglePreview(targetControl, draggedCards))
SetDragPreviewCards([this]);
else
SetDragPreviewCards(draggedCards);
}
private IReadOnlyList<CardControl> GetDraggedCardsForPreview()
{
if (m_CardRow == null || !m_CardRow.SupportsMultiDrag)
return [this];
var selectedCards = m_CardRow.SelectedCards;
if (selectedCards.Count <= 1)
return [this];
foreach (var selectedCard in selectedCards)
{
if (selectedCard.CardId == Card.CardId)
return selectedCards.Select(c => m_CardRow.CardToControl[c.CardId]).ToList();
}
return [this];
}
private bool ShouldForceSinglePreview(CardControl targetControl, IReadOnlyList<CardControl> draggedCards)
{
if (targetControl == null)
return false;
if (draggedCards.Count <= 1)
return false;
if (!targetControl.Selected)
return false;
foreach (var draggedCard in draggedCards)
{
if (draggedCard == targetControl)
return true;
}
return false;
}
private void SetDragPreviewCards(IReadOnlyList<CardControl> cards)
{
if (m_DragPreviewCards != null && m_DragPreviewCards.SequenceEqual(cards))
return;
m_DragPreviewCards = cards.ToList();
foreach (var card in m_CardRow.CardToControl.Values)
{
card.m_Dragging = false;
card.UpdateState();
}
if (m_DragPreviewRoot != null)
{
foreach (var child in m_DragPreviewRoot.GetChildren())
child.QueueFree();
}
if (cards.Count == 0)
return;
if (cards.Count == 1)
{
m_DragPreviewRoot?.QueueFree();
m_DragPreviewRoot = null;
var cardControl = cards[0];
var previewCard = (CardControl)cardControl.Duplicate();
previewCard.SetAnchorsPreset(LayoutPreset.TopLeft);
previewCard.Configure(cardControl.Card, null);
previewCard.Selected = cardControl.Selected;
previewCard.UpdateState();
previewCard.SetDesiredPosition(previewCard.Position);
previewCard.SnapToDesired();
SetDragPreview(previewCard);
return;
}
if (m_DragPreviewRoot == null)
{
m_DragPreviewRoot = new();
m_DragPreviewRoot.MouseFilter = MouseFilterEnum.Ignore;
}
var spacing = m_CardRow?.CardSpacing ?? 0f;
var offset = Mathf.Max(6f, spacing * 2f);
var offsetVector = new Vector2(offset, offset);
for (var index = 0; index < cards.Count; index++)
{
var cardControl = cards[index];
cardControl.m_Dragging = true;
cardControl.UpdateState();
var previewCard = (CardControl)cardControl.Duplicate();
previewCard.SetAnchorsPreset(LayoutPreset.TopLeft);
previewCard.Configure(cardControl.Card, null);
previewCard.Position = new(offsetVector.X * index, offsetVector.Y * index);
previewCard.Size = Size;
previewCard.UpdateState();
previewCard.SetDesiredPosition(previewCard.Position);
previewCard.SnapToDesired();
previewCard.ZIndex = index + 1;
m_DragPreviewRoot.AddChild(previewCard);
}
m_DragPreviewRoot.Size = Size + offsetVector * Math.Max(0, cards.Count - 1);
SetDragPreview(m_DragPreviewRoot);
}
public override void _DropData(Vector2 atPosition, Variant data)
{
if (!CanDrag)
return;
var obj = data.AsGodotObject();
if (obj is CardControl source)
{
var size = Size.X / 2;
var before = atPosition.X < size;
EmitSignal(SignalName.CardDroppedOn, source, this, before);
}
}
[Export]
public bool CanDrag { get; set; }
[Export]
public bool CanSelect { get; set; }
[Export]
public bool Disabled { get; set; }
[Export]
public bool Selected
{
get => m_Selected;
set
{
if (m_Selected == value)
return;
m_Selected = value;
EmitSignal(SignalName.CardSelectionChanged, this, m_Selected);
UpdateState();
if (m_Selected && m_CardRow != null)
{
Tooltip.Instance.Describe(Card);
CardRow.SelectionContext = m_CardRow;
}
}
}
public CardRow CardRow => m_CardRow;
public Card Card => m_Card;
public Vector2 DesiredScale => m_DesiredScale;
public Vector2 DesiredPivotOffset => m_DesiredPivotOffset;
[Signal]
public delegate void CardDroppedOnEventHandler(CardControl source, CardControl target, bool before);
[Signal]
public delegate void CardSelectionChangedEventHandler(CardControl card, bool selected);
[Signal]
public delegate void CardStateChangedEventHandler(CardControl card);
[Export]
public PackedScene ModifierIconScene { get; set; }
private TextureRect m_ArtTextureRect;
private Panel m_Background;
private Panel m_BorderPanel;
private HBoxContainer m_ModifiersContainer;
private Card m_Card;
private bool m_PressedLeftDown;
private bool m_Dragging;
private bool m_Selected;
private bool m_Hover;
private bool? m_DropBefore;
private CardRow m_CardRow;
private Label m_Label;
private HBoxContainer m_DragPreviewRoot;
private IReadOnlyList<CardControl> m_DragPreviewCards;
private Vector2 m_DesiredPosition;
private Vector2 m_DesiredPivotOffset;
private Vector2 m_DesiredScale;
private Color m_DesiredModulate;
}