Added minigame

This commit is contained in:
jonathan
2025-10-02 01:32:52 +02:00
parent fef1bcc3b6
commit 8624e2aea8
19 changed files with 421 additions and 3 deletions
@@ -0,0 +1 @@
uid://dlik4vktiu7dg
@@ -0,0 +1,180 @@
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());
}
}
@@ -0,0 +1 @@
uid://ct7l4er2kljnc
@@ -0,0 +1,19 @@
using Godot;
using System;
public partial class RegionVisual : Node
{
[Export] private Sprite2D _sliceSprite;
public override void _EnterTree()
{
//_sliceSprite.Material = new Material()
}
public void Setup(Vector2 normalAngles, Color color)
{
var mat = (_sliceSprite.Material as ShaderMaterial)!;
mat.SetShaderParameter("angles", normalAngles);
mat.SetShaderParameter("fillColor", color);
}
}
@@ -0,0 +1 @@
uid://cdpsa4qrlai31
@@ -0,0 +1,10 @@
using Godot;
using System;
public partial class SpinnyArmVisual : Node2D
{
public void SetAngle(float normalAngle)
{
Rotation = normalAngle * float.Pi * 2;
}
}
@@ -0,0 +1 @@
uid://djkyrp24ljff0
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
using Babushka.scripts.CSharp.Common.Minigame;
using Godot;
namespace Babushka.scripts.CSharp.Common.TestScripts;
public partial class MinigameTestStarter : Node
{
[Export] private MinigameController _minigameController = null!;
public override void _Ready()
{
_minigameController.Run(
new MinigameController.Builder<int>()
.WithHitCount(5)
.AddRegion(1)
.AddRegion(2).RegionWithProportion(2)
.AddRegion(3)
.AddRegion(4).RegionWithProportion(2)
.AddRegion(5)
.AddRegion(6).RegionWithProportion(2)
).ContinueWith(task => OnEnd(task.Result));
}
private void OnEnd(List<int> result)
{
GD.Print(string.Join(" ,", result));
}
}
@@ -0,0 +1 @@
uid://iv0dbf32bfw1
+16
View File
@@ -0,0 +1,16 @@
using Godot;
namespace Babushka.scripts.CSharp.Common.Util;
public partial class ClickDetect : Area2D
{
[Signal] public delegate void ClickEventHandler();
public override void _Input(InputEvent evt)
{
if (evt is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: true })
{
EmitSignalClick();
}
}
}
@@ -0,0 +1 @@
uid://dq7gahfp0lk7v
+9
View File
@@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Xml.Schema;
using Godot;
namespace Babushka.scripts.CSharp.Common.Util;
public static class LinqExtras
@@ -46,4 +48,11 @@ public static class LinqExtras
}
return selfList;
}
public static Color RandomHue(this Color color)
{
color.ToHsv(out _, out float saturation, out var value );
var rng = new RandomNumberGenerator();
return Color.FromHsv(rng.Randf(), saturation, value);
}
}