It's much nicer to code with now
This commit is contained in:
97
Set.gd
97
Set.gd
@@ -1,4 +1,5 @@
|
||||
class_name Set extends RefCounted
|
||||
## Adapted from: https://gist.github.com/NoodleSushi/3eb9cc08eb1d1c369bef308262804f1e
|
||||
## A custom implementation of a set data structure in GDScript.
|
||||
##
|
||||
## Usage Example:
|
||||
@@ -48,47 +49,47 @@ func duplicate() -> Set:
|
||||
# O(min(|self|, |set|))
|
||||
## Returns a new [Set] containing elements that are present in the current set
|
||||
## but not in the provided [param set].
|
||||
func difference(set: Set) -> Set:
|
||||
if set.hash() == self.hash():
|
||||
func difference(param: Set) -> Set:
|
||||
if param.hash() == self.hash():
|
||||
return Set.new()
|
||||
var out := self.duplicate()
|
||||
if set.size() > self.size():
|
||||
if param.size() > self.size():
|
||||
for element in self.elements():
|
||||
if set.has(element):
|
||||
if param.has(element):
|
||||
out.erase(element)
|
||||
else:
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
out.erase(element)
|
||||
return out
|
||||
|
||||
# O(min(|self|, |set|))
|
||||
## Modifies the current [Set] to contain elements present in the current set\
|
||||
## but not in the provided [param set].
|
||||
func difference_update(set: Set) -> void:
|
||||
if set.hash() == self.hash():
|
||||
func difference_update(param: Set) -> void:
|
||||
if param.hash() == self.hash():
|
||||
self.clear()
|
||||
return
|
||||
if set.size() > self.size():
|
||||
if param.size() > self.size():
|
||||
for element in self.elements():
|
||||
if set.has(element):
|
||||
if param.has(element):
|
||||
self.erase(element)
|
||||
else:
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
self.erase(element)
|
||||
|
||||
# O(min(|self|, |set|))
|
||||
## Returns a new [Set] containing elements common to both the current set and
|
||||
## the provided [param set].
|
||||
func intersection(set: Set) -> Set:
|
||||
if set.hash() == self.hash():
|
||||
func intersection(param: Set) -> Set:
|
||||
if param.hash() == self.hash():
|
||||
return duplicate()
|
||||
var out := Set.new()
|
||||
if set.size() > self.size():
|
||||
if param.size() > self.size():
|
||||
for element in self.elements():
|
||||
if set.has(element):
|
||||
if param.has(element):
|
||||
out.add(element)
|
||||
else:
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
if self.has(element):
|
||||
out.add(element)
|
||||
return out
|
||||
@@ -96,16 +97,16 @@ func intersection(set: Set) -> Set:
|
||||
# O(min(|self|, |set|))
|
||||
## Modifies the current set to contain only elements common to both the
|
||||
## current set and the provided [param set].
|
||||
func intersection_update(set: Set) -> void:
|
||||
if set.hash() == self.hash():
|
||||
func intersection_update(param: Set) -> void:
|
||||
if param.hash() == self.hash():
|
||||
return
|
||||
var out := Set.new()
|
||||
if set.size() > self.size():
|
||||
if param.size() > self.size():
|
||||
for element in self.elements():
|
||||
if set.has(element):
|
||||
if param.has(element):
|
||||
out.add(element)
|
||||
else:
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
if self.has(element):
|
||||
out.add(element)
|
||||
self.clear()
|
||||
@@ -114,13 +115,13 @@ func intersection_update(set: Set) -> void:
|
||||
# O([1, min(|self|, |set|)])
|
||||
## Returns [code]true[/code] if the sets have no elements in common;
|
||||
## otherwise, returns [code]false[/code].
|
||||
func isdisjoint(set: Set) -> bool:
|
||||
if set.size() > self.size():
|
||||
func isdisjoint(param: Set) -> bool:
|
||||
if param.size() > self.size():
|
||||
for element in self.elements():
|
||||
if set.has(element):
|
||||
if param.has(element):
|
||||
return false
|
||||
else:
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
if self.has(element):
|
||||
return false
|
||||
return true
|
||||
@@ -128,23 +129,23 @@ func isdisjoint(set: Set) -> bool:
|
||||
# O([1, |self|])
|
||||
## Returns [code]true[/code] if every element of the current set is present
|
||||
## in the provided [param set]; otherwise, returns [code]false[/code].
|
||||
func issubset(set: Set) -> bool:
|
||||
if set.size() < self.size():
|
||||
func issubset(param: Set) -> bool:
|
||||
if param.size() < self.size():
|
||||
return false
|
||||
else:
|
||||
for element in self.elements():
|
||||
if !set.has(element):
|
||||
if !param.has(element):
|
||||
return false
|
||||
return true
|
||||
|
||||
# O([1, |set|])
|
||||
## Returns [code]true[/code] if every element of the provided [param set]
|
||||
## is present in the current set; otherwise, returns [code]false[/code].
|
||||
func issuperset(set: Set) -> bool:
|
||||
if self.size() < set.size():
|
||||
func issuperset(param: Set) -> bool:
|
||||
if self.size() < param.size():
|
||||
return false
|
||||
else:
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
if !self.has(element):
|
||||
return false
|
||||
return true
|
||||
@@ -164,18 +165,18 @@ func erase(element: Variant) -> void:
|
||||
# O(min(|self|, |set|))
|
||||
## Returns a new [Set] containing elements that are present in either
|
||||
## the current set or the provided [param set], but not in both.
|
||||
func symmetric_difference(set: Set) -> Set:
|
||||
if set.hash() == self.hash():
|
||||
func symmetric_difference(param: Set) -> Set:
|
||||
if param.hash() == self.hash():
|
||||
return Set.new()
|
||||
if set.size() > self.size():
|
||||
var out := set.duplicate()
|
||||
if param.size() > self.size():
|
||||
var out := param.duplicate()
|
||||
for element in self.elements():
|
||||
if set.has(element):
|
||||
if param.has(element):
|
||||
out.remove(element)
|
||||
return out
|
||||
else:
|
||||
var out := self.duplicate()
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
if self.has(element):
|
||||
out.remove(element)
|
||||
return out
|
||||
@@ -183,9 +184,9 @@ func symmetric_difference(set: Set) -> Set:
|
||||
# O(min(|self|, |set|))
|
||||
## Modifies the current set to contain elements that are present in either
|
||||
## the current set or the provided [param set], but not in both.
|
||||
func symmetric_difference_update(set: Set) -> void:
|
||||
if set.size() > self.size():
|
||||
var temp := set.duplicate()
|
||||
func symmetric_difference_update(param: Set) -> void:
|
||||
if param.size() > self.size():
|
||||
var temp := param.duplicate()
|
||||
for element in self.elements():
|
||||
if temp.has(element):
|
||||
temp.erase(element)
|
||||
@@ -193,7 +194,7 @@ func symmetric_difference_update(set: Set) -> void:
|
||||
temp.add(element)
|
||||
self._set = temp._set
|
||||
else:
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
if self.has(element):
|
||||
self.erase(element)
|
||||
else:
|
||||
@@ -202,30 +203,30 @@ func symmetric_difference_update(set: Set) -> void:
|
||||
# O(min(|self|, |set|))
|
||||
## Returns a new [Set] containing all elements from both the current
|
||||
## set and the provided [param set].
|
||||
func union(set: Set) -> Set:
|
||||
if set.size() > self.size():
|
||||
var out := set.duplicate()
|
||||
func union(param: Set) -> Set:
|
||||
if param.size() > self.size():
|
||||
var out := param.duplicate()
|
||||
for element in self.elements():
|
||||
out.add(element)
|
||||
return out
|
||||
else:
|
||||
var out := self.duplicate()
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
out.add(element)
|
||||
return out
|
||||
|
||||
# O(min(|self|, |set|))
|
||||
## Modifies the current set to contain all elements from both the current set
|
||||
## and the provided [param set].
|
||||
func update(set: Set) -> void:
|
||||
if set.size() > self.size():
|
||||
var temp := set.duplicate()
|
||||
func update(param: Set) -> void:
|
||||
if param.size() > self.size():
|
||||
var temp := param.duplicate()
|
||||
for element in self.elements():
|
||||
temp.add(element)
|
||||
self._dict = temp._dict
|
||||
else:
|
||||
var temp := self.duplicate()
|
||||
for element in set.elements():
|
||||
for element in param.elements():
|
||||
temp.add(element)
|
||||
self._dict = temp.dict
|
||||
|
||||
|
||||
72
client.gd
72
client.gd
@@ -3,11 +3,6 @@ extends Node
|
||||
#Master list of chunks
|
||||
var _chunks: Array[Chunk] = []
|
||||
|
||||
#Create some test samples - these samples must ALWAYS be 9-elements long, 5th element is the result
|
||||
#var _samples: Array[PackedInt32Array] = [
|
||||
#[1,1,1, 1,1,1, 1,1,1],
|
||||
#]
|
||||
|
||||
@onready
|
||||
var wfc: Node = get_node("../Generator")
|
||||
|
||||
@@ -15,73 +10,14 @@ var wfc: Node = get_node("../Generator")
|
||||
var map: TileMapLayer = get_node("../TileMapLayer")
|
||||
|
||||
func _ready() -> void:
|
||||
var ruleset: PackedInt32Array = read_sample_ruleset("sample2.png")
|
||||
var samples: Array[PackedInt32Array] = parse_samples_from_ruleset(ruleset)
|
||||
var samples: Array[PackedInt32Array] = Rulesets.load_samples("sample3.png")
|
||||
var c: Chunk = wfc.generate_chunk_at(0,0,_chunks,samples)
|
||||
#TODO: build a visual layout for the results
|
||||
#TODO: handle the chunk-edges
|
||||
#TODO: handle the chunk-edges, non-zero chunk coords
|
||||
|
||||
#print(ruleset)
|
||||
#print(samples)
|
||||
print(c.data)
|
||||
print(c.data) #debugging
|
||||
draw_map_data(c)
|
||||
|
||||
## Read the png file, and parse it to a useable ruleset
|
||||
func read_sample_ruleset(filename: String) -> PackedInt32Array:
|
||||
var img: Image = Image.load_from_file(filename)
|
||||
var png: PackedByteArray = img.get_data()
|
||||
var hex: PackedInt32Array = []
|
||||
|
||||
@warning_ignore("integer_division")
|
||||
var size: int = (png.size() / 3)
|
||||
hex.resize(size)
|
||||
|
||||
#print(png)
|
||||
for i in range(size): #the file is assumed to be in RGB format
|
||||
hex[i] = (png[i * 3] << 16) | (png[i * 3 + 1] << 8) | (png[i * 3 + 2] << 0)
|
||||
#print(i, "(", hex[i], "): ", png[i * 3], ",", png[i * 3 + 1], ",", png[i * 3 + 2])
|
||||
return hex
|
||||
|
||||
func parse_samples_from_ruleset(ruleset: PackedInt32Array) -> Array[PackedInt32Array]:
|
||||
#for now, assume the ruleset is 8x8
|
||||
const RULESET_WIDTH: int = 8
|
||||
const RULESET_HEIGHT: int = 8
|
||||
|
||||
#using a custom container type
|
||||
var samples: Set = Set.new()
|
||||
|
||||
for x in range(1, RULESET_WIDTH-1):
|
||||
for y in range(1, RULESET_HEIGHT-1):
|
||||
var sample: PackedInt32Array = [
|
||||
ruleset[(y -1) * RULESET_WIDTH + (x -1)],
|
||||
ruleset[(y -1) * RULESET_WIDTH + (x )],
|
||||
ruleset[(y -1) * RULESET_WIDTH + (x +1)],
|
||||
|
||||
ruleset[(y ) * RULESET_WIDTH + (x -1)],
|
||||
ruleset[(y ) * RULESET_WIDTH + (x )],
|
||||
ruleset[(y ) * RULESET_WIDTH + (x +1)],
|
||||
|
||||
ruleset[(y +1) * RULESET_WIDTH + (x -1)],
|
||||
ruleset[(y +1) * RULESET_WIDTH + (x )],
|
||||
ruleset[(y +1) * RULESET_WIDTH + (x +1)],
|
||||
]
|
||||
|
||||
samples.add(sample)
|
||||
|
||||
var result: Array[PackedInt32Array] = []
|
||||
result.assign(samples.elements())
|
||||
return result
|
||||
|
||||
func draw_map_data(chunk: Chunk) -> void:
|
||||
#hacky
|
||||
var fix = func(x) -> int:
|
||||
match x:
|
||||
0x000000: return 0
|
||||
0xFF0000: return 1
|
||||
0x00FF00: return 2
|
||||
0x0000FF: return 3
|
||||
_: return -1
|
||||
|
||||
for x in range(Chunk.CHUNK_WIDTH):
|
||||
for y in range(Chunk.CHUNK_HEIGHT):
|
||||
map.set_cell(Vector2i(x, y), fix.call(chunk.data[y * Chunk.CHUNK_WIDTH + x]), Vector2i.ZERO)
|
||||
map.set_cell(Vector2i(x, y), chunk.data[y * Chunk.CHUNK_WIDTH + x], Vector2i.ZERO)
|
||||
|
||||
BIN
sample2.png
BIN
sample2.png
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
sample3.png
Normal file
BIN
sample3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 618 B |
40
sample3.png.import
Normal file
40
sample3.png.import
Normal file
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cvusc1g2mcgj5"
|
||||
path="res://.godot/imported/sample3.png-40d6657997a818597ed152abe7d0ca8d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sample3.png"
|
||||
dest_files=["res://.godot/imported/sample3.png-40d6657997a818597ed152abe7d0ca8d.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
|
||||
@@ -29,10 +29,10 @@ texture_region_size = Vector2i(32, 32)
|
||||
|
||||
[sub_resource type="TileSet" id="TileSet_gu47o"]
|
||||
tile_size = Vector2i(32, 32)
|
||||
sources/0 = SubResource("TileSetAtlasSource_5juve")
|
||||
sources/1 = SubResource("TileSetAtlasSource_fy5k1")
|
||||
sources/2 = SubResource("TileSetAtlasSource_5c1cw")
|
||||
sources/3 = SubResource("TileSetAtlasSource_akxrc")
|
||||
sources/4 = SubResource("TileSetAtlasSource_akxrc")
|
||||
sources/3 = SubResource("TileSetAtlasSource_5c1cw")
|
||||
sources/2 = SubResource("TileSetAtlasSource_fy5k1")
|
||||
sources/1 = SubResource("TileSetAtlasSource_5juve")
|
||||
|
||||
[node name="Scene" type="Node2D"]
|
||||
|
||||
|
||||
@@ -12,20 +12,26 @@ func _reset_entropy() -> void:
|
||||
entropy_total.clear()
|
||||
entropy_total.resize(Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT)
|
||||
entropy_total.fill(-1)
|
||||
|
||||
|
||||
func _update_entropy(chunk: Chunk, samples: Array[PackedInt32Array]) -> void:
|
||||
#iterate over indeterminate tiles to find their valid neighbours
|
||||
for x in range(Chunk.CHUNK_WIDTH):
|
||||
for y in range((Chunk.CHUNK_HEIGHT)):
|
||||
if entropy_total[y * Chunk.CHUNK_WIDTH + x] < 0: #There's some excess multiplication in this code, but I want to get it right first
|
||||
entropy_valid[y * Chunk.CHUNK_WIDTH + x] = _find_valid_samples_at(chunk, x, y, samples) #this overrides any pre-existing cached data
|
||||
entropy_total[y * Chunk.CHUNK_WIDTH + x] = entropy_valid[y * Chunk.CHUNK_WIDTH + x].size() #the entropy is stored separately or faster processing
|
||||
var index: int = y * Chunk.CHUNK_WIDTH + x #reduce number crunching
|
||||
|
||||
if chunk.data[index] > 0: #if the tile is already set, ignore
|
||||
entropy_total[index] = 0
|
||||
|
||||
if entropy_total[index] < 0:
|
||||
entropy_valid[index] = _find_valid_samples_at(chunk, x, y, samples) #this overrides any pre-existing cached data
|
||||
entropy_total[index] = entropy_valid[index].size() #the entropy is stored separately for faster processing
|
||||
|
||||
func _clear_entropy_at(index: int) -> void:
|
||||
func _zero_entropy_at(chunk: Chunk, index: int) -> void:
|
||||
var clear_at := func (dx: int, dy: int) -> void:
|
||||
var check = index + (dy * Chunk.CHUNK_WIDTH) + (dx)
|
||||
if check >= 0 and check < Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT:
|
||||
if entropy_total[check] > 0: entropy_total[check] = -1 #unset surrounding tiles
|
||||
var idx = index + (dy * Chunk.CHUNK_WIDTH) + (dx)
|
||||
if idx >= 0 and idx < Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT:
|
||||
if entropy_total[idx] > 0 and chunk.data[idx] <= 0: entropy_total[idx] = -1 #unset surrounding tiles
|
||||
|
||||
clear_at.call(-1, -1)
|
||||
clear_at.call( 0, -1)
|
||||
clear_at.call( 1, -1)
|
||||
@@ -40,16 +46,18 @@ func _clear_entropy_at(index: int) -> void:
|
||||
func generate_chunk_at(_x: int, _y: int, chunk_array: Array[Chunk], samples: Array[PackedInt32Array]) -> Chunk:
|
||||
var chunk: Chunk = Chunk.new(_x, _y)
|
||||
_reset_entropy()
|
||||
#chunk.data[0] = 1 #DEBUG: seed
|
||||
_update_entropy(chunk, samples)
|
||||
|
||||
while true:
|
||||
while true: #TODO: would a floodfill approach work better?
|
||||
var index = _find_lowest_entropy_tile_index()
|
||||
if index < 0: break #none found, finished
|
||||
|
||||
print(index, ":", entropy_total[index])
|
||||
|
||||
var s: PackedInt32Array = entropy_valid[index].pick_random()
|
||||
chunk.data[index] = s[4]
|
||||
|
||||
_clear_entropy_at(index)
|
||||
_zero_entropy_at(chunk, index)
|
||||
_update_entropy(chunk, samples)
|
||||
|
||||
chunk_array.append(chunk)
|
||||
@@ -59,18 +67,25 @@ func generate_chunk_at(_x: int, _y: int, chunk_array: Array[Chunk], samples: Arr
|
||||
func _find_lowest_entropy_tile_index() -> int:
|
||||
#find the lowest-entropy tile
|
||||
var lowest: int = -1
|
||||
var lowest_list: Array[int] #smooth out order bias
|
||||
|
||||
for i in range(Chunk.CHUNK_WIDTH * Chunk.CHUNK_HEIGHT):
|
||||
if lowest < 0:
|
||||
if entropy_total[i] <= 0: continue #no options
|
||||
lowest = i
|
||||
lowest_list = [i]
|
||||
continue
|
||||
|
||||
if entropy_total[i] > 0 and entropy_total[i] < entropy_total[lowest]:
|
||||
lowest = i
|
||||
|
||||
#actually update the lowest found
|
||||
if entropy_total[i] > 0:
|
||||
if entropy_total[i] < entropy_total[lowest]:
|
||||
lowest = i
|
||||
lowest_list = [i]
|
||||
elif entropy_total[i] == entropy_total[lowest]:
|
||||
lowest_list.append(i)
|
||||
|
||||
#finished
|
||||
return lowest
|
||||
return lowest_list.pick_random() if lowest_list.size() > 0 else lowest
|
||||
|
||||
func _find_valid_samples_at(chunk: Chunk, tile_x: int, tile_y: int, _samples: Array[PackedInt32Array]) -> Array[PackedInt32Array]:
|
||||
var valid: Array[PackedInt32Array] = []
|
||||
|
||||
63
wfc/rulesets.gd
Normal file
63
wfc/rulesets.gd
Normal file
@@ -0,0 +1,63 @@
|
||||
class_name Rulesets extends Object
|
||||
## These rulesets are loaded from a file and return samples for the generator
|
||||
|
||||
## Different rulesets can have different file dimensions
|
||||
var _size: Vector2i = Vector2i.ZERO
|
||||
|
||||
static func load_samples(filename: String) -> Array[PackedInt32Array]:
|
||||
var obj: Rulesets = Rulesets.new()
|
||||
var ruleset: PackedInt32Array = obj._load_ruleset_file(filename)
|
||||
var samples: Array[PackedInt32Array] = obj._parse_ruleset_to_samples(ruleset)
|
||||
return samples
|
||||
|
||||
## Read the png file, and parse it to a useable ruleset
|
||||
func _load_ruleset_file(filename: String) -> PackedInt32Array:
|
||||
var img: Image = Image.load_from_file(filename)
|
||||
|
||||
img.decompress()
|
||||
_size = img.get_size()
|
||||
|
||||
var png: PackedByteArray = img.get_data()
|
||||
var hex: PackedInt32Array = []
|
||||
|
||||
@warning_ignore("integer_division")
|
||||
var hex_size: int = (png.size() / 3)
|
||||
hex.resize(hex_size)
|
||||
|
||||
for i in range(hex_size): #using RGB8 format
|
||||
var rgb8: int = (png[i * 3] << 16) | (png[i * 3 + 1] << 8) | (png[i * 3 + 2] << 0)
|
||||
match rgb8: #BUGFIX: remapped RGB to values the tilemap can handle
|
||||
0x000000: hex[i] = 1
|
||||
0xFF0000: hex[i] = 2
|
||||
0x00FF00: hex[i] = 3
|
||||
0x0000FF: hex[i] = 4
|
||||
_: hex[i] = 1
|
||||
|
||||
return hex
|
||||
|
||||
## Convert the raw hexcodes to usable WFC samples
|
||||
func _parse_ruleset_to_samples(ruleset: PackedInt32Array) -> Array[PackedInt32Array]:
|
||||
#wrapped in a custom container type
|
||||
var samples: Set = Set.new()
|
||||
|
||||
for x in range(1, _size.x-1):
|
||||
for y in range(1, _size.y-1):
|
||||
var sample: PackedInt32Array = [
|
||||
ruleset[(y -1) * _size.x + (x -1)],
|
||||
ruleset[(y -1) * _size.x + (x )],
|
||||
ruleset[(y -1) * _size.x + (x +1)],
|
||||
|
||||
ruleset[(y ) * _size.x + (x -1)],
|
||||
ruleset[(y ) * _size.x + (x )],
|
||||
ruleset[(y ) * _size.x + (x +1)],
|
||||
|
||||
ruleset[(y +1) * _size.x + (x -1)],
|
||||
ruleset[(y +1) * _size.x + (x )],
|
||||
ruleset[(y +1) * _size.x + (x +1)],
|
||||
]
|
||||
|
||||
samples.add(sample)
|
||||
|
||||
var result: Array[PackedInt32Array] = []
|
||||
result.assign(samples.elements())
|
||||
return result
|
||||
1
wfc/rulesets.gd.uid
Normal file
1
wfc/rulesets.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cb6akqmmarmxa
|
||||
Reference in New Issue
Block a user