Add BlockRegion helper class

main
copygirl 1 week ago
parent 3dc9a3a3da
commit ad76ef1703
  1. 45
      world/block_region.gd
  2. 1
      world/block_region.gd.uid
  3. 5
      world/chunk.gd
  4. 4
      world/generation/generator_simple.gd
  5. 9
      world/layers/matter.gd
  6. 9
      world/layers/shape.gd

@ -0,0 +1,45 @@
class_name BlockRegion
extends RefCounted
static var LOCAL_CHUNK := BlockRegion.new(Vector2i.ZERO, Vector2i.ONE * (Chunk.SIZE - 1))
var min: Vector2i
var max: Vector2i
func _init(a: Vector2i, b: Vector2i) -> void:
min = a.min(b)
max = a.max(b)
func size() -> Vector2i:
return max - min + Vector2i.ONE
func contains(pos: Vector2i) -> bool:
return (pos.x >= min.x) && (pos.x <= max.x) and (pos.y >= min.y) && (pos.y <= max.y)
func offset(value: Vector2i) -> BlockRegion:
return BlockRegion.new(min + value, max + value)
func extend(amount: int) -> BlockRegion:
var v := Vector2i.ONE * amount
return BlockRegion.new(min - v, max + v)
func array_index(pos: Vector2i) -> int:
assert(contains(pos))
var size := size()
var index := (pos.x - min.x) + (pos.y - min.y) * size.x
return clampi(index, 0, size.x * size.y)
# Custom iterators in GDScript are WEIRD.
func _iter_init(state: Array) -> bool:
state[0] = min
return true
func _iter_next(state: Array) -> bool:
var cur: Vector2i = state[0]
if cur.x < max.x: state[0].x += 1; return true
elif cur.y < max.y: state[0] = Vector2i(min.x, cur.y + 1); return true
else: return false
func _iter_get(state: Variant) -> Vector2i:
return state

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

@ -76,7 +76,4 @@ static func pos_to_center(chunk_pos: Vector2i) -> Vector2:
## Creates an integer index from a chunk-local block position that can be used to index into an array. ## Creates an integer index from a chunk-local block position that can be used to index into an array.
static func array_index(local_pos: Vector2i) -> int: static func array_index(local_pos: Vector2i) -> int:
assert((local_pos.x >= 0) and (local_pos.x < Chunk.SIZE)) return BlockRegion.LOCAL_CHUNK.array_index(local_pos)
assert((local_pos.y >= 0) and (local_pos.y < Chunk.SIZE))
# Using `posmod` ensures `local_pos` is valid, and this returns a valid index.
return posmod(local_pos.x, Chunk.SIZE) + posmod(local_pos.y, Chunk.SIZE) * Chunk.SIZE

@ -31,9 +31,7 @@ func generate(world: World, chunk: Chunk) -> void:
var matter: Matter.Layer = chunk.get_or_create_layer(Matter) var matter: Matter.Layer = chunk.get_or_create_layer(Matter)
var shapes: Shape.Layer = chunk.get_or_create_layer(Shape) var shapes: Shape.Layer = chunk.get_or_create_layer(Shape)
for x in Chunk.SIZE: for local_pos in BlockRegion.LOCAL_CHUNK:
for y in Chunk.SIZE:
var local_pos := Vector2i(x, y)
var shape: Shape var shape: Shape
match _values(local_pos): match _values(local_pos):
[ true, true, true, true ]: shape = Shape.FULL [ true, true, true, true ]: shape = Shape.FULL

@ -41,13 +41,14 @@ class Layer extends MeshInstance2D:
@rpc("reliable") @rpc("reliable")
func set_id_at(pos: Vector2i, id: int) -> void: func set_id_at(pos: Vector2i, id: int) -> void:
var index := Chunk.array_index(pos) var index := Chunk.array_index(pos)
if data[index] != id: if data[index] == id: return
# TODO: Keep track of how many non-air blocks are present, to allow for cleaning empty chunks. # TODO: Keep track of how many non-air blocks are present, to allow for cleaning empty chunks.
data[index] = id data[index] = id
chunk.dirty = true chunk.dirty = true
# Send change to every player tracking this chunk.
if multiplayer.is_server(): if multiplayer.is_server():
# Send change to every player tracking this chunk.
for player in chunk.get_players_tracking(): for player in chunk.get_players_tracking():
if player.network.is_local: continue # skip server player if player.network.is_local: continue # skip server player
set_id_at.rpc_id(player.network.peer_id, pos, id) set_id_at.rpc_id(player.network.peer_id, pos, id)
@ -66,9 +67,7 @@ class Layer extends MeshInstance2D:
var shapes: Shape.Layer = chunk.get_layer_or_null(Shape) var shapes: Shape.Layer = chunk.get_layer_or_null(Shape)
if shapes: # Should exist, but let's sanity check. if shapes: # Should exist, but let's sanity check.
for x in Chunk.SIZE: for pos in BlockRegion.LOCAL_CHUNK:
for y in Chunk.SIZE:
var pos := Vector2i(x, y)
var offset := (Vector2.ONE / 2) + Vector2(pos) var offset := (Vector2.ONE / 2) + Vector2(pos)
# TODO: Support different matter. # TODO: Support different matter.

@ -130,13 +130,14 @@ class Layer extends StaticBody2D:
@rpc("reliable") @rpc("reliable")
func set_id_at(pos: Vector2i, id: int) -> void: func set_id_at(pos: Vector2i, id: int) -> void:
var index := Chunk.array_index(pos) var index := Chunk.array_index(pos)
if data[index] != id: if data[index] == id: return
# TODO: Keep track of how many non-air blocks are present, to allow for cleaning empty chunks. # TODO: Keep track of how many non-air blocks are present, to allow for cleaning empty chunks.
data[index] = id data[index] = id
chunk.dirty = true chunk.dirty = true
# Send change to every player tracking this chunk.
if multiplayer.is_server(): if multiplayer.is_server():
# Send change to every player tracking this chunk.
for player in chunk.get_players_tracking(): for player in chunk.get_players_tracking():
if player.network.is_local: continue # skip server player if player.network.is_local: continue # skip server player
set_id_at.rpc_id(player.network.peer_id, pos, id) set_id_at.rpc_id(player.network.peer_id, pos, id)
@ -155,9 +156,7 @@ class Layer extends StaticBody2D:
if child is CollisionShape2D: if child is CollisionShape2D:
child.queue_free() child.queue_free()
for x in Chunk.SIZE: for pos in BlockRegion.LOCAL_CHUNK:
for y in Chunk.SIZE:
var pos := Vector2i(x, y)
var shape := get_at(pos) var shape := get_at(pos)
if shape.shape == null: continue if shape.shape == null: continue

Loading…
Cancel
Save