This commit is contained in:
2026-06-17 09:45:25 +02:00
commit cca539dc32
40 changed files with 831 additions and 0 deletions

6
src/Core/GameInputMap.cs Normal file
View File

@@ -0,0 +1,6 @@
using Godot;
namespace Slimepire.Core;
[InputMap]
public partial class GameInputMap { }

View File

@@ -0,0 +1 @@
uid://cta7vf1tc8mrp

40
src/Core/MainGame.cs Normal file
View File

@@ -0,0 +1,40 @@
using Godot;
using Slimepire.Levels;
namespace Slimepire.Core;
[SceneTree]
public partial class MainGame : Node
{
private BaseLevel? _currentLevel;
public override void _Ready()
{
GD.Print("MainGame ready");
CallDeferred(nameof(LoadLevel), "res://src/Levels/DemoLevel.tscn");
}
private async void LoadLevel(string level)
{
_currentLevel?.QueueFree();
_currentLevel = null;
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
var newLevelPacked = ResourceLoader.Load<PackedScene>(level);
if (newLevelPacked == null)
{
GD.PushError($"Could not load packed scene {level}");
return;
}
_currentLevel = newLevelPacked.Instantiate<BaseLevel>();
if (_currentLevel == null)
{
GD.PushError($"Could not load level {level}");
return;
}
LevelRoot.AddChild(_currentLevel);
}
}

1
src/Core/MainGame.cs.uid Normal file
View File

@@ -0,0 +1 @@
uid://b0u4agvdxawp

62
src/Core/MainGame.tscn Normal file
View File

@@ -0,0 +1,62 @@
[gd_scene format=3 uid="uid://drxbsnqoi4wmw"]
[ext_resource type="Script" uid="uid://b0u4agvdxawp" path="res://src/Core/MainGame.cs" id="1_g6xmk"]
[node name="Main Game" type="Node" unique_id=1541697061]
process_mode = 3
script = ExtResource("1_g6xmk")
[node name="Systems" type="Node" parent="." unique_id=71014693]
[node name="World" type="Node2D" parent="." unique_id=436280701]
process_mode = 1
[node name="LevelRoot" type="Node2D" parent="World" unique_id=1066129272]
unique_name_in_owner = true
[node name="EntityRoot" type="Node2D" parent="World" unique_id=706944944]
unique_name_in_owner = true
[node name="HudLayer" type="CanvasLayer" parent="." unique_id=407027346]
process_mode = 1
layer = 10
[node name="HudRoot" type="Control" parent="HudLayer" unique_id=428309734]
unique_name_in_owner = true
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
[node name="PauseLayer" type="CanvasLayer" parent="." unique_id=72018978]
process_mode = 2
layer = 20
[node name="PauseRoot" type="Control" parent="PauseLayer" unique_id=171929713]
unique_name_in_owner = true
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
[node name="TransitionLayer" type="CanvasLayer" parent="." unique_id=1737536414]
process_mode = 3
layer = 100
[node name="TransitionRoot" type="Control" parent="TransitionLayer" unique_id=78768800]
unique_name_in_owner = true
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
[node name="DebugLayer" type="CanvasLayer" parent="." unique_id=1550774256]
process_mode = 3
layer = 128
[node name="DebugRoot" type="Control" parent="DebugLayer" unique_id=1697594173]
unique_name_in_owner = true
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0

View File

@@ -0,0 +1,39 @@
using Godot;
namespace Slimepire.Gameplay;
[SceneTree]
public partial class BuildGhost : Node2D
{
public event Action<Vector2>? OnBuild;
private Tube? _tube;
public override void _Ready()
{
_tube = Instantiator.Instantiate<Tube>();
_tube.Position = Position / 2; // TODO WHYYY????
GetParent()?.AddChild(_tube);
}
public override void _Process(double delta)
{
GlobalPosition = GetGlobalMousePosition();
_tube?.SetTarget(GlobalPosition);
}
public override void _UnhandledInput(InputEvent @event)
{
using (@event)
{
if (@event is InputEventMouseButton { Pressed: false, ButtonIndex: MouseButton.Left })
{
OnBuild?.Invoke(GlobalPosition);
_tube?.QueueFree();
QueueFree();
GetViewport().SetInputAsHandled();
}
}
}
}

View File

@@ -0,0 +1 @@
uid://ck5yw760bu8sb

