fixed basic movement, now add world loading
This commit is contained in:
parent
34ba37312a
commit
50f37ebc5f
23 changed files with 482 additions and 27 deletions
7
packs/base/data/abilities/abilities.json
Normal file
7
packs/base/data/abilities/abilities.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"abilities": [
|
||||||
|
{ "id": "ability:dash", "name": "Dash", "description": "Allows you to break through weak barriers." }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
28
packs/base/data/creatures/creatures.json
Normal file
28
packs/base/data/creatures/creatures.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"creatures": [
|
||||||
|
{
|
||||||
|
"id": "creature:ember_fox",
|
||||||
|
"name": "Ember Fox",
|
||||||
|
"types": ["type:ember"],
|
||||||
|
"biomes": ["biome:forest"],
|
||||||
|
"time_windows": ["day", "night"],
|
||||||
|
"base_hp": 20,
|
||||||
|
"base_atk": 8,
|
||||||
|
"base_def": 6,
|
||||||
|
"capture_difficulty": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "creature:dew_sprout",
|
||||||
|
"name": "Dew Sprout",
|
||||||
|
"types": ["type:flora"],
|
||||||
|
"biomes": ["biome:plains"],
|
||||||
|
"time_windows": ["morning", "day"],
|
||||||
|
"base_hp": 18,
|
||||||
|
"base_atk": 6,
|
||||||
|
"base_def": 7,
|
||||||
|
"capture_difficulty": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
8
packs/base/data/items/items.json
Normal file
8
packs/base/data/items/items.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"items": [
|
||||||
|
{ "id": "item:forest_herb", "name": "Forest Herb", "stack_max": 99, "tags": ["natural"] },
|
||||||
|
{ "id": "item:rusty_coin", "name": "Rusty Coin", "stack_max": 99, "tags": ["unnatural"] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
24
packs/base/data/npcs/npcdefs.json
Normal file
24
packs/base/data/npcs/npcdefs.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"npcdefs": [
|
||||||
|
{
|
||||||
|
"id": "npcdef:shopkeeper_basic",
|
||||||
|
"name": "Mara",
|
||||||
|
"dialogue": ["Welcome!", "Take a look at my goods."],
|
||||||
|
"shop_inventory": [
|
||||||
|
{ "item": "item:forest_herb", "price": 10 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npcdef:trainer_story_01",
|
||||||
|
"name": "Rook",
|
||||||
|
"dialogue": ["If you want to pass, prove yourself."],
|
||||||
|
"battle": {
|
||||||
|
"team": ["creature:ember_fox"],
|
||||||
|
"reward_money": 25,
|
||||||
|
"on_win_unlocks": ["ability:dash"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
34
packs/base/data/world/chunks/0_0.json
Normal file
34
packs/base/data/world/chunks/0_0.json
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"id": "worldchunk:0_0",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"biome": "biome:forest",
|
||||||
|
"collision_rects": [
|
||||||
|
{ "id": "col:rockwall_01", "rect": [0, 0, 512, 32] }
|
||||||
|
],
|
||||||
|
"spawn_zones": [
|
||||||
|
{
|
||||||
|
"id": "spawn:forest_a",
|
||||||
|
"biome": "biome:forest",
|
||||||
|
"rect": [64, 64, 320, 320],
|
||||||
|
"time_windows": ["day", "night"],
|
||||||
|
"weather": ["any"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pickups": [
|
||||||
|
{
|
||||||
|
"id": "pickup:forest_herb_01",
|
||||||
|
"item": "item:forest_herb",
|
||||||
|
"pos": [120, 140],
|
||||||
|
"respawn_seconds": 600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pickup:rusty_coin_01",
|
||||||
|
"item": "item:rusty_coin",
|
||||||
|
"pos": [220, 180],
|
||||||
|
"respawn_seconds": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
17
packs/base/data/world/chunks/0_1.json
Normal file
17
packs/base/data/world/chunks/0_1.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"id": "worldchunk:0_1",
|
||||||
|
"x": 0,
|
||||||
|
"y": 1,
|
||||||
|
"biome": "biome:plains",
|
||||||
|
"spawn_zones": [
|
||||||
|
{
|
||||||
|
"id": "spawn:plains_a",
|
||||||
|
"biome": "biome:plains",
|
||||||
|
"rect": [80, 80, 360, 300],
|
||||||
|
"time_windows": ["morning", "day"],
|
||||||
|
"weather": ["any"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
24
packs/base/data/world/chunks/1_0.json
Normal file
24
packs/base/data/world/chunks/1_0.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"id": "worldchunk:1_0",
|
||||||
|
"x": 1,
|
||||||
|
"y": 0,
|
||||||
|
"biome": "biome:forest",
|
||||||
|
"gates": [
|
||||||
|
{
|
||||||
|
"id": "gate:fallen_tree_01",
|
||||||
|
"rect": [200, 220, 96, 32],
|
||||||
|
"requires": "ability:dash",
|
||||||
|
"message": "A fallen tree blocks the path."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"npcs": [
|
||||||
|
{
|
||||||
|
"id": "npc:shopkeeper_01",
|
||||||
|
"kind": "shop",
|
||||||
|
"pos": [320, 300],
|
||||||
|
"ref": "npcdef:shopkeeper_basic"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
16
packs/base/data/world/chunks/1_1.json
Normal file
16
packs/base/data/world/chunks/1_1.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"id": "worldchunk:1_1",
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"biome": "biome:town",
|
||||||
|
"npcs": [
|
||||||
|
{
|
||||||
|
"id": "npc:trainer_01",
|
||||||
|
"kind": "trainer",
|
||||||
|
"pos": [260, 260],
|
||||||
|
"ref": "npcdef:trainer_story_01"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
15
packs/base/data/world/world_main.json
Normal file
15
packs/base/data/world/world_main.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"world_id": "world:main",
|
||||||
|
"display_name": "Main World",
|
||||||
|
"chunk_size_px": 512,
|
||||||
|
"tile_size_px": 16,
|
||||||
|
"start_pos_px": [256, 256],
|
||||||
|
"chunks": [
|
||||||
|
{ "x": 0, "y": 0, "path": "data/world/chunks/0_0.json" },
|
||||||
|
{ "x": 1, "y": 0, "path": "data/world/chunks/1_0.json" },
|
||||||
|
{ "x": 0, "y": 1, "path": "data/world/chunks/0_1.json" },
|
||||||
|
{ "x": 1, "y": 1, "path": "data/world/chunks/1_1.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
9
packs/base/pack.json
Normal file
9
packs/base/pack.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"id": "base",
|
||||||
|
"name": "Base Featurepack",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"priority": 0,
|
||||||
|
"worlds": ["world:main"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +19,41 @@ config/icon="res://icon.svg"
|
||||||
|
|
||||||
project/assembly_name="ocker"
|
project/assembly_name="ocker"
|
||||||
|
|
||||||
|
[input]
|
||||||
|
|
||||||
|
ui_left={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":13,"pressure":0.0,"pressed":false,"script":null)
|
||||||
|
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
ui_right={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":14,"pressure":0.0,"pressed":false,"script":null)
|
||||||
|
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
ui_up={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":11,"pressure":0.0,"pressed":false,"script":null)
|
||||||
|
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
ui_down={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":12,"pressure":0.0,"pressed":false,"script":null)
|
||||||
|
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[physics]
|
[physics]
|
||||||
|
|
||||||
3d/physics_engine="Jolt Physics"
|
3d/physics_engine="Jolt Physics"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,21 @@
|
||||||
[gd_scene format=3 uid="uid://dackb8ekt4sk6"]
|
[gd_scene format=3 uid="uid://dackb8ekt4sk6"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://bjt15rm720w5g" path="res://scenes/Player.tscn" id="1_elqb8"]
|
[ext_resource type="PackedScene" uid="uid://bjt15rm720w5g" path="res://scenes/Player.tscn" id="1_elqb8"]
|
||||||
|
[ext_resource type="Script" uid="uid://cqdq8fslu7cyp" path="res://scripts/world/ChunkManager.cs" id="2_0bbpv"]
|
||||||
|
[ext_resource type="Script" uid="uid://db2vbbh5737ke" path="res://scripts/core/PackManager.cs" id="3_rarhs"]
|
||||||
|
|
||||||
[node name="Main" type="Node2D" unique_id=1194367579]
|
[node name="Main" type="Node2D" unique_id=1194367579]
|
||||||
|
|
||||||
[node name="Overworld" type="Node2D" parent="." unique_id=1249792834]
|
[node name="Overworld" type="Node2D" parent="." unique_id=1249792834]
|
||||||
|
|
||||||
[node name="Player" parent="." unique_id=1675096620 instance=ExtResource("1_elqb8")]
|
[node name="Player" parent="Overworld" unique_id=1675096620 instance=ExtResource("1_elqb8")]
|
||||||
position = Vector2(-34, -36)
|
|
||||||
|
[node name="ChunkManager" type="Node2D" parent="Overworld" unique_id=1407815239]
|
||||||
|
script = ExtResource("2_0bbpv")
|
||||||
|
|
||||||
|
[node name="ColorRect" type="ColorRect" parent="." unique_id=34197065]
|
||||||
|
offset_right = 40.0
|
||||||
|
offset_bottom = 40.0
|
||||||
|
|
||||||
|
[node name="PackManager" type="Node" parent="." unique_id=1706387276]
|
||||||
|
script = ExtResource("3_rarhs")
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,28 @@
|
||||||
[gd_scene format=3 uid="uid://bjt15rm720w5g"]
|
[gd_scene format=3 uid="uid://bjt15rm720w5g"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://duf0vlr2yin8l" path="res://scripts/Player.cs" id="1_p0vlq"]
|
[ext_resource type="Script" uid="uid://duf0vlr2yin8l" path="res://scripts/gameplay/Player.cs" id="1_p0vlq"]
|
||||||
[ext_resource type="Script" uid="uid://b25o2r2lf6nst" path="res://scripts/Visual.cs" id="2_v6fml"]
|
|
||||||
|
|
||||||
[sub_resource type="CircleShape2D" id="CircleShape2D_v6fml"]
|
[sub_resource type="CircleShape2D" id="CircleShape2D_v6fml"]
|
||||||
|
|
||||||
|
[sub_resource type="Gradient" id="Gradient_p0vlq"]
|
||||||
|
|
||||||
|
[sub_resource type="GradientTexture2D" id="GradientTexture2D_v6fml"]
|
||||||
|
gradient = SubResource("Gradient_p0vlq")
|
||||||
|
width = 16
|
||||||
|
height = 16
|
||||||
|
|
||||||
[node name="Player" type="CharacterBody2D" unique_id=1675096620]
|
[node name="Player" type="CharacterBody2D" unique_id=1675096620]
|
||||||
script = ExtResource("1_p0vlq")
|
script = ExtResource("1_p0vlq")
|
||||||
|
|
||||||
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=554119030]
|
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1337429488]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1337429488]
|
||||||
position = Vector2(-15, -16)
|
position = Vector2(-15, -16)
|
||||||
shape = SubResource("CircleShape2D_v6fml")
|
shape = SubResource("CircleShape2D_v6fml")
|
||||||
|
|
||||||
[node name="ColorRect" type="ColorRect" parent="CollisionShape2D" unique_id=2088988808]
|
[node name="Sprite2D" type="Sprite2D" parent="CollisionShape2D" unique_id=554119030]
|
||||||
|
position = Vector2(15, 16)
|
||||||
|
texture = SubResource("GradientTexture2D_v6fml")
|
||||||
|
|
||||||
|
[node name="ColorRect" type="ColorRect" parent="CollisionShape2D" unique_id=1178008943]
|
||||||
anchors_preset = 8
|
anchors_preset = 8
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
|
|
@ -26,7 +34,5 @@ offset_right = 8.0
|
||||||
offset_bottom = 8.0
|
offset_bottom = 8.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
color = Color(0.13958341, 0.81406105, 0, 1)
|
|
||||||
|
|
||||||
[node name="Visual" type="Node2D" parent="." unique_id=630351727]
|
[node name="Camera2D" type="Camera2D" parent="." unique_id=483897590]
|
||||||
script = ExtResource("2_v6fml")
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
using Godot;
|
|
||||||
|
|
||||||
public partial class PlayerVisual : Node2D
|
|
||||||
{
|
|
||||||
[Export] public float Radius = 8f;
|
|
||||||
|
|
||||||
public override void _Draw()
|
|
||||||
{
|
|
||||||
DrawCircle(Vector2.Zero, Radius, new Color(0.2f, 0.8f, 1.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void _Ready()
|
|
||||||
{
|
|
||||||
QueueRedraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://b25o2r2lf6nst
|
|
||||||
74
scripts/core/PackManager.cs
Normal file
74
scripts/core/PackManager.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
public partial class PackManager : Node
|
||||||
|
{
|
||||||
|
public record PackManifest(int schema, string id, string name, string version, int priority);
|
||||||
|
|
||||||
|
private readonly List<(string Root, PackManifest Manifest)> _packs = new();
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
LoadPacks("res://packs");
|
||||||
|
GD.Print($"Loaded packs: {string.Join(", ", _packs.Select(p => $"{p.Manifest.id}(prio={p.Manifest.priority})"))}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadPacks(string packsRoot)
|
||||||
|
{
|
||||||
|
_packs.Clear();
|
||||||
|
|
||||||
|
if (!DirAccess.DirExistsAbsolute(packsRoot))
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Packs root missing: {packsRoot}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var dir = DirAccess.Open(packsRoot);
|
||||||
|
dir.ListDirBegin();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var entry = dir.GetNext();
|
||||||
|
if (entry == "") break;
|
||||||
|
if (entry.StartsWith(".")) continue;
|
||||||
|
|
||||||
|
var packPath = $"{packsRoot}/{entry}";
|
||||||
|
if (!dir.CurrentIsDir()) continue;
|
||||||
|
|
||||||
|
var manifestPath = $"{packPath}/pack.json";
|
||||||
|
if (!FileAccess.FileExists(manifestPath))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var json = FileAccess.GetFileAsString(manifestPath);
|
||||||
|
var manifest = JsonSerializer.Deserialize<PackManifest>(json);
|
||||||
|
if (manifest == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Invalid pack.json: {manifestPath}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_packs.Add((packPath, manifest));
|
||||||
|
}
|
||||||
|
dir.ListDirEnd();
|
||||||
|
|
||||||
|
// Sort bottom->top by priority (low first, high last)
|
||||||
|
_packs.Sort((a, b) => a.Manifest.priority.CompareTo(b.Manifest.priority));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the best (topmost) existing file path for a relative path inside a pack.
|
||||||
|
public string? Resolve(string relativePath)
|
||||||
|
{
|
||||||
|
relativePath = relativePath.TrimStart('/');
|
||||||
|
|
||||||
|
// Highest priority pack wins -> iterate from end
|
||||||
|
for (int i = _packs.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var candidate = $"{_packs[i].Root}/{relativePath}";
|
||||||
|
if (FileAccess.FileExists(candidate))
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
scripts/core/PackManager.cs.uid
Normal file
1
scripts/core/PackManager.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://db2vbbh5737ke
|
||||||
146
scripts/world/ChunkManager.cs
Normal file
146
scripts/world/ChunkManager.cs
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
public partial class ChunkManager : Node2D
|
||||||
|
{
|
||||||
|
[Export] public NodePath PlayerPath;
|
||||||
|
[Export] public int LoadRadius = 1; // 1 -> 3x3
|
||||||
|
|
||||||
|
private CharacterBody2D? _player;
|
||||||
|
private PackManager? _packs;
|
||||||
|
|
||||||
|
private WorldIndex? _world;
|
||||||
|
private Dictionary<(int x, int y), string> _chunkMap = new();
|
||||||
|
private readonly Dictionary<(int x, int y), Node2D> _loaded = new();
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_player = GetNodeOrNull<CharacterBody2D>(PlayerPath);
|
||||||
|
if (_player == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr("ChunkManager: PlayerPath not set or invalid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_packs = GetNodeOrNull<PackManager>("/root/Main/PackManager");
|
||||||
|
if (_packs == null)
|
||||||
|
{
|
||||||
|
// fallback: search up the tree for PackManager
|
||||||
|
_packs = GetParent()?.GetNodeOrNull<PackManager>("PackManager");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_packs == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr("ChunkManager: PackManager not found. Add it to your Main scene.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadWorldIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadWorldIndex()
|
||||||
|
{
|
||||||
|
var worldPath = _packs.Resolve("data/world/world_main.json");
|
||||||
|
if (worldPath == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr("ChunkManager: Could not resolve data/world/world_main.json from packs.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = FileAccess.GetFileAsString(worldPath);
|
||||||
|
_world = JsonSerializer.Deserialize<WorldIndex>(json);
|
||||||
|
if (_world == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr("ChunkManager: Failed to parse world index.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_chunkMap = _world.BuildChunkMap();
|
||||||
|
GD.Print($"World loaded: {_world.world_id}, chunks: {_chunkMap.Count}, chunk_size_px={_world.chunk_size_px}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Process(double delta)
|
||||||
|
{
|
||||||
|
if (_player == null || _world == null) return;
|
||||||
|
|
||||||
|
var (cx, cy) = WorldPosToChunk(_player.GlobalPosition, _world.chunk_size_px);
|
||||||
|
|
||||||
|
for (int dy = -LoadRadius; dy <= LoadRadius; dy++)
|
||||||
|
for (int dx = -LoadRadius; dx <= LoadRadius; dx++)
|
||||||
|
{
|
||||||
|
var key = (cx + dx, cy + dy);
|
||||||
|
EnsureLoaded(key.x, key.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unload chunks that are too far
|
||||||
|
var keysToRemove = new List<(int x, int y)>();
|
||||||
|
foreach (var kv in _loaded)
|
||||||
|
{
|
||||||
|
var (x, y) = kv.Key;
|
||||||
|
if (Math.Abs(x - cx) > LoadRadius || Math.Abs(y - cy) > LoadRadius)
|
||||||
|
keysToRemove.Add((x, y));
|
||||||
|
}
|
||||||
|
foreach (var k in keysToRemove)
|
||||||
|
{
|
||||||
|
_loaded[k].QueueFree();
|
||||||
|
_loaded.Remove(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureLoaded(int x, int y)
|
||||||
|
{
|
||||||
|
var key = (x, y);
|
||||||
|
if (_loaded.ContainsKey(key)) return;
|
||||||
|
if (!_chunkMap.TryGetValue(key, out var relPath)) return;
|
||||||
|
|
||||||
|
// Use pack resolve for the chunk file itself
|
||||||
|
var chunkPath = _packs!.Resolve(relPath);
|
||||||
|
if (chunkPath == null)
|
||||||
|
{
|
||||||
|
GD.PrintErr($"Chunk missing in packs: {relPath}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, we don't parse chunk contents yet; we just visualize existence + biome by file naming/placeholder.
|
||||||
|
// Next step: parse biome from JSON and use it to color.
|
||||||
|
var chunkJson = FileAccess.GetFileAsString(chunkPath);
|
||||||
|
using var doc = JsonDocument.Parse(chunkJson);
|
||||||
|
var biome = doc.RootElement.TryGetProperty("biome", out var b) ? b.GetString() : "biome:unknown";
|
||||||
|
|
||||||
|
var node = CreateDebugChunkNode(x, y, _world!.chunk_size_px, biome ?? "biome:unknown");
|
||||||
|
AddChild(node);
|
||||||
|
_loaded[key] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (int cx, int cy) WorldPosToChunk(Vector2 pos, int chunkSizePx)
|
||||||
|
{
|
||||||
|
int cx = Mathf.FloorToInt(pos.X / chunkSizePx);
|
||||||
|
int cy = Mathf.FloorToInt(pos.Y / chunkSizePx);
|
||||||
|
return (cx, cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Node2D CreateDebugChunkNode(int cx, int cy, int chunkSizePx, string biome)
|
||||||
|
{
|
||||||
|
var n = new Node2D();
|
||||||
|
n.Name = $"Chunk_{cx}_{cy}";
|
||||||
|
n.Position = new Vector2(cx * chunkSizePx, cy * chunkSizePx);
|
||||||
|
|
||||||
|
var rect = new ColorRect();
|
||||||
|
rect.Size = new Vector2(chunkSizePx, chunkSizePx);
|
||||||
|
rect.MouseFilter = Control.MouseFilterEnum.Ignore;
|
||||||
|
|
||||||
|
rect.Color = biome switch
|
||||||
|
{
|
||||||
|
"biome:forest" => new Color(0.1f, 0.35f, 0.1f, 0.25f),
|
||||||
|
"biome:plains" => new Color(0.35f, 0.35f, 0.1f, 0.25f),
|
||||||
|
"biome:town" => new Color(0.25f, 0.25f, 0.25f, 0.25f),
|
||||||
|
_ => new Color(0.2f, 0.2f, 0.2f, 0.25f)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add an outline using a Panel (cheap) or just rely on transparency for now
|
||||||
|
n.AddChild(rect);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
scripts/world/ChunkManager.cs.uid
Normal file
1
scripts/world/ChunkManager.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cqdq8fslu7cyp
|
||||||
15
scripts/world/WorldIndex.cs
Normal file
15
scripts/world/WorldIndex.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
public record WorldIndex(int schema, string world_id, string display_name, int chunk_size_px, int tile_size_px, int[] start_pos_px, List<WorldIndexChunk> chunks)
|
||||||
|
{
|
||||||
|
public Dictionary<(int x, int y), string> BuildChunkMap()
|
||||||
|
{
|
||||||
|
var map = new Dictionary<(int, int), string>();
|
||||||
|
foreach (var c in chunks)
|
||||||
|
map[(c.x, c.y)] = c.path;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record WorldIndexChunk(int x, int y, string path);
|
||||||
1
scripts/world/WorldIndex.cs.uid
Normal file
1
scripts/world/WorldIndex.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bgtmfbm4qg0l5
|
||||||
Loading…
Add table
Add a link
Reference in a new issue