1 pub mod theme; |
1 pub mod theme; |
2 |
2 |
3 use std::{ |
3 use self::theme::Theme; |
4 collections::hash_map::HashMap, |
4 use integral_geometry::{Point, Rect, Size}; |
5 borrow::Borrow, |
5 use land2d::Land2D; |
6 mem::replace |
6 use landgen::outline_template::OutlineTemplate; |
7 }; |
7 use rand::{thread_rng, Rng}; |
8 use serde_derive::{Deserialize}; |
8 use serde_derive::Deserialize; |
9 use serde_yaml; |
9 use serde_yaml; |
10 use integral_geometry::{Point, Size, Rect}; |
10 use std::{borrow::Borrow, collections::hash_map::HashMap, mem::replace}; |
11 use landgen::{ |
|
12 outline_template::OutlineTemplate |
|
13 }; |
|
14 use rand::{thread_rng, Rng}; |
|
15 use land2d::Land2D; |
|
16 use vec2d::Vec2D; |
11 use vec2d::Vec2D; |
17 use self::theme::Theme; |
|
18 |
12 |
19 #[derive(Deserialize)] |
13 #[derive(Deserialize)] |
20 struct PointDesc { |
14 struct PointDesc { |
21 x: u32, |
15 x: u32, |
22 y: u32 |
16 y: u32, |
23 } |
17 } |
24 |
18 |
25 #[derive(Deserialize)] |
19 #[derive(Deserialize)] |
26 struct RectDesc { |
20 struct RectDesc { |
27 x: u32, |
21 x: u32, |
28 y: u32, |
22 y: u32, |
29 w: u32, |
23 w: u32, |
30 h: u32 |
24 h: u32, |
31 } |
25 } |
32 |
26 |
33 #[derive(Deserialize)] |
27 #[derive(Deserialize)] |
34 struct TemplateDesc { |
28 struct TemplateDesc { |
35 width: usize, |
29 width: usize, |
39 can_mirror: bool, |
33 can_mirror: bool, |
40 is_negative: bool, |
34 is_negative: bool, |
41 put_girders: bool, |
35 put_girders: bool, |
42 max_hedgehogs: u8, |
36 max_hedgehogs: u8, |
43 outline_points: Vec<Vec<RectDesc>>, |
37 outline_points: Vec<Vec<RectDesc>>, |
44 fill_points: Vec<PointDesc> |
38 fill_points: Vec<PointDesc>, |
45 } |
39 } |
46 |
40 |
47 #[derive(Deserialize)] |
41 #[derive(Deserialize)] |
48 struct TemplateCollectionDesc { |
42 struct TemplateCollectionDesc { |
49 templates: Vec<TemplateDesc>, |
43 templates: Vec<TemplateDesc>, |
50 template_types: HashMap<String, Vec<usize>> |
44 template_types: HashMap<String, Vec<usize>>, |
51 } |
45 } |
52 |
46 |
53 impl From<&TemplateDesc> for OutlineTemplate { |
47 impl From<&TemplateDesc> for OutlineTemplate { |
54 fn from(desc: &TemplateDesc) -> Self { |
48 fn from(desc: &TemplateDesc) -> Self { |
55 OutlineTemplate { |
49 OutlineTemplate { |
56 islands: desc.outline_points.iter() |
50 islands: desc |
57 .map(|v| v.iter() |
51 .outline_points |
58 .map(|r| Rect::from_size( |
52 .iter() |
59 Point::new(r.x as i32, r.y as i32), |
53 .map(|v| { |
60 Size::new(r.w as usize, r.h as usize))) |
54 v.iter() |
61 .collect()) |
55 .map(|r| { |
|
56 Rect::from_size( |
|
57 Point::new(r.x as i32, r.y as i32), |
|
58 Size::new(r.w as usize, r.h as usize), |
|
59 ) |
|
60 }) |
|
61 .collect() |
|
62 }) |
62 .collect(), |
63 .collect(), |
63 fill_points: desc.fill_points.iter() |
64 fill_points: desc |
|
65 .fill_points |
|
66 .iter() |
64 .map(|p| Point::new(p.x as i32, p.y as i32)) |
67 .map(|p| Point::new(p.x as i32, p.y as i32)) |
65 .collect(), |
68 .collect(), |
66 size: Size::new(desc.width, desc.height), |
69 size: Size::new(desc.width, desc.height), |
67 can_flip: desc.can_flip, |
70 can_flip: desc.can_flip, |
68 can_invert: desc.can_invert, |
71 can_invert: desc.can_invert, |
69 can_mirror: desc.can_mirror, |
72 can_mirror: desc.can_mirror, |
70 is_negative: desc.is_negative |
73 is_negative: desc.is_negative, |
71 } |
74 } |
72 } |
75 } |
73 } |
76 } |
74 |
77 |
75 #[derive(PartialEq, Eq, Hash, Clone, Debug)] |
78 #[derive(PartialEq, Eq, Hash, Clone, Debug)] |
81 } |
84 } |
82 } |
85 } |
83 |
86 |
84 #[derive(Debug)] |
87 #[derive(Debug)] |
85 pub struct MapGenerator { |
88 pub struct MapGenerator { |
86 pub(crate) templates: HashMap<TemplateType, Vec<OutlineTemplate>> |
89 pub(crate) templates: HashMap<TemplateType, Vec<OutlineTemplate>>, |
87 } |
90 } |
88 |
91 |
89 impl MapGenerator { |
92 impl MapGenerator { |
90 pub fn new() -> Self { |
93 pub fn new() -> Self { |
91 Self { templates: HashMap::new() } |
94 Self { |
|
95 templates: HashMap::new(), |
|
96 } |
92 } |
97 } |
93 |
98 |
94 pub fn import_yaml_templates(&mut self, text: &str) { |
99 pub fn import_yaml_templates(&mut self, text: &str) { |
95 let mut desc: TemplateCollectionDesc = serde_yaml::from_str(text).unwrap(); |
100 let mut desc: TemplateCollectionDesc = serde_yaml::from_str(text).unwrap(); |
96 let templates = replace(&mut desc.templates, vec![]); |
101 let templates = replace(&mut desc.templates, vec![]); |
97 self.templates = desc.template_types.into_iter() |
102 self.templates = desc |
98 .map(|(size, indices)| |
103 .template_types |
99 (TemplateType(size), indices.iter() |
104 .into_iter() |
100 .map(|i| (&templates[*i]).into()) |
105 .map(|(size, indices)| { |
101 .collect())) |
106 ( |
|
107 TemplateType(size), |
|
108 indices.iter().map(|i| (&templates[*i]).into()).collect(), |
|
109 ) |
|
110 }) |
102 .collect(); |
111 .collect(); |
103 } |
112 } |
104 |
113 |
105 pub fn get_template(&self, template_type: &str) -> Option<&OutlineTemplate> { |
114 pub fn get_template(&self, template_type: &str) -> Option<&OutlineTemplate> { |
106 self.templates.get(template_type).and_then(|t| thread_rng().choose(t)) |
115 self.templates |
107 } |
116 .get(template_type) |
108 |
117 .and_then(|t| thread_rng().choose(t)) |
109 pub fn make_texture(&self, land: &Land2D<u8>, theme: &Theme) -> Vec2D<u32> { |
118 } |
|
119 |
|
120 pub fn make_texture<LandT>(&self, land: &Land2D<LandT>, theme: &Theme) -> Vec2D<u32> |
|
121 where |
|
122 LandT: Copy + Default + PartialEq, |
|
123 { |
110 let mut texture = Vec2D::new(land.size(), 0); |
124 let mut texture = Vec2D::new(land.size(), 0); |
111 |
125 |
112 if let Some(land_sprite) = theme.land_texture() { |
126 if let Some(land_sprite) = theme.land_texture() { |
113 for (row_index, (land_row, tex_row)) in land.rows() |
127 for (row_index, (land_row, tex_row)) in land.rows().zip(texture.rows_mut()).enumerate() |
114 .zip(texture.rows_mut()) |
|
115 .enumerate() |
|
116 { |
128 { |
117 let sprite_row = land_sprite.get_row(row_index % land_sprite.height()); |
129 let sprite_row = land_sprite.get_row(row_index % land_sprite.height()); |
118 let mut x_offset = 0; |
130 let mut x_offset = 0; |
119 while sprite_row.len() < land.width() - x_offset { |
131 while sprite_row.len() < land.width() - x_offset { |
120 let copy_range = x_offset..x_offset + sprite_row.len(); |
132 let copy_range = x_offset..x_offset + sprite_row.len(); |
121 tex_row_copy( |
133 tex_row_copy( |
122 &land_row[copy_range.clone()], |
134 &land_row[copy_range.clone()], |
123 &mut tex_row[copy_range], |
135 &mut tex_row[copy_range], |
124 sprite_row |
136 sprite_row, |
125 ); |
137 ); |
126 |
138 |
127 x_offset += land_sprite.width() |
139 x_offset += land_sprite.width() |
128 } |
140 } |
129 |
141 |
130 if x_offset < land.width() { |
142 if x_offset < land.width() { |
131 let final_range = x_offset..land.width(); |
143 let final_range = x_offset..land.width(); |
132 tex_row_copy( |
144 tex_row_copy( |
133 &land_row[final_range.clone()], |
145 &land_row[final_range.clone()], |
134 &mut tex_row[final_range], |
146 &mut tex_row[final_range], |
135 &sprite_row[..land.width() - x_offset] |
147 &sprite_row[..land.width() - x_offset], |
136 ); |
148 ); |
137 } |
149 } |
138 } |
150 } |
139 } |
151 } |
140 |
152 |
147 |
159 |
148 land_border_pass( |
160 land_border_pass( |
149 land.rows().rev().zip(texture.rows_mut().rev()), |
161 land.rows().rev().zip(texture.rows_mut().rev()), |
150 &mut offsets, |
162 &mut offsets, |
151 border_width, |
163 border_width, |
152 |x, y| border_sprite.get_pixel( |
164 |x, y| { |
153 x % border_sprite.width(), |
165 border_sprite |
154 border_sprite.height() - 1 - y, |
166 .get_pixel(x % border_sprite.width(), border_sprite.height() - 1 - y) |
155 ) |
167 }, |
156 ); |
168 ); |
157 |
169 |
158 offsets.iter_mut().for_each(|v| *v = 255); |
170 offsets.iter_mut().for_each(|v| *v = 255); |
159 |
171 |
160 land_border_pass( |
172 land_border_pass( |
161 land.rows().zip(texture.rows_mut()), |
173 land.rows().zip(texture.rows_mut()), |
162 &mut offsets, |
174 &mut offsets, |
163 border_width, |
175 border_width, |
164 |x, y| border_sprite.get_pixel( |
176 |x, y| border_sprite.get_pixel(x % border_sprite.width(), y), |
165 x % border_sprite.width(), |
|
166 y, |
|
167 ) |
|
168 ); |
|
169 } |
|
170 |
|
171 texture |
|
172 } |
|
173 |
|
174 // TODO: no way to pass both u8 & u32? |
|
175 pub fn make_texture32(&self, land: &Land2D<u32>, theme: &Theme) -> Vec2D<u32> { |
|
176 let mut texture = Vec2D::new(land.size(), 0); |
|
177 |
|
178 if let Some(land_sprite) = theme.land_texture() { |
|
179 for (row_index, (land_row, tex_row)) in land.rows() |
|
180 .zip(texture.rows_mut()) |
|
181 .enumerate() |
|
182 { |
|
183 let sprite_row = land_sprite.get_row(row_index % land_sprite.height()); |
|
184 let mut x_offset = 0; |
|
185 while sprite_row.len() < land.width() - x_offset { |
|
186 let copy_range = x_offset..x_offset + sprite_row.len(); |
|
187 tex_row_copy32( |
|
188 &land_row[copy_range.clone()], |
|
189 &mut tex_row[copy_range], |
|
190 sprite_row |
|
191 ); |
|
192 |
|
193 x_offset += land_sprite.width() |
|
194 } |
|
195 |
|
196 if x_offset < land.width() { |
|
197 let final_range = x_offset..land.width(); |
|
198 tex_row_copy32( |
|
199 &land_row[final_range.clone()], |
|
200 &mut tex_row[final_range], |
|
201 &sprite_row[..land.width() - x_offset] |
|
202 ); |
|
203 } |
|
204 } |
|
205 } |
|
206 |
|
207 if let Some(border_sprite) = theme.border_texture() { |
|
208 assert!(border_sprite.height() <= 512); |
|
209 let border_width = (border_sprite.height() / 2) as u8; |
|
210 let border_sprite = border_sprite.to_tiled(); |
|
211 |
|
212 let mut offsets = vec![255u8; land.width()]; |
|
213 |
|
214 land_border_pass32( |
|
215 land.rows().rev().zip(texture.rows_mut().rev()), |
|
216 &mut offsets, |
|
217 border_width, |
|
218 |x, y| border_sprite.get_pixel( |
|
219 x % border_sprite.width(), |
|
220 border_sprite.height() - 1 - y, |
|
221 ) |
|
222 ); |
|
223 |
|
224 offsets.iter_mut().for_each(|v| *v = 255); |
|
225 |
|
226 land_border_pass32( |
|
227 land.rows().zip(texture.rows_mut()), |
|
228 &mut offsets, |
|
229 border_width, |
|
230 |x, y| border_sprite.get_pixel( |
|
231 x % border_sprite.width(), |
|
232 y, |
|
233 ) |
|
234 ); |
177 ); |
235 } |
178 } |
236 |
179 |
237 texture |
180 texture |
238 } |
181 } |
277 let green = lerp(target.green(), source.green(), source.alpha()); |
220 let green = lerp(target.green(), source.green(), source.alpha()); |
278 let blue = lerp(target.blue(), source.blue(), source.alpha()); |
221 let blue = lerp(target.blue(), source.blue(), source.alpha()); |
279 (red as u32) << 0 | (green as u32) << 8 | (blue as u32) << 16 | (alpha as u32) << 24 |
222 (red as u32) << 0 | (green as u32) << 8 | (blue as u32) << 16 | (alpha as u32) << 24 |
280 } |
223 } |
281 |
224 |
282 fn land_border_pass<'a, T, F>(rows: T, offsets: &mut [u8], border_width: u8, pixel_getter: F) |
225 fn land_border_pass<'a, LandT, T, F>(rows: T, offsets: &mut [u8], border_width: u8, pixel_getter: F) |
283 where T: Iterator<Item = (&'a [u8], &'a mut [u32])>, |
226 where |
284 F: (Fn(usize, usize) -> u32) |
227 LandT: Default + PartialEq + 'a, |
|
228 T: Iterator<Item = (&'a [LandT], &'a mut [u32])>, |
|
229 F: (Fn(usize, usize) -> u32), |
285 { |
230 { |
286 for (land_row, tex_row) in rows { |
231 for (land_row, tex_row) in rows { |
287 for (x, ((land_v, tex_v), offset_v)) in land_row.iter() |
232 for (x, ((land_v, tex_v), offset_v)) in land_row |
|
233 .iter() |
288 .zip(tex_row.iter_mut()) |
234 .zip(tex_row.iter_mut()) |
289 .zip(offsets.iter_mut()) |
235 .zip(offsets.iter_mut()) |
290 .enumerate() |
236 .enumerate() |
291 { |
237 { |
292 *offset_v = if *land_v == 0 { |
238 *offset_v = if *land_v == LandT::default() { |
293 if *offset_v < border_width { |
239 if *offset_v < border_width { |
294 *tex_v = blend( |
240 *tex_v = blend(pixel_getter(x, *offset_v as usize), *tex_v) |
295 pixel_getter(x, *offset_v as usize), |
|
296 *tex_v, |
|
297 ) |
|
298 } |
241 } |
299 offset_v.saturating_add(1) |
242 offset_v.saturating_add(1) |
300 } else { |
243 } else { |
301 0 |
244 0 |
302 } |
245 } |
303 } |
246 } |
304 } |
247 } |
305 } |
248 } |
306 |
249 |
307 fn land_border_pass32<'a, T, F>(rows: T, offsets: &mut [u8], border_width: u8, pixel_getter: F) |
250 fn tex_row_copy<LandT>(land_row: &[LandT], tex_row: &mut [u32], sprite_row: &[u32]) |
308 where T: Iterator<Item = (&'a [u32], &'a mut [u32])>, |
251 where |
309 F: (Fn(usize, usize) -> u32) |
252 LandT: Default + PartialEq, |
310 { |
253 { |
311 for (land_row, tex_row) in rows { |
254 for ((land_v, tex_v), sprite_v) in land_row.iter().zip(tex_row.iter_mut()).zip(sprite_row) { |
312 for (x, ((land_v, tex_v), offset_v)) in land_row.iter() |
255 *tex_v = if *land_v == LandT::default() { |
313 .zip(tex_row.iter_mut()) |
|
314 .zip(offsets.iter_mut()) |
|
315 .enumerate() |
|
316 { |
|
317 *offset_v = if *land_v == 0 { |
|
318 if *offset_v < border_width { |
|
319 *tex_v = blend( |
|
320 pixel_getter(x, *offset_v as usize), |
|
321 *tex_v, |
|
322 ) |
|
323 } |
|
324 offset_v.saturating_add(1) |
|
325 } else { |
|
326 0 |
|
327 } |
|
328 } |
|
329 } |
|
330 } |
|
331 |
|
332 fn tex_row_copy(land_row: &[u8], tex_row: &mut [u32], sprite_row: &[u32]) { |
|
333 for ((land_v, tex_v), sprite_v) in |
|
334 land_row.iter().zip(tex_row.iter_mut()).zip(sprite_row) |
|
335 { |
|
336 *tex_v = if *land_v == 0 { |
|
337 *sprite_v |
256 *sprite_v |
338 } else { |
257 } else { |
339 0 |
258 0 |
340 } |
259 } |
341 } |
260 } |
342 } |
261 } |
343 |
262 |
344 fn tex_row_copy32(land_row: &[u32], tex_row: &mut [u32], sprite_row: &[u32]) { |
|
345 for ((land_v, tex_v), sprite_v) in |
|
346 land_row.iter().zip(tex_row.iter_mut()).zip(sprite_row) |
|
347 { |
|
348 *tex_v = if *land_v == 0 { |
|
349 *sprite_v |
|
350 } else { |
|
351 0 |
|
352 } |
|
353 } |
|
354 } |
|
355 |
|
356 #[cfg(test)] |
263 #[cfg(test)] |
357 mod tests { |
264 mod tests { |
358 use crate::{ |
265 use crate::{MapGenerator, TemplateType}; |
359 MapGenerator, |
|
360 TemplateType |
|
361 }; |
|
362 |
266 |
363 #[test] |
267 #[test] |
364 fn simple_load() { |
268 fn simple_load() { |
365 let text = r#" |
269 let text = r#" |
366 # comment |
270 # comment |