You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
4.5 KiB
180 lines
4.5 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Threading.Tasks;
|
|
using Babushka.scripts.CSharp.Common.Util;
|
|
using Godot;
|
|
|
|
namespace Babushka.scripts.CSharp.Common.Minigame;
|
|
|
|
public partial class MinigameController : Node2D
|
|
{
|
|
public class Builder<T>
|
|
{
|
|
internal class Region
|
|
{
|
|
public required T value;
|
|
public float proportion = 1f;
|
|
}
|
|
|
|
public enum DoubleHitHandling
|
|
{
|
|
Allow,
|
|
Disallow,
|
|
Remove,
|
|
}
|
|
|
|
internal List<Region> regions = new();
|
|
internal DoubleHitHandling doubleHitHandling = DoubleHitHandling.Allow;
|
|
internal int maxHitCount = 3;
|
|
|
|
// add region
|
|
public Builder<T> AddRegion(T value)
|
|
{
|
|
regions.Add(new Region { value = value });
|
|
return this;
|
|
}
|
|
|
|
// region settings
|
|
public Builder<T> RegionWithProportion(float proportion)
|
|
{
|
|
if (regions.Count == 0)
|
|
throw new InvalidOperationException("No region to set proportion for");
|
|
|
|
regions.Last().proportion = proportion;
|
|
return this;
|
|
}
|
|
|
|
// general settings
|
|
public Builder<T> WithDoubleHitHandling(DoubleHitHandling handling)
|
|
{
|
|
if (handling != DoubleHitHandling.Allow)
|
|
throw new NotImplementedException();
|
|
|
|
doubleHitHandling = handling;
|
|
return this;
|
|
}
|
|
|
|
public Builder<T> WithHitCount(int hitCount)
|
|
{
|
|
this.maxHitCount = hitCount;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
private TaskCompletionSource? _minigameComplete;
|
|
private List<int>? _hits;
|
|
private float _armPosition = 0f;
|
|
private float _armSpeed = 1f;
|
|
private List<float>? _regions;
|
|
private int _maxHitCount;
|
|
|
|
[Export] private PackedScene _regionVisualPrefab = null!;
|
|
[Export] private Node2D _regionsParent = null!;
|
|
[Export] private Color _baseRegionColor = Colors.Red;
|
|
|
|
[Signal] public delegate void ArmMovedEventHandler(float newPos);
|
|
|
|
public override void _EnterTree()
|
|
{
|
|
HideMinigame();
|
|
}
|
|
|
|
public override void _Process(double delta)
|
|
{
|
|
_armPosition += _armSpeed * (float)delta;
|
|
_armPosition = Mathf.PosMod(_armPosition, 1);
|
|
EmitSignalArmMoved(_armPosition);
|
|
}
|
|
|
|
public async Task<List<T>> Run<T>(Builder<T> builder)
|
|
{
|
|
ShowMinigame();
|
|
Setup(builder);
|
|
await _minigameComplete!.Task;
|
|
var returnValue = _hits!.Select(h => builder.regions[h].value).ToList();
|
|
Reset();
|
|
HideMinigame();
|
|
return returnValue;
|
|
}
|
|
|
|
public void Hit()
|
|
{
|
|
if (_hits == null) return;
|
|
|
|
int i;
|
|
for (i = 0; i < _regions!.Count - 1; i++)
|
|
{
|
|
if (_armPosition < _regions[i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
_hits.Add(i);
|
|
|
|
_armSpeed = -_armSpeed;
|
|
|
|
if (_hits.Count >= _maxHitCount)
|
|
{
|
|
_minigameComplete!.SetResult();
|
|
}
|
|
}
|
|
|
|
private void Setup<T>(Builder<T> builder)
|
|
{
|
|
_minigameComplete = new();
|
|
_hits = [];
|
|
_armPosition = 0f;
|
|
_armSpeed = 1f;
|
|
_regions = [];
|
|
|
|
SetupRegions(builder);
|
|
|
|
_maxHitCount = builder.maxHitCount;
|
|
}
|
|
|
|
private void SetupRegions<T>(Builder<T> builder)
|
|
{
|
|
// common calculations
|
|
var totalRegionProportion = builder.regions.Sum(r => r.proportion);
|
|
|
|
// spawn regions
|
|
var regionSum = 0f;
|
|
foreach (var region in builder.regions)
|
|
{
|
|
var regionVisual = _regionVisualPrefab.Instantiate<RegionVisual>();
|
|
_regionsParent.AddChild(regionVisual);
|
|
|
|
var normalisedAngleStart = regionSum / totalRegionProportion;
|
|
var normalisedAngleEnd = (regionSum + region.proportion) / totalRegionProportion;
|
|
var normalAngles = new Vector2(normalisedAngleStart, normalisedAngleEnd);
|
|
|
|
regionVisual.Setup(normalAngles, _baseRegionColor.RandomHue());
|
|
|
|
regionSum += region.proportion;
|
|
|
|
_regions!.Add(normalisedAngleEnd);
|
|
}
|
|
}
|
|
|
|
private void ShowMinigame()
|
|
{
|
|
Show();
|
|
ProcessMode = ProcessModeEnum.Inherit;
|
|
}
|
|
|
|
private void HideMinigame()
|
|
{
|
|
Hide();
|
|
ProcessMode = ProcessModeEnum.Disabled;
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
_minigameComplete = null;
|
|
_hits = null;
|
|
_regionsParent.GetChildren().ForEach(c => c.QueueFree());
|
|
}
|
|
} |