steel_core/world/player_spawn_finder/
candidates.rs1use steel_utils::{BlockPos, ChunkPos};
2
3const ABSOLUTE_MAX_ATTEMPTS: i32 = 1024;
4
5#[derive(Clone, Copy)]
7pub(super) struct CandidateSequence {
8 spawn_suggestion: BlockPos,
9 radius: i32,
10 count: i32,
11 coprime: i32,
12 pub(super) offset: i32,
13}
14
15impl CandidateSequence {
16 pub(super) fn new(spawn_suggestion: BlockPos, radius: i32) -> Result<Self, String> {
17 let square_side = i64::from(radius) * 2 + 1;
18 let count = i32::try_from(i64::from(ABSOLUTE_MAX_ATTEMPTS).min(square_side * square_side))
19 .map_err(|e| format!("invalid spawn candidate count: {e}"))?;
20 Ok(Self {
21 spawn_suggestion,
22 radius,
23 count,
24 coprime: get_coprime(count),
25 offset: rand::random_range(0..count),
26 })
27 }
28
29 pub(super) const fn empty(spawn_suggestion: BlockPos) -> Self {
31 Self {
32 spawn_suggestion,
33 radius: 0,
34 count: 0,
35 coprime: 0,
36 offset: 0,
37 }
38 }
39
40 pub(super) const fn count(&self) -> i32 {
41 self.count
42 }
43
44 pub(super) const fn column(&self, index: i32) -> (i32, i32) {
46 let value = (self.offset + self.coprime * index) % self.count;
47 let side = self.radius * 2 + 1;
48 (
49 self.spawn_suggestion.x() + value % side - self.radius,
50 self.spawn_suggestion.z() + value / side - self.radius,
51 )
52 }
53
54 pub(super) const fn chunk(&self, index: i32) -> ChunkPos {
56 let (x, z) = self.column(index);
57 ChunkPos::from_block_pos(BlockPos::new(x, 0, z))
58 }
59}
60
61const fn get_coprime(possible_origins: i32) -> i32 {
62 if possible_origins <= 16 {
63 possible_origins - 1
64 } else {
65 17
66 }
67}