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
use crate::{Chunk, Effect, PcHandle, PlayerCharacter, WorldInstant, CHUNKS_X, CHUNKS_Y};

// Used in docs.
#[allow(unused_imports)]
use crate::ChunkUpdate;

/// Contains all the game state.
#[derive(Clone)]
pub struct World {
    /// The timestamp of the world currently.
    pub time: WorldInstant,
    /// The chunks the world consists of. The server's chunks will never be
    /// [None], but the client's will. Only the chunks around the player
    /// character will be [Some] on the client-side, to save on network
    /// bandwidth and client-side performance.
    pub chunks: Vec<Option<Chunk>>,
}

impl World {
    /// Creates an empty world at time 0. A starting point for a client to apply
    /// [ChunkUpdate]s to, as they come from the server.
    pub fn empty() -> World {
        World {
            time: 0,
            chunks: vec![None; (CHUNKS_X * CHUNKS_Y) as usize],
        }
    }

    /// Applies the given effects to the chunk, and returns false if the chunk
    /// being affected is not loaded.
    #[must_use]
    pub fn apply_effects(&mut self, chunk_index: usize, chunk_effects: Vec<Effect>) -> bool {
        assert!(chunk_index < self.chunks.len());
        if let Some(chunk) = &mut self.chunks[chunk_index] {
            for effect in chunk_effects {
                effect.apply_effect(self.time, chunk);
            }
            true
        } else {
            false
        }
    }

    /// Does deterministic simulation of the game state for one time step, and
    /// increments [World::time] by 1.
    pub fn update(&mut self) {
        for pc in self
            .chunks
            .iter_mut()
            .filter_map(|chunk| chunk.as_mut())
            .flat_map(|chunk| chunk.pcs.values_mut())
        {
            if let Some(current_move) = pc.current_move.take() {
                if self.time < current_move.start_frame + current_move.duration {
                    pc.current_move = Some(current_move);
                }
            }
        }
        self.time += 1;
    }

    /// Returns the player character and the index of the chunk where the player
    /// character is.
    pub fn get_player_character(&self, handle: PcHandle) -> Option<(usize, &PlayerCharacter)> {
        self.chunks
            .iter()
            .enumerate()
            .filter_map(|(i, chunk)| Some((i, chunk.as_ref()?)))
            .flat_map(|(i, chunk)| chunk.pcs.get(&handle).map(|pc| (i, pc)))
            .next()
    }
}