1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
# Hurry Curry! - a game about cooking
# Copyright (C) 2025 Hurry Curry! contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
class_name Map
extends Node3D
class TileInfo:
func _init(position_, tiles_, tile_instances_, tile_parent_) -> void:
position = position_; tiles = tiles_; tile_instances = tile_instances_; tile_parent = tile_parent_
var position: Vector2i
var tiles: Array # Array[String]
var tile_instances: Array[Tile]
var tile_parent: Node3D
var interact_tile: Tile
const NEIGHBOR_OFFSETS: Array[Vector2i] = [Vector2i.UP, Vector2i.LEFT, Vector2i.DOWN, Vector2i.RIGHT]
const TILE_COMBINATOR: Dictionary[Array, Array] = {
["counter", "sink"]: ["sink"]
} # : Dictionary[Array[String], Array[String]]
var tile_by_pos: Dictionary[Vector2i, TileInfo] = {}
var autoflush = false
var currently_baked = false
var floor_node := MeshInstance3D.new()
var tile_factory := TileFactory.new()
func get_tiles_at(pos: Vector2i): # -> Array[String]?
var e = tile_by_pos.get(pos)
if e == null: return null
return e.tiles
func get_topmost_instance(pos: Vector2i): # -> Tile?
var e = tile_by_pos.get(pos)
if e == null: return null
return e.tile_instances[-1]
func get_tile_item(pos: Vector2i): # -> Item?
var e = get_topmost_instance(pos)
if e == null: return null
return e.item
func set_all_tiles(changes: Dictionary[Vector2i, Array], srv: Game.ServerContext = null):
for pos: Vector2i in changes:
set_tiles(Vector2i(pos.x, pos.y), changes[pos], changes, srv)
func set_tiles(pos: Vector2i, tiles: Array = [], pending_changes: Dictionary[Vector2i, Array] = {}, srv: Game.ServerContext = null): # tiles: Array[String]
var tile_info = tile_by_pos.get(pos)
if tile_info != null:
for inst: Tile in tile_info.tile_instances:
for tile: String in tiles:
# TODO: Don't return, but handle changes which weren't handled by the instance below.
if inst.change(tile): return # Instance handled change itself!
_remove_tile(pos)
if not tiles.is_empty(): _add_tiles(pos, tiles, pending_changes, srv)
if autoflush: flush()
func _add_tiles(pos: Vector2i, tiles: Array, pending_changes: Dictionary[Vector2i, Array], srv: Game.ServerContext) -> void:
# Combinate tiles
for k in TILE_COMBINATOR:
if G.has_all(tiles, k):
for item: String in k: tiles.erase(item)
tiles.append_array(TILE_COMBINATOR[k])
# Find neighbor tile names
var neighbors: Array[Array] = [] # Array[Array[String]]
for offset: Vector2i in NEIGHBOR_OFFSETS:
var neighbor_pos: Vector2i = pos + offset
if pending_changes.has(neighbor_pos):
neighbors.append(pending_changes[neighbor_pos])
elif tile_by_pos.has(neighbor_pos):
neighbors.append(tile_by_pos[neighbor_pos])
else: neighbors.append([])
var tiles_parent = Node3D.new()
tiles_parent.name = str(pos)
add_child(tiles_parent)
var tile_instances: Array[Tile] = []
for tile_name: String in tiles:
var tile := tile_factory.produce(tile_name, pos, neighbors, tile_instances, srv)
tile_instances.append(tile)
tile.position = Vector3(pos.x, 0, pos.y)
tiles_parent.add_child(tile)
tile_by_pos[pos] = TileInfo.new(pos, tiles, tile_instances, tiles_parent)
func _remove_tile(pos: Vector2i):
var tile_info = tile_by_pos.get(pos)
if tile_info == null: return
var topmost_instance = get_topmost_instance(pos)
if topmost_instance.item != null:
topmost_instance.item.queue_free()
for instance: Tile in tile_info.tile_instances:
if instance is FloorLike:
var floor_mesher = tile_factory.floor_meshers.get(instance.fm_id())
if floor_mesher != null:
floor_mesher.remove_tile(pos)
instance.queue_free()
tile_by_pos.erase(pos)
@onready var voxelgi: VoxelGI = $VoxelGI
func _ready():
Settings.hook_changed("graphics.gi", self, apply_gi_setting)
floor_node.material_override = preload("res://map/tiles/floor_material.tres")
for fm in tile_factory.floor_meshers.values():
add_child(fm.mesh_instance)
add_child(floor_node)
func flush() -> void:
for fm in tile_factory.floor_meshers.values():
fm.flush()
gi_bake()
func apply_gi_setting(state):
if state == "voxelgi" and not currently_baked:
gi_bake()
else:
currently_baked = false
voxelgi.data = null
func gi_bake():
if Settings.read("graphics.gi") != "voxelgi": return
print("Map: Rebaking VoxelGI")
currently_baked = true
gi_bake_blocking()
func gi_bake_blocking():
var map_extents = extents()
var extent_min = map_extents[0]
var extent_max = map_extents[1]
var center = (extent_max + extent_min) / 2
var size = extent_max - extent_min
voxelgi.position = Vector3(center.x, 3., center.y)
voxelgi.size = Vector3(size.x, 8., size.y)
print("Baking now!")
var start = Time.get_ticks_msec()
voxelgi.bake()
voxelgi.visible = true
print("Bake done. elapsed=", Time.get_ticks_msec() - start)
func extents() -> Array[Vector2]:
var extent_min = Vector2(0,0)
var extent_max = Vector2(0,0)
for e: TileInfo in tile_by_pos.values():
extent_min.x = min(extent_min.x, e.position.x)
extent_min.y = min(extent_min.y, e.position.y)
extent_max.x = max(extent_max.x, e.position.x)
extent_max.y = max(extent_max.y, e.position.y)
return [extent_min, extent_max + Vector2(1., 1.)]
|