diff --git a/Slimepire.sln.DotSettings.user b/Slimepire.sln.DotSettings.user
index 2c38479..4a33232 100644
--- a/Slimepire.sln.DotSettings.user
+++ b/Slimepire.sln.DotSettings.user
@@ -1,5 +1,6 @@
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
\ No newline at end of file
diff --git a/assets/gradient_linear.png b/assets/gradient_linear.png
new file mode 100644
index 0000000..d332608
Binary files /dev/null and b/assets/gradient_linear.png differ
diff --git a/assets/gradient_linear.png.import b/assets/gradient_linear.png.import
new file mode 100644
index 0000000..3198bc4
--- /dev/null
+++ b/assets/gradient_linear.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b2y7oc421yoyd"
+path="res://.godot/imported/gradient_linear.png-f1664a719d29c2fd7ef75dfe3ee8f4a4.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/gradient_linear.png"
+dest_files=["res://.godot/imported/gradient_linear.png-f1664a719d29c2fd7ef75dfe3ee8f4a4.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/assets/metaball.kra b/assets/metaball.kra
new file mode 100644
index 0000000..cf8bf3d
Binary files /dev/null and b/assets/metaball.kra differ
diff --git a/assets/metaball.kra~ b/assets/metaball.kra~
new file mode 100644
index 0000000..40eda39
Binary files /dev/null and b/assets/metaball.kra~ differ
diff --git a/assets/metaball.png b/assets/metaball.png
new file mode 100644
index 0000000..6762a87
Binary files /dev/null and b/assets/metaball.png differ
diff --git a/assets/metaball.png.import b/assets/metaball.png.import
new file mode 100644
index 0000000..ef8dea6
--- /dev/null
+++ b/assets/metaball.png.import
@@ -0,0 +1,41 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://byk86jmfv7p64"
+path.s3tc="res://.godot/imported/metaball.png-a6b8702ab5ec503295d5f109cf071bd1.s3tc.ctex"
+metadata={
+"imported_formats": ["s3tc_bptc"],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://assets/metaball.png"
+dest_files=["res://.godot/imported/metaball.png-a6b8702ab5ec503295d5f109cf071bd1.s3tc.ctex"]
+
+[params]
+
+compress/mode=2
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=true
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=0
diff --git a/assets/metaball.png~ b/assets/metaball.png~
new file mode 100644
index 0000000..1feef2a
Binary files /dev/null and b/assets/metaball.png~ differ
diff --git a/assets/shaders/metaball.gdshader b/assets/shaders/metaball.gdshader
new file mode 100644
index 0000000..9b19aad
--- /dev/null
+++ b/assets/shaders/metaball.gdshader
@@ -0,0 +1,37 @@
+shader_type canvas_item;
+
+varying vec2 quad_uv;
+
+uniform vec2 ball_0;
+uniform vec2 ball_1;
+uniform vec2 ball_2;
+uniform vec2 rect_size = vec2(1920, 1080);
+uniform float ball_radius = 12.0;
+uniform float cam_zoom = 1.0;
+uniform vec2 cam_pos = vec2(0.0);
+uniform float threshold : hint_range(0.0, 1.0) = 0.5;
+uniform vec4 color : source_color = vec4(0.3, 0.5, 0.2, 1.0);
+uniform sampler2D noise_tex : repeat_enable;
+
+void vertex() {
+ quad_uv = UV;
+}
+
+float field(vec2 pos) {
+ return (ball_radius * cam_zoom) / max(length(quad_uv * rect_size - pos), 1.0);
+}
+
+void fragment() {
+ float sum = field(ball_0) + field(ball_1) + field(ball_2);
+ float a = smoothstep(threshold - 0.05, threshold, sum);
+
+ vec2 screen_px = quad_uv * rect_size;
+ vec2 world_px = (screen_px - rect_size * 0.5) / cam_zoom + cam_pos;
+ float n1 = texture(noise_tex, world_px * 0.0005).r;
+ float n2 = texture(noise_tex, world_px * 0.0008 + 0.33).r;
+ float hue_shift = (n1 - 0.5) * 0.12;
+ float value_shift = (n2 - 0.5) * 0.15;
+ vec3 shifted = color.rgb + vec3(hue_shift * 0.5, hue_shift, -hue_shift * 0.5) + value_shift;
+
+ COLOR = vec4(shifted, a * color.a);
+}
diff --git a/assets/shaders/metaball.gdshader.uid b/assets/shaders/metaball.gdshader.uid
new file mode 100644
index 0000000..3b5f419
--- /dev/null
+++ b/assets/shaders/metaball.gdshader.uid
@@ -0,0 +1 @@
+uid://8xu2s3gyxp0v
diff --git a/global.json b/global.json
index f4fd385..4ca0782 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"sdk": {
- "version": "9.0.0",
- "rollForward": "latestMajor",
- "allowPrerelease": true
+ "version": "9.0.0",
+ "rollForward": "latestMajor",
+ "allowPrerelease": true
}
-}
\ No newline at end of file
+}
diff --git a/project.godot b/project.godot
index 45ccd53..c834f92 100644
--- a/project.godot
+++ b/project.godot
@@ -21,8 +21,8 @@ buses/default_bus_layout="uid://djkq0k3jsuxcg"
[display]
-window/size/viewport_width=480
-window/size/viewport_height=320
+window/size/viewport_width=1920
+window/size/viewport_height=1080
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
diff --git a/src/Gameplay/GooSubViewPort.cs b/src/Gameplay/GooSubViewPort.cs
new file mode 100644
index 0000000..b335231
--- /dev/null
+++ b/src/Gameplay/GooSubViewPort.cs
@@ -0,0 +1,65 @@
+using Godot;
+using Slimepire.Levels;
+
+public partial class GooSubViewPort : Node
+{
+ private readonly Vector2[] _worldPositions =
+ [
+ new(400, 300),
+ new(430, 310),
+ new(380, 330),
+ ];
+
+ private ShaderMaterial _material = null!;
+ private ColorRect _colorRect = null!;
+ private DemoLevel _level = null!;
+
+ public override void _Ready()
+ {
+ _level = GetParent();
+ var canvasLayer = _level.GetNode("CanvasLayer");
+ _colorRect = canvasLayer.GetNode("ColorRect");
+
+ _material = new ShaderMaterial
+ {
+ Shader = GD.Load("res://assets/shaders/metaball.gdshader"),
+ };
+ _material.SetShaderParameter("threshold", 0.5f);
+ _material.SetShaderParameter("color", new Color(0.3f, 0.5f, 0.2f, 1f));
+ _material.SetShaderParameter("ball_radius", 12f);
+ _material.SetShaderParameter("rect_size", GetViewport().GetVisibleRect().Size);
+ _material.SetShaderParameter("cam_zoom", 1f);
+ _material.SetShaderParameter("noise_tex", GenerateNoiseTexture(32));
+ _colorRect.Material = _material;
+ }
+
+ public override void _Process(double delta)
+ {
+ var cam = _level.Camera;
+ var viewSize = GetViewport().GetVisibleRect().Size;
+
+ _material.SetShaderParameter("cam_zoom", cam.Zoom.X);
+ _material.SetShaderParameter("cam_pos", cam.GlobalPosition);
+ _material.SetShaderParameter("rect_size", GetViewport().GetVisibleRect().Size);
+
+ for (var i = 0; i < _worldPositions.Length && i < 3; i++)
+ {
+ var screenPos = (_worldPositions[i] - cam.GlobalPosition) * cam.Zoom.X + viewSize / 2;
+ _material.SetShaderParameter($"ball_{i}", screenPos);
+ }
+ }
+
+ private static ImageTexture GenerateNoiseTexture(int size)
+ {
+ var img = Image.CreateEmpty(size, size, false, Image.Format.Rgba8);
+ var rng = new Random(42);
+ for (var y = 0; y < size; y++)
+ for (var x = 0; x < size; x++)
+ {
+ var v = (float)rng.NextDouble();
+ img.SetPixel(x, y, new Color(v, v, v, 1));
+ }
+
+ return ImageTexture.CreateFromImage(img);
+ }
+}
diff --git a/src/Gameplay/GooSubViewPort.cs.uid b/src/Gameplay/GooSubViewPort.cs.uid
new file mode 100644
index 0000000..fe26544
--- /dev/null
+++ b/src/Gameplay/GooSubViewPort.cs.uid
@@ -0,0 +1 @@
+uid://bft481hoodv5d
diff --git a/src/Gameplay/GooSubViewPort.tscn b/src/Gameplay/GooSubViewPort.tscn
new file mode 100644
index 0000000..9589933
--- /dev/null
+++ b/src/Gameplay/GooSubViewPort.tscn
@@ -0,0 +1,6 @@
+[gd_scene format=3 uid="uid://ce2frpb21ct5l"]
+
+[ext_resource type="Script" uid="uid://bft481hoodv5d" path="res://src/Gameplay/GooSubViewPort.cs" id="1_xo53p"]
+
+[node name="GooSubViewPort" type="Node" unique_id=348721885]
+script = ExtResource("1_xo53p")
diff --git a/src/Gameplay/Metaball.cs b/src/Gameplay/Metaball.cs
new file mode 100644
index 0000000..2384cc3
--- /dev/null
+++ b/src/Gameplay/Metaball.cs
@@ -0,0 +1,11 @@
+using System;
+using Godot;
+
+public partial class Metaball : Control
+{
+ // Called when the node enters the scene tree for the first time.
+ public override void _Ready() { }
+
+ // Called every frame. 'delta' is the elapsed time since the previous frame.
+ public override void _Process(double delta) { }
+}
diff --git a/src/Gameplay/Metaball.cs.uid b/src/Gameplay/Metaball.cs.uid
new file mode 100644
index 0000000..caf8364
--- /dev/null
+++ b/src/Gameplay/Metaball.cs.uid
@@ -0,0 +1 @@
+uid://cpj1q44qfwsps
diff --git a/src/Gameplay/NoduleManager.cs b/src/Gameplay/NoduleManager.cs
index bebea10..cbf1eb8 100644
--- a/src/Gameplay/NoduleManager.cs
+++ b/src/Gameplay/NoduleManager.cs
@@ -49,14 +49,13 @@ public partial class NoduleManager
_aStar.ConnectPoints(nodule.NoduleId, neighbor.NoduleId);
}
- // Convert any ghost tubes connected to this nodule to real tubes.
- // Tubes are not always children of this nodule (depends on hash ordering in NoduleConnection),
- // so iterating children alone isn't enough.
+ // Convert ghost tubes where BOTH sides are now fully constructed
foreach (var (conn, tube) in _tubeConnections)
{
if (tube.IsGhost && (ReferenceEquals(conn.A, nodule) || ReferenceEquals(conn.B, nodule)))
{
- tube.SetGhost(false);
+ if (!conn.A.IsConstructing && !conn.B.IsConstructing)
+ tube.SetGhost(false);
}
}
}
diff --git a/src/Gameplay/metaball.tscn b/src/Gameplay/metaball.tscn
new file mode 100644
index 0000000..1116c3f
--- /dev/null
+++ b/src/Gameplay/metaball.tscn
@@ -0,0 +1,32 @@
+[gd_scene format=3 uid="uid://ghg1o288grwq"]
+
+[ext_resource type="Script" uid="uid://cpj1q44qfwsps" path="res://src/Gameplay/Metaball.cs" id="1_jfklr"]
+[ext_resource type="Shader" uid="uid://8xu2s3gyxp0v" path="res://assets/shaders/metaball.gdshader" id="2_vvscw"]
+
+[sub_resource type="ViewportTexture" id="ViewportTexture_vvscw"]
+viewport_path = NodePath("SubViewport")
+
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_vvscw"]
+resource_local_to_scene = true
+shader = ExtResource("2_vvscw")
+shader_parameter/metaball_tex = SubResource("ViewportTexture_vvscw")
+shader_parameter/threshold = 0.9250000439375
+shader_parameter/color = Color(0.3, 0.5, 0.2, 1)
+
+[node name="Metaball" type="Control" unique_id=1102466349]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_jfklr")
+
+[node name="ColorRect" type="ColorRect" parent="." unique_id=1080383949]
+material = SubResource("ShaderMaterial_vvscw")
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
diff --git a/src/Levels/BaseLevel.cs b/src/Levels/BaseLevel.cs
index d8ce27f..bc10711 100644
--- a/src/Levels/BaseLevel.cs
+++ b/src/Levels/BaseLevel.cs
@@ -5,5 +5,5 @@ namespace Slimepire.Levels;
[GlobalClass]
public abstract partial class BaseLevel : Node
{
- protected abstract Camera2D Camera { get; }
+ public abstract Camera2D Camera { get; }
}
diff --git a/src/Levels/DemoLevel.cs b/src/Levels/DemoLevel.cs
index 756f843..e9bb800 100644
--- a/src/Levels/DemoLevel.cs
+++ b/src/Levels/DemoLevel.cs
@@ -5,5 +5,5 @@ namespace Slimepire.Levels;
[SceneTree]
public partial class DemoLevel : BaseLevel
{
- protected override Camera2D Camera => _.Camera;
+ public override Camera2D Camera => _.Camera;
}
diff --git a/src/Levels/DemoLevel.tscn b/src/Levels/DemoLevel.tscn
index 381489b..b3da49c 100644
--- a/src/Levels/DemoLevel.tscn
+++ b/src/Levels/DemoLevel.tscn
@@ -3,6 +3,7 @@
[ext_resource type="Script" uid="uid://c8l12dlpexosv" path="res://src/Levels/DemoLevel.cs" id="1_l2cn0"]
[ext_resource type="Script" uid="uid://cdgi3y0sm3rt1" path="res://src/Gameplay/Camera.cs" id="2_mxa1b"]
[ext_resource type="PackedScene" uid="uid://j15868gtem0y" path="res://src/Gameplay/Nodules/RootNodule/RootNodule.tscn" id="3_knlma"]
+[ext_resource type="PackedScene" uid="uid://ce2frpb21ct5l" path="res://src/Gameplay/GooSubViewPort.tscn" id="4_goo"]
[sub_resource type="LabelSettings" id="LabelSettings_l2cn0"]
font_size = 107
@@ -29,3 +30,15 @@ text = "DEMO LEVEL"
label_settings = SubResource("LabelSettings_l2cn0")
[node name="RootNodule" parent="." unique_id=1797926942 instance=ExtResource("3_knlma")]
+
+[node name="CanvasLayer" type="CanvasLayer" parent="." unique_id=648836414]
+
+[node name="ColorRect" type="ColorRect" parent="CanvasLayer" unique_id=1287004749]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+mouse_filter = 2
+
+[node name="GooSubViewPort" parent="." unique_id=348721885 instance=ExtResource("4_goo")]