View File

@@ -0,0 +1,12 @@
[gd_scene format=3 uid="uid://c3n70a52kk34f"]
[ext_resource type="Script" uid="uid://ck5yw760bu8sb" path="res://src/Gameplay/BuildGhost.cs" id="1_ahcoi"]
[ext_resource type="Texture2D" uid="uid://ddduid7esr36f" path="res://assets/icon.svg" id="2_uo42j"]
[node name="BuildGhost" type="Node2D" unique_id=150653292]
script = ExtResource("1_ahcoi")
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=218448453]
modulate = Color(0.32941177, 0.29411766, 0.2901961, 0.5764706)
scale = Vector2(0.3, 0.3)
texture = ExtResource("2_uo42j")

27
src/Gameplay/Camera.cs Normal file
View File

@@ -0,0 +1,27 @@
using Godot;
using Slimepire.Core;
namespace Slimepire.Gameplay;
public partial class Camera : Camera2D
{
[Export]
public float CameraSpeed = 500;
private Vector2 _direction;
public override void _UnhandledKeyInput(InputEvent @event)
{
_direction = Input.GetVector(
GameInputMap.CamLeft,
GameInputMap.CamRight,
GameInputMap.CamUp,
GameInputMap.CamDown
);
}
public override void _Process(double delta)
{
Position += _direction * CameraSpeed * (float)delta;
}
}

View File

@@ -0,0 +1 @@
uid://cdgi3y0sm3rt1

View File

@@ -0,0 +1,96 @@
using Godot;
namespace Slimepire.Gameplay.Nodules;
[SceneTree]
public partial class Nodule : Node2D
{
[Export]
public bool IsRoot;
private BuildGhost? _ghost;
private bool _mouseInsideClickArea;
private bool _clickedInside;
public readonly List<Nodule> Connections = [];
// todo remove?
private bool _isExpanding;
public override void _Ready()
{
ClickArea.MouseEntered += ClickAreaOnMouseEntered;
ClickArea.MouseExited += ClickAreaOnMouseExited;
TubeConnectionArea.AreaEntered += TubeConnectionAreaOnAreaEntered;
TubeConnectionArea.AreaExited += TubeConnectionAreaOnAreaExited;
TubeManager.Instance.RegisterNodule(this);
}
private void TubeConnectionAreaOnAreaEntered(Area2D area)
{
if (area.GetParent() is Nodule nodule)
{
Connections.Add(nodule);
TubeManager.Instance.Update();
}
}
private void TubeConnectionAreaOnAreaExited(Area2D area)
{
if (area.GetParent() is Nodule nodule)
{
Connections.Remove(nodule);
}
}
public override void _UnhandledInput(InputEvent @event)
{
using (@event)
{
if (@event is InputEventMouseButton && @event.IsPressed() && _mouseInsideClickArea)
{
_clickedInside = true;
GetViewport().SetInputAsHandled();
}
}
}
private void ClickAreaOnMouseEntered()
{
_mouseInsideClickArea = true;
}
private void ClickAreaOnMouseExited()
{
_mouseInsideClickArea = false;
// ghost == null check probably unnecessary
if (_clickedInside && _ghost == null)
{
_clickedInside = false;
_isExpanding = true;
_ghost = Instantiator.Instantiate<BuildGhost>();
_ghost.Position = Position;
_ghost.OnBuild += OnBuildComplete;
GetParent()?.AddChild(_ghost);
}
}
private void OnBuildComplete(Vector2 pos)
{
_isExpanding = false;
_ghost = null;
var nodule = Instantiator.Instantiate<Nodule>();
nodule.Position = pos;
Connections.Add(nodule);
nodule.Connections.Add(this);
GetParent()?.AddChild(nodule);
}
}

View File

@@ -0,0 +1 @@
uid://cmpl0if164dba

View File

