forked from iMilchshake/gores-mapgen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwalker.rs
200 lines (169 loc) · 6.05 KB
/
walker.rs
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
use crate::{
config::GenerationConfig,
kernel::Kernel,
map::{BlockType, KernelType, Map},
position::{Position, ShiftDirection},
random::Random,
};
// this walker is indeed very cute
#[derive(Debug)]
pub struct CuteWalker {
pub pos: Position,
pub steps: usize,
pub inner_kernel: Kernel,
pub outer_kernel: Kernel,
pub goal: Option<Position>,
pub goal_index: usize,
pub waypoints: Vec<Position>,
/// indicates whether walker has reached the last waypoint
pub finished: bool,
pub steps_since_platform: usize,
pub last_direction: Option<ShiftDirection>,
}
impl CuteWalker {
pub fn new(
initial_pos: Position,
inner_kernel: Kernel,
outer_kernel: Kernel,
config: &GenerationConfig,
) -> CuteWalker {
CuteWalker {
pos: initial_pos,
steps: 0,
inner_kernel,
outer_kernel,
goal: Some(config.waypoints.first().unwrap().clone()),
goal_index: 0,
waypoints: config.waypoints.clone(),
finished: false,
steps_since_platform: 0,
last_direction: None,
}
}
pub fn is_goal_reached(&self, waypoint_reached_dist: &usize) -> Option<bool> {
self.goal
.as_ref()
.map(|goal| goal.distance_squared(&self.pos) <= *waypoint_reached_dist)
}
pub fn next_waypoint(&mut self) {
if let Some(next_goal) = self.waypoints.get(self.goal_index + 1) {
self.goal_index += 1;
self.goal = Some(next_goal.clone());
} else {
self.finished = true;
self.goal = None;
}
}
/// will try to place a platform at the walkers position.
/// If force is true it will enforce a platform.
pub fn check_platform(
&mut self,
map: &mut Map,
min_distance: usize,
max_distance: usize,
) -> Result<(), &'static str> {
self.steps_since_platform += 1;
// Case 1: min distance is not reached -> skip
if self.steps_since_platform < min_distance {
return Ok(());
}
let walker_pos = self.pos.clone();
// Case 2: max distance has been exceeded -> force platform using a room
if self.steps_since_platform > max_distance {
// TODO: for now this is hardcoded so that platform is shifted down by 7 blocks.
map.generate_room(&walker_pos.shifted_by(0, 6)?, 5, 3, None)?;
self.steps_since_platform = 0;
return Ok(());
}
// Case 3: min distance has been exceeded -> Try to place platform, but only if possible
let area_empty = map.check_area_all(
&walker_pos.shifted_by(-3, -3)?,
&walker_pos.shifted_by(3, 2)?,
&BlockType::Empty,
)?;
if area_empty {
map.set_area(
&walker_pos.shifted_by(-1, 0)?,
&walker_pos.shifted_by(1, 0)?,
&BlockType::Platform,
true,
);
self.steps_since_platform = 0;
}
Ok(())
}
pub fn probabilistic_step(
&mut self,
map: &mut Map,
config: &GenerationConfig,
rnd: &mut Random,
) -> Result<(), &'static str> {
if self.finished {
return Err("Walker is finished");
}
let goal = self.goal.as_ref().ok_or("Error: Goal is None")?;
let shifts = self.pos.get_rated_shifts(goal, map);
let mut sampled_shift = &rnd.sample_move(&shifts);
// with a certain probabiliy re-use last direction instead
if rnd.with_probability(config.momentum_prob) && self.last_direction.is_some() {
sampled_shift = self.last_direction.as_ref().unwrap();
}
// apply that shift
self.pos.shift_in_direction(sampled_shift, map)?;
self.steps += 1;
self.last_direction = Some(sampled_shift.clone());
// remove blocks using a kernel at current position
map.update(self, KernelType::Outer)?;
map.update(self, KernelType::Inner)?;
Ok(())
}
pub fn cuddle(&self) {
println!("Cute walker was cuddled!");
}
pub fn mutate_kernel(&mut self, config: &GenerationConfig, rnd: &mut Random) {
let mut inner_size = self.inner_kernel.size;
let mut inner_circ = self.inner_kernel.circularity;
let mut outer_size = self.outer_kernel.size;
let mut outer_circ = self.outer_kernel.circularity;
let mut outer_margin = outer_size - inner_size;
let mut modified = false;
if rnd.with_probability(config.inner_size_mut_prob) {
inner_size = rnd.sample_inner_kernel_size(&config.inner_size_probs);
modified = true;
} else {
rnd.skip_n(2); // for some reason sampling requires two values?
}
if rnd.with_probability(config.outer_size_mut_prob) {
outer_margin = rnd.sample_outer_kernel_margin(&config.outer_margin_probs);
modified = true;
} else {
rnd.skip_n(2);
}
if rnd.with_probability(config.inner_rad_mut_prob) {
inner_circ = *rnd.pick_element(&[0.0, 0.1, 0.2, 0.6, 0.8]); // TODO: this is terrible
modified = true;
} else {
rnd.skip();
}
if rnd.with_probability(config.outer_rad_mut_prob) {
outer_circ = *rnd.pick_element(&[0.0, 0.1, 0.2, 0.6, 0.8]);
modified = true;
} else {
rnd.skip();
}
outer_size = inner_size + outer_margin;
// constraint 1: small circles must be fully rect
if inner_size <= 3 {
inner_circ = 0.0;
}
if outer_size <= 3 {
outer_circ = 0.0;
}
// constraint 2: outer size cannot be smaller than inner
assert!(outer_size >= inner_size); // this shoulnt happen -> crash!
if modified {
self.inner_kernel = Kernel::new(inner_size, inner_circ);
self.outer_kernel = Kernel::new(outer_size, outer_circ);
}
}
}