Skip to main content

rpfm_lib/files/bmd_vegetation/grass_list/
mod.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! Grass placement list for BMD vegetation files.
12//!
13//! This module defines the grass placement data structure used in BMD vegetation files.
14//! Grass data is stored as a compressed binary blob that defines grass coverage areas
15//! and density across the battle map.
16//!
17//! # Structure
18//!
19//! - [`GrassList`]: Container for grass placement data
20//!
21//! # Supported Versions
22//!
23//! - **Version 4**: Current format used in Total War: Warhammer III
24//!
25//! # Implementation Notes
26//!
27//! The grass placement data is stored as a raw byte array. The internal format
28//! is not fully documented but represents compressed grass coverage information.
29//!
30//! # Examples
31//!
32//! ```rust,ignore
33//! use rpfm_lib::files::bmd_vegetation::grass_list::*;
34//!
35//! // Decode a grass list from binary data
36//! let grass_list = GrassList::decode(&mut data, &extra_data)?;
37//!
38//! // Access the raw grass data
39//! println!("Grass data size: {} bytes", grass_list.grass_list().len());
40//! ```
41
42use getset::*;
43use serde_derive::{Serialize, Deserialize};
44
45use crate::binary::{ReadBytes, WriteBytes};
46use crate::error::{Result, RLibError};
47use crate::files::{Decodeable, EncodeableExtraData, Encodeable};
48
49use super::DecodeableExtraData;
50
51mod v1;
52mod v4;
53
54//---------------------------------------------------------------------------//
55//                              Enum & Structs
56//---------------------------------------------------------------------------//
57
58/// Grass placement data for a battle map: groups of grass positions, by grass type.
59///
60/// Only version 1, used by Attila, is decoded. Version 4 is only read when empty.
61///
62/// # Fields
63///
64/// * `serialise_version` - File format version (1 in Attila, 4 in later games)
65/// * `grass_list` - Grass positions, by grass type
66#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
67#[getset(get = "pub", get_mut = "pub", set = "pub")]
68pub struct GrassList {
69    /// File format version number.
70    serialise_version: u16,
71
72    /// Grass positions, by grass type.
73    grass_list: Vec<GrassItemVector>,
74}
75
76/// The positions of one type of grass.
77#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
78#[getset(get = "pub", get_mut = "pub", set = "pub")]
79pub struct GrassItemVector {
80    /// Grass type, like `grass/0`.
81    key: String,
82
83    /// Positions of the grass of this type.
84    value: Vec<GrassItem>,
85}
86
87/// The position of a grass patch. Stored as f16.
88#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
89#[getset(get = "pub", get_mut = "pub", set = "pub")]
90pub struct GrassItem {
91    /// X coordinate.
92    x: f32,
93
94    /// Y coordinate (height).
95    y: f32,
96
97    /// Z coordinate.
98    z: f32,
99}
100
101//---------------------------------------------------------------------------//
102//                           Implementation of GrassList
103//---------------------------------------------------------------------------//
104
105impl Decodeable for GrassList {
106
107    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
108        let mut decoded = Self::default();
109        decoded.serialise_version = data.read_u16()?;
110
111        match decoded.serialise_version {
112            1 => decoded.read_v1(data, extra_data)?,
113            4 => decoded.read_v4(data, extra_data)?,
114            _ => return Err(RLibError::DecodingFastBinUnsupportedVersion(String::from("GrassList"), decoded.serialise_version)),
115        }
116
117        Ok(decoded)
118    }
119}
120
121impl Encodeable for GrassList {
122
123    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
124        buffer.write_u16(self.serialise_version)?;
125
126        match self.serialise_version {
127            1 => self.write_v1(buffer, extra_data)?,
128            4 => self.write_v4(buffer, extra_data)?,
129            _ => return Err(RLibError::EncodingFastBinUnsupportedVersion(String::from("GrassList"), self.serialise_version)),
130        }
131
132        Ok(())
133    }
134}