@@ -0,0 +1,64 @@
[gd_scene format=3 uid="uid://cnl6t8o4mh0j3"]
[ext_resource type="Script" uid="uid://cmpl0if164dba" path="res://src/Gameplay/Nodules/Nodule.cs" id="1_yembh"]
[ext_resource type="Texture2D" uid="uid://cecg47efx7gw8" path="res://assets/Sprite-0001.png" id="2_7qt20"]
[sub_resource type="CircleShape2D" id="CircleShape2D_7qt20"]
radius = 15.0
[sub_resource type="AtlasTexture" id="AtlasTexture_7qt20"]
atlas = ExtResource("2_7qt20")
region = Rect2(32, 0, 32, 32)
[sub_resource type="AtlasTexture" id="AtlasTexture_x44k2"]
atlas = ExtResource("2_7qt20")
region = Rect2(0, 0, 32, 32)
[sub_resource type="AtlasTexture" id="AtlasTexture_inlcu"]
atlas = ExtResource("2_7qt20")
region = Rect2(64, 0, 32, 32)
[sub_resource type="SpriteFrames" id="SpriteFrames_xyjsl"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": SubResource("AtlasTexture_7qt20")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_x44k2")
}, {
"duration": 1.0,
"texture": SubResource("AtlasTexture_inlcu")
}],
"loop": true,
"name": &"default",
"speed": 2.0
}]
[sub_resource type="CircleShape2D" id="CircleShape2D_x44k2"]
radius = 96.38
[node name="Nodule" type="Node2D" unique_id=1637542829]
z_index = 1
script = ExtResource("1_yembh")
[node name="ClickArea" type="Area2D" parent="." unique_id=120357381]
unique_name_in_owner = true
[node name="CollisionShape2D" type="CollisionShape2D" parent="ClickArea" unique_id=2005066434]
shape = SubResource("CircleShape2D_7qt20")
[node name="Blob" type="AnimatedSprite2D" parent="." unique_id=190397869]
modulate = Color(0.34855, 0.6165254, 0.5102762, 1)
texture_filter = 1
sprite_frames = SubResource("SpriteFrames_xyjsl")
autoplay = "default"
frame = 1
frame_progress = 0.9100785
[node name="TubeConnectionArea" type="Area2D" parent="." unique_id=339547035]
unique_name_in_owner = true
[node name="CollisionShape2D" type="CollisionShape2D" parent="TubeConnectionArea" unique_id=234655540]
shape = SubResource("CircleShape2D_x44k2")
debug_color = Color(0.38922516, 0.6054754, 0.25354394, 0.41960785)

View File

@@ -0,0 +1,9 @@
extends Node2D
var clicked_inside := false
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed == true:
clicked_inside = true
get_viewport().set_input_as_handled()

View File

@@ -0,0 +1 @@
uid://bvl0487nflq0q

47
src/Gameplay/Tube.cs Normal file
View File

@@ -0,0 +1,47 @@
using Godot;
namespace Slimepire.Gameplay;
[SceneTree]
[Tool]
public partial class Tube : Node2D
{
[ExportToolButton("Generate Tube")]
private Callable GenerateTubeButton => Callable.From(GenerateTube);
private void GenerateTube()
{
float maxLength = 200;
// Generate 5 points from origin to Target
var diff = Target.Position - Position;
var points = new Vector2[5];
points[0] = Position;
for (var i = 1; i < 4; i++)
{
points[i] = points[i - 1] + diff / 5;
}
points[4] = Target.Position;
var thickness = (-1 / (maxLength / 2f)) * diff.Length() + 2f;
Line2D.Points = points;
Line2D.WidthCurve = new Curve();
Line2D.WidthCurve.AddPoint(new Vector2(0, 1));
Line2D.WidthCurve.AddPoint(new Vector2(0.5f, thickness));
Line2D.WidthCurve.AddPoint(new Vector2(1, 1));
Line2D.WidthCurve.Bake();
}
public void SetTarget(Vector2 newTarget)
{
Target.GlobalPosition = newTarget;
GenerateTube();
}
public override void _Ready()
{
Line2D.Points = [];
}
}

1
src/Gameplay/Tube.cs.uid Normal file
View File

@@ -0,0 +1 @@
uid://dwunaa303epy6

25
src/Gameplay/Tube.tscn Normal file
View File

@@ -0,0 +1,25 @@
[gd_scene format=3 uid="uid://b3nn42ralc31r"]
[ext_resource type="Script" uid="uid://dwunaa303epy6" path="res://src/Gameplay/Tube.cs" id="1_76hpl"]
[sub_resource type="Curve" id="Curve_0lkqj"]
_limits = [0.0, 2.0, 0.0, 1.0]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.5, 1.4598354), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
point_count = 3
[node name="Tube" type="Node2D" unique_id=226677696]
script = ExtResource("1_76hpl")
[node name="Line2D" type="Line2D" parent="." unique_id=1553055261]
unique_name_in_owner = true
z_as_relative = false
width = 8.0
width_curve = SubResource("Curve_0lkqj")
default_color = Color(0.30588236, 0.4862745, 0.23921569, 0.5568628)
joint_mode = 2
begin_cap_mode = 2
end_cap_mode = 2
[node name="Target" type="Marker2D" parent="." unique_id=630564540]
unique_name_in_owner = true
position = Vector2(47, 66)

