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.
105 lines
4.1 KiB
105 lines
4.1 KiB
using System.Collections.Generic; |
|
using Godot; |
|
|
|
public class World : Node |
|
{ |
|
private static readonly PackedScene BLOCK = GD.Load<PackedScene>("res://scene/Block.tscn"); |
|
private static readonly PackedScene PLAYER = GD.Load<PackedScene>("res://scene/Player.tscn"); |
|
private static readonly PackedScene LOCAL_PLAYER = GD.Load<PackedScene>("res://scene/LocalPlayer.tscn"); |
|
private static readonly PackedScene HIT_DECAL = GD.Load<PackedScene>("res://scene/HitDecal.tscn"); |
|
|
|
internal Node PlayerContainer { get; } |
|
internal Node ChunkContainer { get; } |
|
|
|
public World() |
|
{ |
|
AddChild(PlayerContainer = new Node { Name = "Players" }); |
|
AddChild(ChunkContainer = new Node { Name = "Chunks" }); |
|
} |
|
|
|
public IEnumerable<Player> Players |
|
=> PlayerContainer.GetChildren<Player>(); |
|
public Player GetPlayer(int networkID) |
|
=> PlayerContainer.GetNodeOrNull<Player>(networkID.ToString()); |
|
public void ClearPlayers() |
|
{ foreach (var player in Players) player.RemoveFromParent(); } |
|
|
|
public IEnumerable<Chunk> Chunks |
|
=> ChunkContainer.GetChildren<Chunk>(); |
|
public Chunk GetChunkOrNull((int X, int Y) chunkPos) |
|
=> ChunkContainer.GetNodeOrNull<Chunk>($"Chunk ({chunkPos.X}, {chunkPos.Y})"); |
|
public Chunk GetOrCreateChunk((int X, int Y) chunkPos) |
|
=> ChunkContainer.GetOrCreateChild($"Chunk ({chunkPos.X}, {chunkPos.Y})", () => new Chunk(chunkPos.X, chunkPos.Y)); |
|
[PuppetSync] public void ClearChunks() |
|
{ foreach (var chunk in Chunks) chunk.RemoveFromParent(); } |
|
|
|
|
|
public Block GetBlockAt(BlockPos position) |
|
=> GetChunkOrNull(position.ToChunkPos()) |
|
?.GetLayerOrNull<Block>()?[position.GlobalToChunkRel()]; |
|
[PuppetSync] |
|
public void SpawnBlock(int x, int y, Color color, bool unbreakable) |
|
{ |
|
var blockPos = new BlockPos(x, y); |
|
var block = BLOCK.Init<Block>(); |
|
block.Name = blockPos.ToString(); |
|
block.Color = color; |
|
block.Unbreakable = unbreakable; |
|
block.ChunkLocalBlockPos = blockPos.GlobalToChunkRel(); |
|
|
|
GetOrCreateChunk(blockPos.ToChunkPos()) |
|
.GetOrCreateLayer<Block>()[block.ChunkLocalBlockPos] = block; |
|
} |
|
[PuppetSync] |
|
public void DespawnBlock(int x, int y) |
|
{ |
|
var blockPos = new BlockPos(x, y); |
|
var blockLayer = GetChunkOrNull(blockPos.ToChunkPos())?.GetLayerOrNull<Block>(); |
|
if (blockLayer != null) blockLayer[blockPos.GlobalToChunkRel()] = null; |
|
} |
|
|
|
[PuppetSync] |
|
public void SpawnPlayer(int networkID, Vector2 position) |
|
{ |
|
var isLocal = networkID == GetTree().GetNetworkUniqueId(); |
|
var player = (isLocal ? LOCAL_PLAYER : PLAYER).Init<Player>(); |
|
player.NetworkID = networkID; |
|
player.Position = position; |
|
PlayerContainer.AddChild(player); |
|
|
|
if (player is LocalPlayer localPlayer) |
|
this.GetClient().FireLocalPlayerSpawned(localPlayer); |
|
|
|
if (this.GetGame() is Server) { |
|
player.VisibilityTracker.ChunkTracked += (chunkPos) => { |
|
var chunk = GetChunkOrNull(chunkPos); |
|
if (chunk == null) return; |
|
foreach (var block in chunk.GetLayerOrNull<Block>().GetChildren<Block>()) |
|
RPC.Reliable(player.NetworkID, SpawnBlock, |
|
block.GlobalBlockPos.X, block.GlobalBlockPos.Y, |
|
block.Color, block.Unbreakable); |
|
}; |
|
player.VisibilityTracker.ChunkUntracked += (chunkPos) => { |
|
var chunk = GetChunkOrNull(chunkPos); |
|
if (chunk == null) return; |
|
RPC.Reliable(player.NetworkID, Despawn, GetPathTo(chunk)); |
|
}; |
|
} |
|
} |
|
|
|
[Puppet] |
|
public void SpawnHit(NodePath spritePath, Vector2 hitPosition, Color color) |
|
{ |
|
var hit = HIT_DECAL.Init<HitDecal>(); |
|
var sprite = this.GetWorld().GetNode<Sprite>(spritePath); |
|
hit.Add(sprite, hitPosition, color); |
|
} |
|
|
|
[PuppetSync] |
|
public void Despawn(NodePath path) |
|
{ |
|
var node = GetNode(path); |
|
node.GetParent().RemoveChild(node); |
|
node.QueueFree(); |
|
} |
|
}
|
|
|