Skip to main content

steel_core/world/player_spawn_finder/
candidates.rs

1use steel_utils::{BlockPos, ChunkPos};
2
3const ABSOLUTE_MAX_ATTEMPTS: i32 = 1024;
4
5/// The shuffled order in which the square around the spawn suggestion is searched.
6#[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    /// A sequence without candidates, which goes straight to the height fixup.
30    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    /// The block column of the candidate at `index`.
45    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    /// The chunk of the candidate at `index`.
55    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}