View File

@@ -0,0 +1,98 @@
using Godot;
using Slimepire.Gameplay.Nodules;
namespace Slimepire.Gameplay;
[Singleton]
public partial class TubeManager
{
private Nodule? _root;
private List<Nodule> _nodules = [];
private HashSet<NoduleConnection> _connections = [];
private Dictionary<NoduleConnection, Tube> _tubeConnections = new();
private List<Tube> _tubes = [];
public void RegisterNodule(Nodule nodule)
{
if (nodule.IsRoot)
{
_root = nodule;
}
_nodules.Add(nodule);
UpdateNoduleConnections();
UpdateTubes();
}
public void Update()
{
GD.Print("UPDATE");
UpdateNoduleConnections();
UpdateTubes();
}
private void UpdateNoduleConnections()
{
foreach (var n in _nodules)
{
foreach (var c in n.Connections)
{
_connections.Add(new NoduleConnection(n, c));
}
}
GD.Print($"Connections: {_connections.Count}");
}
private void UpdateTubes()
{
foreach (var c in _connections)
{
if (!_tubeConnections.ContainsKey(c))
{
GD.Print("Hello?");
var tube = Instantiator.Instantiate<Tube>();
_tubes.Add(tube);
_tubeConnections.Add(c, tube);
// tube.GlobalPosition = c.A.GlobalPosition;
c.A.AddChild(tube);
tube.SetTarget(c.B.Position);
}
}
}
private record NoduleConnection
{
public Nodule A { get; init; }
public Nodule B { get; init; }
public NoduleConnection(Nodule a, Nodule b)
{
if (a.GetHashCode() <= b.GetHashCode())
{
A = a;
B = b;
}
else
{
A = b;
B = a;
}
}
public virtual bool Equals(NoduleConnection? other)
{
if (other is null)
return false;
return ReferenceEquals(A, other.A) && ReferenceEquals(B, other.B);
}
public override int GetHashCode()
{
return HashCode.Combine(A, B);
}
}
}

View File

@@ -0,0 +1 @@
uid://bxyquhfnvob64

9
src/Levels/BaseLevel.cs Normal file
View File

@@ -0,0 +1,9 @@
using Godot;
namespace Slimepire.Levels;
[GlobalClass]
public abstract partial class BaseLevel : Node
{
protected abstract Camera2D Camera { get; }
}

View File

@@ -0,0 +1 @@
uid://durlongh484is

9
src/Levels/DemoLevel.cs Normal file
View File

@@ -0,0 +1,9 @@
using Godot;
namespace Slimepire.Levels;
[SceneTree]
public partial class DemoLevel : BaseLevel
{
protected override Camera2D Camera => _.Camera;
}

View File

@@ -0,0 +1 @@
uid://c8l12dlpexosv

32
src/Levels/DemoLevel.tscn Normal file
View File

@@ -0,0 +1,32 @@
[gd_scene format=3 uid="uid://bqv3sg03tcv71"]
[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://cnl6t8o4mh0j3" path="res://src/Gameplay/Nodules/Nodule.tscn" id="3_qhkhc"]
[sub_resource type="LabelSettings" id="LabelSettings_l2cn0"]
font_size = 107
[node name="DemoLevel" type="Node2D" unique_id=1583216522]
script = ExtResource("1_l2cn0")
[node name="Camera" type="Camera2D" parent="." unique_id=1348670701]
script = ExtResource("2_mxa1b")
[node name="Label" type="Label" parent="." unique_id=1452841224]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -331.0
offset_top = -221.0
offset_right = 320.0
offset_bottom = -74.0
grow_horizontal = 2
grow_vertical = 2
text = "DEMO LEVEL"
label_settings = SubResource("LabelSettings_l2cn0")
[node name="Nodule" parent="." unique_id=1637542829 instance=ExtResource("3_qhkhc")]
IsRoot = true