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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
use crate::prelude::*;

use crate::macroquad::texture::{load_texture, Texture2D};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CharacterClass {
    pub id: String,
    pub prototype_id: String,
    pub name: String,
    pub description: String,
}

fn default_filter_mode() -> FilterMode {
    FilterMode::Nearest
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextureAssetParams {
    pub id: String,
    pub path: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub height_map_path: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub normal_map_path: Option<String>,
    #[serde(default = "default_filter_mode", with = "json::FilterModeDef")]
    pub filter_mode: FilterMode,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageAssetParams {
    pub id: String,
    pub path: String,
    pub format: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FontAssetParams {
    pub id: String,
    pub path: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SoundAssetParams {
    pub id: String,
    pub path: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetsParams {
    materials: Vec<MaterialAssetParams>,
    textures: Vec<TextureAssetParams>,
    images: Vec<ImageAssetParams>,
    fonts: Vec<FontAssetParams>,
    sound_effects: Vec<SoundAssetParams>,
    music: Vec<SoundAssetParams>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UniformAssetParams {
    pub name: String,
    #[serde(rename = "type", with = "json::UniformTypeDef")]
    pub value_type: UniformType,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialAssetParams {
    pub id: String,
    pub vertex_path: String,
    pub fragment_path: String,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub textures: Vec<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub uniforms: Vec<UniformAssetParams>,
}

impl From<MaterialAssetParams> for MaterialParams {
    fn from(params: MaterialAssetParams) -> MaterialParams {
        let textures = params.textures;
        let uniforms = params
            .uniforms
            .into_iter()
            .map(|u| (u.name, u.value_type))
            .collect();

        MaterialParams {
            textures,
            uniforms,
            pipeline_params: Default::default(),
        }
    }
}

pub struct Resources {
    pub actors: HashMap<String, ActorParams>,
    pub character_classes: HashMap<String, CharacterClass>,
    pub items: HashMap<String, ItemParams>,
    pub abilities: HashMap<String, AbilityParams>,
    pub missions: HashMap<String, MissionParams>,
    pub dialogue: HashMap<String, Dialogue>,
    pub chapters: Vec<Chapter>,
    pub materials: HashMap<String, Material>,
    pub textures: HashMap<String, Texture>,
    pub images: HashMap<String, Image>,
    pub font_bytes: HashMap<String, Vec<u8>>,
    pub sound_effects: HashMap<String, Sound>,
    pub music: HashMap<String, Sound>,
}

impl Resources {
    const CLASSES_FILE_NAME: &'static str = "character_classes.json";
    const ACTORS_FILE_NAME: &'static str = "actors.json";
    const ITEMS_FILE_NAME: &'static str = "items.json";
    const MISSIONS_FILE_NAME: &'static str = "missions.json";
    const DIALOGUE_FILE_NAME: &'static str = "dialogue.json";
    const ABILITIES_FILE_NAME: &'static str = "abilities.json";
    const SCENARIO_FILE_NAME: &'static str = "scenario.json";

    const MATERIALS_FILE_NAME: &'static str = "materials.json";
    const TEXTURES_FILE_NAME: &'static str = "textures.json";
    const IMAGES_FILE_NAME: &'static str = "images.json";
    const FONTS_FILE_NAME: &'static str = "fonts.json";
    const SOUND_EFFECTS_FILE_NAME: &'static str = "sound_effects.json";
    const MUSIC_FILE_NAME: &'static str = "music.json";

    pub const WHITE_TEXTURE_ID: &'static str = "__WHITE_TEXTURE__";

    pub async fn new(game_params: &GameParams) -> Result<Self> {
        let data_path = Path::new(&game_params.data_path);
        let assets_path = Path::new(&game_params.assets_path);

        #[cfg(debug_assertions)]
        println!("Resources: Loading character classes");
        let character_classes_path = data_path.join(Self::CLASSES_FILE_NAME);
        let bytes = load_file(&character_classes_path).await?;
        let character_classes_data: Vec<CharacterClass> = serde_json::from_slice(&bytes)?;
        let character_classes = HashMap::from_iter(
            character_classes_data
                .into_iter()
                .map(|class| (class.id.clone(), class)),
        );

        #[cfg(debug_assertions)]
        println!("Resources: Loading actors");
        let actors_file_path = data_path.join(Self::ACTORS_FILE_NAME);
        let bytes = load_file(&actors_file_path).await?;
        let actor_data: Vec<ActorParams> = serde_json::from_slice(&bytes)?;
        let actors = HashMap::from_iter(
            actor_data
                .into_iter()
                .map(|params| (params.id.clone(), params)),
        );

        #[cfg(debug_assertions)]
        println!("Resources: Loading items");
        let items_file_path = data_path.join(Self::ITEMS_FILE_NAME);
        let bytes = load_file(&items_file_path).await?;
        let items_data: Vec<ItemParams> = serde_json::from_slice(&bytes)?;
        let items = HashMap::from_iter(
            items_data
                .into_iter()
                .map(|params| (params.id.clone(), params)),
        );

        #[cfg(debug_assertions)]
        println!("Resources: Loading missions");
        let missions_file_path = data_path.join(Self::MISSIONS_FILE_NAME);
        let bytes = load_file(&missions_file_path).await?;
        let missions_data: Vec<MissionParams> = serde_json::from_slice(&bytes)?;
        let missions = HashMap::from_iter(
            missions_data
                .into_iter()
                .map(|mission| (mission.id.clone(), mission)),
        );

        #[cfg(debug_assertions)]
        println!("Resources: Loading dialogue");
        let dialogue_file_path = data_path.join(Self::DIALOGUE_FILE_NAME);
        let bytes = load_file(&dialogue_file_path).await?;
        let dialogue_data: Vec<Dialogue> = serde_json::from_slice(&bytes)?;
        let dialogue = HashMap::from_iter(
            dialogue_data
                .into_iter()
                .map(|dialogue| (dialogue.id.clone(), dialogue)),
        );

        #[cfg(debug_assertions)]
        println!("Resources: Loading abilities");
        let abilities_file_path = data_path.join(Self::ABILITIES_FILE_NAME);
        let bytes = load_file(&abilities_file_path).await?;
        let ability_data: Vec<AbilityParams> = serde_json::from_slice(&bytes)?;
        let abilities = HashMap::from_iter(
            ability_data
                .into_iter()
                .map(|ability| (ability.id.clone(), ability)),
        );

        #[cfg(debug_assertions)]
        println!("Resources: Loading scenario");
        let scenario_path = data_path.join(Self::SCENARIO_FILE_NAME);
        let bytes = load_file(&scenario_path).await?;
        let chapter_params: Vec<ChapterParams> = serde_json::from_slice(&bytes)?;
        let mut chapters = Vec::new();
        for params in chapter_params {
            let chapter = Chapter::new(game_params, params).await?;
            chapters.push(chapter);
        }

        let materials_file_path = assets_path.join(Self::MATERIALS_FILE_NAME);
        let bytes = load_file(&materials_file_path).await?;
        let material_assets: Vec<MaterialAssetParams> = serde_json::from_slice(&bytes)?;

        #[cfg(debug_assertions)]
        println!("Resources: Loading materials");
        let mut materials = HashMap::new();
        for params in material_assets {
            let id = params.id.clone();
            let vertex_path = assets_path.join(&params.vertex_path);
            let fragment_path = assets_path.join(&params.fragment_path);
            let material = Material::new(vertex_path, fragment_path, params.into()).await?;
            materials.insert(id, material);
        }

        let textures_file_path = assets_path.join(Self::TEXTURES_FILE_NAME);
        let bytes = load_file(&textures_file_path).await?;
        let texture_assets: Vec<TextureAssetParams> = serde_json::from_slice(&bytes)?;

        #[cfg(debug_assertions)]
        println!("Resources: Loading textures");
        let mut textures = HashMap::new();
        let white_image = Image::gen_image_color(32, 32, color::WHITE);
        let white_texture = Texture2D::from_image(&white_image);
        white_texture.set_filter(FilterMode::Nearest);

        let texture = Texture::new(white_texture, None, None);
        textures.insert(Self::WHITE_TEXTURE_ID.to_string(), texture);

        for params in texture_assets {
            let path = assets_path.join(&params.path);
            let texture = load_texture(&path.to_string_lossy()).await?;
            texture.set_filter(params.filter_mode);

            let mut height_map = None;
            if let Some(path) = &params.height_map_path {
                let path = assets_path.join(path);
                let res = load_texture(&path.to_string_lossy()).await?;
                res.set_filter(params.filter_mode);
                height_map = Some(res);
            }

            let mut normal_map = None;
            if let Some(path) = &params.normal_map_path {
                let path = assets_path.join(path);
                let res = load_texture(&path.to_string_lossy()).await?;
                res.set_filter(params.filter_mode);
                normal_map = Some(res);
            }

            let texture = Texture::new(texture, height_map, normal_map);
            textures.insert(params.id.clone(), texture);
        }

        let images_file_path = assets_path.join(Self::IMAGES_FILE_NAME);
        let bytes = load_file(&images_file_path).await?;
        let image_assets: Vec<ImageAssetParams> = serde_json::from_slice(&bytes)?;

        #[cfg(debug_assertions)]
        println!("Resources: Loading images");
        let mut images = HashMap::new();
        for params in image_assets {
            let path = assets_path.join(&params.path);
            let bytes = load_file(&path).await?;
            let format = match params.format.as_ref() {
                Some(ext) => ImageFormat::from_extension(ext),
                _ => None,
            };

            let image = Image::from_file_with_format(&bytes, format);
            images.insert(params.id.clone(), image);
        }

        let fonts_file_path = assets_path.join(Self::FONTS_FILE_NAME);
        let bytes = load_file(&fonts_file_path).await?;
        let font_assets: Vec<FontAssetParams> = serde_json::from_slice(&bytes)?;

        #[cfg(debug_assertions)]
        println!("Resources: Loading fonts");
        let mut font_bytes = HashMap::new();
        for params in font_assets {
            let path = assets_path.join(&params.path);
            let bytes = load_file(&path).await?;
            font_bytes.insert(params.id.clone(), bytes);
        }

        let sound_effects_file_path = assets_path.join(Self::SOUND_EFFECTS_FILE_NAME);
        let bytes = load_file(&sound_effects_file_path).await?;
        let sound_effect_assets: Vec<SoundAssetParams> = serde_json::from_slice(&bytes)?;

        #[cfg(debug_assertions)]
        println!("Resources: Loading sound effects");
        let mut sound_effects = HashMap::new();
        for params in sound_effect_assets {
            let path = assets_path.join(&params.path);
            let sound = load_sound(VolumeCategory::SoundEffect, path).await?;
            sound_effects.insert(params.id.clone(), sound);
        }

        let music_file_path = assets_path.join(Self::MUSIC_FILE_NAME);
        let bytes = load_file(&music_file_path).await?;
        let music_assets: Vec<SoundAssetParams> = serde_json::from_slice(&bytes)?;

        #[cfg(debug_assertions)]
        println!("Resources: Loading music");
        let mut music = HashMap::new();
        for params in music_assets {
            let path = assets_path.join(&params.path);
            let track = load_sound(VolumeCategory::Music, path).await?;
            music.insert(params.id.clone(), track);
        }

        let resources = Resources {
            actors,
            character_classes,
            items,
            abilities,
            missions,
            dialogue,
            chapters,
            materials,
            textures,
            images,
            font_bytes,
            sound_effects,
            music,
        };

        Ok(resources)
    }

    pub fn get_font(&self, font_id: &str) -> Result<Font> {
        let bytes = self
            .font_bytes
            .get(font_id)
            .unwrap_or_else(|| panic!("No font with id '{}' was found!", font_id));
        let font = load_ttf_font_from_bytes(bytes)?;
        Ok(font)
    }
}