1use {
2 simple_tiled_wfc::grid_generation::{WfcModule, WfcContext, WfcContextBuilder},
3 macroquad::prelude::{
4 scene::{Node, RefMut},
5 *
6 },
7 macroquad::miniquad::{TextureParams, TextureFormat, TextureWrap},
8 ron::de::from_reader,
9 std::{
10 collections::{VecDeque},
11 cmp::Ordering,
12 sync::mpsc::channel
13 }
14};
15mod serialization {
16 use serde::Deserialize;
17 use std::collections::HashMap;
18
19 #[derive(Debug, Deserialize, Clone, Copy)]
20 pub struct SubRect {
21 pub x: i32,
22 pub y: i32,
23 pub width: i32,
24 pub height: i32
25 }
26
27 #[derive(Debug, Deserialize, PartialEq, Eq, Clone, Copy)]
28 pub enum TileKind {
29 Wang4Corner(u8), VerticalBridgeGroundVoid0x0,
35 VerticalBridgeGroundVoid0x1,
36 VerticalBridgeGroundVoid0x2,
37 VerticalBridgeGroundVoid1x0,
38 VerticalBridgeGroundVoid1x1,
39 VerticalBridgeGroundVoid1x2,
40 VerticalBridgeGroundVoid2x0,
41 VerticalBridgeGroundVoid2x1,
42 VerticalBridgeGroundVoid2x2,
43 VerticalBridgeWaterGround0x0,
44 VerticalBridgeWaterGround0x1,
45 VerticalBridgeWaterGround0x2,
46 VerticalBridgeWaterGround1x0,
47 VerticalBridgeWaterGround1x1,
48 VerticalBridgeWaterGround1x2,
49 VerticalBridgeWaterGround2x0,
50 VerticalBridgeWaterGround2x1,
51 VerticalBridgeWaterGround2x2,
52 HorizontalBridgeGroundVoid0x0,
53 HorizontalBridgeGroundVoid0x1,
54 HorizontalBridgeGroundVoid0x2,
55 HorizontalBridgeGroundVoid1x0,
56 HorizontalBridgeGroundVoid1x1,
57 HorizontalBridgeGroundVoid1x2,
58 HorizontalBridgeGroundVoid2x0,
59 HorizontalBridgeGroundVoid2x1,
60 HorizontalBridgeGroundVoid2x2,
61 HorizontalBridgeWaterGround0x0,
62 HorizontalBridgeWaterGround0x1,
63 HorizontalBridgeWaterGround0x2,
64 HorizontalBridgeWaterGround1x0,
65 HorizontalBridgeWaterGround1x1,
66 HorizontalBridgeWaterGround1x2,
67 HorizontalBridgeWaterGround2x0,
68 HorizontalBridgeWaterGround2x1,
69 HorizontalBridgeWaterGround2x2,
70 }
71
72 #[derive(Debug, Deserialize, Clone)]
73 pub struct Tile {
74 pub kind: TileKind,
75 pub subrects: Vec<SubRect>,
76 pub neighbours_east: Vec<TileKind>,
77 pub neighbours_west: Vec<TileKind>,
78 pub neighbours_north: Vec<TileKind>,
79 pub neighbours_south: Vec<TileKind>
80 }
81
82 #[derive(Debug, Deserialize)]
83 pub struct DungeonTiles {
84 pub tile_width: usize,
85 pub tile_height: usize,
86 pub wang_4_corner_tiles: HashMap<u8, Vec<SubRect>>,
87 pub extra_tiles: Vec<Tile>
88 }
89}
90use serialization::*;
91
92pub const fn get_north_east(kind_code: u8) -> u8 { kind_code & 0b11 }
93pub const fn get_north_west(kind_code: u8) -> u8 { (kind_code / 0b100) & 0b11 }
94pub const fn get_south_east(kind_code: u8) -> u8 { (kind_code / 0b10000) & 0b11 }
95pub const fn get_south_west(kind_code: u8) -> u8 { (kind_code / 0b1000000) & 0b11 }
96
97fn draw_subrect(tex: Texture2D, subrect: &SubRect, x: f32, y: f32, scale: usize) {
98 let InternalGlContext {
99 quad_context: ctx, ..
100 } = unsafe { get_internal_gl() };
101 draw_texture_ex(
102 tex,
103 x * ctx.dpi_scale(), y * ctx.dpi_scale(),
104 WHITE,
105 DrawTextureParams {
106 source: Some(Rect::new(
107 subrect.x as f32, subrect.y as f32,
108 subrect.width as f32, subrect.height as f32
109 )),
110 dest_size: Some([
111 subrect.width as f32 * ctx.dpi_scale() * scale as f32,
112 subrect.height as f32 * ctx.dpi_scale() * scale as f32
113 ].into()),
114 ..Default::default()
115 },
116 );
117}
118
119fn window_conf() -> Conf {
120 Conf {
121 window_title: "Dungeon".to_owned(),
122 fullscreen: false,
123 window_width: 1280,
124 window_height: 800,
125 high_dpi: true,
126 ..Default::default()
127 }
128}
129
130type CustomBitSet = [u8; 8];
131
132struct DungeonTilemap {
133 w: usize,
134 h: usize,
135 tile_width: usize,
136 tile_height: usize,
137 tiles: Vec<Tile>,
138 modules: Vec<WfcModule<CustomBitSet>>,
139 texture: Texture2D,
140 map_data: Vec<(usize, usize)>
141}
142
143impl DungeonTilemap {
144 pub async fn new(w: usize, h: usize) -> Self {
145 let InternalGlContext {
146 quad_context: ctx, ..
147 } = unsafe { get_internal_gl() };
148
149 let dungeon_bytes = load_file("assets/dungeon_tiles.png").await.unwrap();
150
151 let img = image::load_from_memory(&dungeon_bytes[..])
152 .unwrap_or_else(|e| panic!("{}", e))
153 .to_rgba8();
154
155 let img_w = img.width();
156 let img_h = img.height();
157
158 let texture = Texture2D::from_miniquad_texture(
159 miniquad::Texture::from_data_and_format(
160 ctx,
161 &img.into_raw(),
162 TextureParams {
163 format: TextureFormat::RGBA8,
164 wrap: TextureWrap::Clamp,
165 filter: FilterMode::Nearest,
166 width: img_w,
167 height: img_h
168 }
169 )
170 );
171
172 let tiles_bytes = load_file("assets/dungeon_tiles.ron").await.unwrap();
173 let dungeon_tiles: DungeonTiles = from_reader(&tiles_bytes[..]).unwrap();
174
175 let mut tiles = Vec::new();
176 for (kind_code, subrects) in dungeon_tiles.wang_4_corner_tiles.iter() {
177 tiles.push(Tile {
178 kind: TileKind::Wang4Corner(*kind_code),
179 subrects: subrects.clone(),
180 neighbours_east: Vec::new(),
181 neighbours_west: Vec::new(),
182 neighbours_north: Vec::new(),
183 neighbours_south: Vec::new()
184 });
185 }
186 tiles.sort_by(|lhs, rhs| {
188 match (lhs.kind, rhs.kind) {
189 (TileKind::Wang4Corner(kind_lhs), TileKind::Wang4Corner(kind_rhs)) => kind_lhs.cmp(&kind_rhs),
190 _ => Ordering::Equal
191 }
192 });
193
194 for i in 0..tiles.len() {
195 let current_kind = tiles[i].kind;
196 for j in 0..tiles.len() {
197 let candidate_kind = tiles[j].kind;
198 let (matches_north, matches_south, matches_east, matches_west) = {
199 match (current_kind, candidate_kind) {
200 (TileKind::Wang4Corner(current), TileKind::Wang4Corner(candidate)) => {
201 (
202 get_north_east(current) == get_south_east(candidate) &&
203 get_north_west(current) == get_south_west(candidate),
204
205 get_south_east(current) == get_north_east(candidate) &&
206 get_south_west(current) == get_north_west(candidate),
207
208 get_north_east(current) == get_north_west(candidate) &&
209 get_south_east(current) == get_south_west(candidate),
210
211 get_north_west(current) == get_north_east(candidate) &&
212 get_south_west(current) == get_south_east(candidate),
213 )
214 },
215 _ => continue
216 }
217 };
218 if matches_east {
219 tiles[i].neighbours_east.push(candidate_kind);
220 }
221 if matches_west {
222 tiles[i].neighbours_west.push(candidate_kind);
223 }
224 if matches_north {
225 tiles[i].neighbours_north.push(candidate_kind);
226 }
227 if matches_south {
228 tiles[i].neighbours_south.push(candidate_kind);
229 }
230 }
231 }
232
233 {
235 let bridge_tiles_offset = tiles.len();
236 tiles.extend_from_slice(&dungeon_tiles.extra_tiles[..]);
237
238 let mut bridge_match_queue_south = VecDeque::new();
239 let mut bridge_match_queue_north = VecDeque::new();
240 let mut bridge_match_queue_east = VecDeque::new();
241 let mut bridge_match_queue_west = VecDeque::new();
242
243 for bridge_tile in &tiles[bridge_tiles_offset..] {
244 for south_neighbour in bridge_tile.neighbours_south.iter() {
245 if let TileKind::Wang4Corner(kind) = south_neighbour {
246 for i in 0..bridge_tiles_offset {
247 match tiles[i].kind {
248 TileKind::Wang4Corner(kind_inner) if kind_inner == *kind => {
249 bridge_match_queue_south.push_back((bridge_tile.kind, i))
250 },
251 _ => ()
252 };
253 }
254 }
255 }
256 for north_neighbour in bridge_tile.neighbours_north.iter() {
257 if let TileKind::Wang4Corner(kind) = north_neighbour {
258 for i in 0..bridge_tiles_offset {
259 match tiles[i].kind {
260 TileKind::Wang4Corner(kind_inner) if kind_inner == *kind => {
261 bridge_match_queue_north.push_back((bridge_tile.kind, i))
262 },
263 _ => ()
264 };
265 }
266 }
267 }
268 for east_neighbour in bridge_tile.neighbours_east.iter() {
269 if let TileKind::Wang4Corner(kind) = east_neighbour {
270 for i in 0..bridge_tiles_offset {
271 match tiles[i].kind {
272 TileKind::Wang4Corner(kind_inner) if kind_inner == *kind => {
273 bridge_match_queue_east.push_back((bridge_tile.kind, i))
274 },
275 _ => ()
276 };
277 }
278 }
279 }
280 for west_neighbour in bridge_tile.neighbours_west.iter() {
281 if let TileKind::Wang4Corner(kind) = west_neighbour {
282 for i in 0..bridge_tiles_offset {
283 match tiles[i].kind {
284 TileKind::Wang4Corner(kind_inner) if kind_inner == *kind => {
285 bridge_match_queue_west.push_back((bridge_tile.kind, i))
286 },
287 _ => ()
288 };
289 }
290 }
291 }
292 }
293
294 while !bridge_match_queue_south.is_empty() {
295 let next_match = bridge_match_queue_south.pop_front().unwrap();
296 tiles[next_match.1].neighbours_north.push(next_match.0);
297 }
298
299 while !bridge_match_queue_north.is_empty() {
300 let next_match = bridge_match_queue_north.pop_front().unwrap();
301 tiles[next_match.1].neighbours_south.push(next_match.0);
302 }
303
304 while !bridge_match_queue_east.is_empty() {
305 let next_match = bridge_match_queue_east.pop_front().unwrap();
306 tiles[next_match.1].neighbours_west.push(next_match.0);
307 }
308
309 while !bridge_match_queue_west.is_empty() {
310 let next_match = bridge_match_queue_west.pop_front().unwrap();
311 tiles[next_match.1].neighbours_east.push(next_match.0);
312 }
313 }
314
315 let modules = tiles
316 .iter()
317 .map(|tile| {
318 let mut module: WfcModule<CustomBitSet> = WfcModule::new();
319 for i in 0..tiles.len() {
320 if tile.neighbours_north.contains(&tiles[i].kind) {
321 module.add_north_neighbour(i);
322 }
323 if tile.neighbours_south.contains(&tiles[i].kind) {
324 module.add_south_neighbour(i);
325 }
326 if tile.neighbours_west.contains(&tiles[i].kind) {
327 module.add_west_neighbour(i);
328 }
329 if tile.neighbours_east.contains(&tiles[i].kind) {
330 module.add_east_neighbour(i);
331 }
332 }
333 module
334 })
335 .collect::<Vec<_>>();
336
337 Self {
338 w,
339 h,
340 tile_width: dungeon_tiles.tile_width,
341 tile_height: dungeon_tiles.tile_height,
342 tiles,
343 modules,
344 texture,
345 map_data: vec![(0, 0); w*h]
346 }
347 }
348
349 pub fn generate_new_map(&mut self) {
350 let mut wfc_context: WfcContext<CustomBitSet> = WfcContextBuilder
351 ::new(&self.modules, self.w, self.h)
352 .build();
353
354 let (tx, rc) = channel();
355
356 {
358 wfc_context.set_module(0, 0, 0);
359 wfc_context.set_module(self.h - 1, 0, 0);
360 wfc_context.set_module(0, self.w - 1, 0);
361 wfc_context.set_module(self.h - 1, self.w - 1, 0);
362 wfc_context.set_module(self.h / 2, self.w / 2, 0);
363 }
364
365 wfc_context.collapse(100, tx.clone());
366
367 let results = rc.recv()
368 .unwrap()
369 .unwrap_or_else(|_| vec![0; self.w * self.h]);
370
371 self.map_data.clear();
372 for &idx in results.iter() {
373 let random_id = rand::gen_range(0, self.tiles[idx].subrects.len());
374 self.map_data.push((idx, random_id));
375 }
376 }
377}
378
379impl Node for DungeonTilemap {
380 fn update(mut node: RefMut<Self>) {
381 if is_key_pressed(KeyCode::Space) {
382 node.generate_new_map();
383 }
384 }
385
386 fn draw(node: RefMut<Self>) {
387 let InternalGlContext {
388 quad_context: ctx, ..
389 } = unsafe { get_internal_gl() };
390
391 const SCALE_UP: usize = 2;
392 let start_x = screen_width() / ctx.dpi_scale();
393 let start_x = (start_x - (node.w * node.tile_width * SCALE_UP) as f32) / 2.0;
394
395 let start_y = screen_height() / ctx.dpi_scale();
396 let start_y = (start_y - (node.h * node.tile_height * SCALE_UP) as f32) / 2.0;
397
398 for j in 0..node.h {
399 for i in 0..node.w {
400 let idx = node.w * j + i;
401 let x = start_x + (node.tile_width * i * SCALE_UP) as f32;
402 let y = start_y + (node.tile_height * j * SCALE_UP) as f32;
403 let (idx, random_id) = node.map_data[idx];
404 draw_subrect(node.texture, &node.tiles[idx].subrects[random_id], x, y, SCALE_UP);
405 }
406 }
407 }
408}
409
410#[macroquad::main(window_conf)]
411async fn main() {
412 scene::add_node({
413 let mut tilemap = DungeonTilemap::new(48, 48).await;
414 tilemap.generate_new_map();
415 tilemap
416 });
417 loop {
418 if is_key_pressed(KeyCode::Escape) {
419 break;
420 }
421 clear_background(Color::new(0.12, 0.1, 0.15, 1.00));
422 next_frame().await;
423 }
424}