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
//! Defines a data structure for the realm.
//!
//! This allows custom realms to be defined and used in game. Realms will be loaded from game data and can be set to generate the realm.
//!
//! Currently realms are just a collection of biomes and a primary element. This will likely be expanded into more details about the biomes
//! or possible other elements that can be included in the realm. Since the realm data is what is used to generate the "Elemental Realm" that
//! the player is able to visit, it will be important to have a good amount of detail about the realm.
//!
//! Additional details we will need are possibly special monsters and any pre-designed structures that can be found in the realm. This will
//! allow for a more unique experience when visiting the realm. The realm will also need to have a unique name and description to give it some
//! flavor and make it feel like a unique place to visit.
//!
//! One thing that would be neat is for the player to be able to choose one or maybe two elements, and a random realm matching those elements
//! will be chosen from the loaded realms. This will allow for a bit of randomness in the game and give the player a unique experience each time
//! they visit a realm. This will also allow for the player to have a bit of control over what they are going to face in the realm, as they can
//! choose the elements that they are strongest against (or that they need to gather resources from).
use bevy::prelude::*;
use std::any::Any;
use std::hash::Hash;

use crate::{
    data_loader::DataFile,
    enums::{GameSystem, MagicType},
    BiomeData, InternalId,
};

/// Details about a realm.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Reflect)]
#[serde(rename_all = "camelCase")]
pub struct Realm {
    /// The internal ID of the Realm.
    pub internal_id: Option<String>,
    /// Name of the realm.
    pub name: String,
    /// Short description of the realm.
    pub description: String,
    /// Biomes for the realm.
    pub biomes: Vec<BiomeData>,
    /// The primary element of the realm. (for now limited as magic school but should be its own.)
    pub primary_element: MagicType,
}

impl InternalId for Realm {
    /// Update the realms's internal ID.
    fn update_internal_id(&mut self) {
        self.internal_id = Some(self.get_internal_id());
    }
    /// Get the realms's internal ID.
    #[must_use]
    fn get_internal_id(&self) -> String {
        if self.internal_id.is_some() {
            let id = self.internal_id.clone().unwrap_or_default();
            if !id.is_empty() {
                return id;
            }
        }

        format!(
            "{}{}{}",
            self.name.replace(' ', ""),
            self.biomes.len(),
            self.primary_element
        )
    }
}

impl Default for Realm {
    fn default() -> Self {
        Self {
            internal_id: None,
            name: "Unknown Realm".to_string(),
            description: "A realm that was loaded incorrectly!".to_string(),
            biomes: Vec::new(),
            primary_element: MagicType::Arcane,
        }
    }
}

impl<D: Hash + InternalId + 'static> TryInto<Realm> for DataFile<D> {
    type Error = ();

    fn try_into(self) -> Result<Realm, Self::Error> {
        if self.header.system != GameSystem::Realm {
            return Err(());
        }

        (&self.data as &dyn Any)
            .downcast_ref::<Realm>()
            .cloned()
            .ok_or(())
    }
}

impl<D: Hash + InternalId + 'static> TryFrom<&DataFile<D>> for Realm {
    type Error = ();

    fn try_from(data_file: &DataFile<D>) -> Result<Self, Self::Error> {
        if data_file.header.system != GameSystem::Realm {
            return Err(());
        }

        (&data_file.data as &dyn Any)
            .downcast_ref::<Self>()
            .cloned()
            .ok_or(())
    }
}

impl Hash for Realm {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        self.description.hash(state);
        self.primary_element.hash(state);
    }
}

impl PartialEq for Realm {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
            && self.description == other.description
            && self.primary_element == other.primary_element
    }
}

impl Eq for Realm {}