diff --git a/examples/items_by_name.rs b/examples/items_by_name.rs index 5502e14..b2eb351 100644 --- a/examples/items_by_name.rs +++ b/examples/items_by_name.rs @@ -7,7 +7,109 @@ //! although even then it can be useful to be able to spawn objects by name for scripting purposes: //! creating effects, dynamically spawning enemies, etc. //! -//! This example shows how to use manifests to create a simple system for spawning objects by name, +//! This example shows how to use manifests to create a simple system for working with manifest entries by name, //! although the same principles can be used to manipulate manifests if using the [`MutableManifest`] trait as well. +//! +//! This code is largely copied from the `simple.rs` example: we're just adding constants and a new system to demonstrate the name-based lookups. + +use bevy::{log::LogPlugin, prelude::*, utils::HashMap}; +use leafwing_manifest::{ + asset_state::SimpleAssetState, + identifier::Id, + manifest::{Manifest, ManifestFormat}, + plugin::{ManifestPlugin, RegisterManifest}, +}; +use serde::{Deserialize, Serialize}; + +// While you *can* simply use the name directly via the various name-based methods on the Manifest trait, +// it's generally a good idea to store constants for the names you're going to use when possible. +// This has three main benefits: +// 1. It makes it easier to refactor your code later, as you can change the name in one place and have it propagate everywhere. +// 2. It makes it easier to catch typos, as the compiler will catch any references to a name that doesn't exist. +// 3. It saves on recomputing the hash of the name every time you need it. +const SWORD: Id = Id::from_name("sword"); +const SHIELD: Id = Id::from_name("shield"); + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[allow(dead_code)] // Properties are for demonstration purposes only. +struct Item { + name: String, + description: String, + value: i32, + weight: f32, + max_stack: u8, +} + +#[derive(Debug, Resource, Asset, TypePath, Serialize, Deserialize, PartialEq)] +struct ItemManifest { + items: HashMap, Item>, +} + +impl Manifest for ItemManifest { + type Item = Item; + type RawItem = Item; + type RawManifest = ItemManifest; + type ConversionError = std::convert::Infallible; + + const FORMAT: ManifestFormat = ManifestFormat::Ron; + + fn get(&self, id: Id) -> Option<&Self::Item> { + self.items.get(&id) + } + + fn from_raw_manifest( + raw_manifest: Self::RawManifest, + _world: &mut World, + ) -> Result { + Ok(raw_manifest) + } +} + +fn main() { + App::new() + .add_plugins((MinimalPlugins, AssetPlugin::default(), LogPlugin::default())) + .init_state::() + .add_plugins(ManifestPlugin::::default()) + .register_manifest::("items.ron") + .add_systems(OnEnter(SimpleAssetState::Ready), look_up_items_by_name) + .run(); +} + +/// This system reads the generated item manifest resource and prints out all the items. +fn look_up_items_by_name(item_manifest: Res) { + // Look up the items by name. + let sword = item_manifest.get(SWORD); + let shield = item_manifest.get(SHIELD); + + // Print out the items. + if let Some(sword) = sword { + println!("Found sword: {:?}", sword); + } else { + println!("Sword not found!"); + } + + if let Some(shield) = shield { + println!("Found shield: {:?}", shield); + } else { + println!("Shield not found!"); + } + + // We could also use the `get_by_name` method, which is a bit more concise, + // but doesn't provide the same level of type safety as using the `Id` directly. + // However, using these methods is the right choice when working with truly dynamic inputs: + // for example, when reading from a file or user input. + let sword = item_manifest.get_by_name("sword"); + let shield = item_manifest.get_by_name("shield"); + + if let Some(sword) = sword { + println!("Found sword by name: {:?}", sword); + } else { + println!("Sword not found by name!"); + } -fn main() {} + if let Some(shield) = shield { + println!("Found shield by name: {:?}", shield); + } else { + println!("Shield not found by name!"); + } +} diff --git a/src/manifest.rs b/src/manifest.rs index cb1cdc7..660224f 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -1,4 +1,4 @@ -use std::error::Error; +use std::{borrow::Borrow, error::Error}; use bevy::{ asset::Asset, @@ -96,8 +96,8 @@ pub trait Manifest: Sized + Resource { /// /// Returns [`None`] if no item with the given name is found. #[must_use] - fn get_by_name(&self, name: impl AsRef) -> Option<&Self::Item> { - self.get(Id::from_name(name.as_ref())) + fn get_by_name(&self, name: impl Borrow) -> Option<&Self::Item> { + self.get(Id::from_name(name.borrow())) } } @@ -168,14 +168,14 @@ pub trait MutableManifest: Manifest { /// The item is given a unique identifier, which is returned. fn insert_by_name( &mut self, - name: impl AsRef, + name: impl Borrow, item: Self::Item, ) -> Result, ManifestModificationError> { - let id = Id::from_name(name.as_ref()); + let id = Id::from_name(name.borrow()); if self.get(id).is_some() { Err(ManifestModificationError::DuplicateName( - name.as_ref().to_string(), + name.borrow().to_string(), )) } else { self.insert(item) @@ -195,9 +195,9 @@ pub trait MutableManifest: Manifest { /// The item removed is returned, if it was found. fn remove_by_name( &mut self, - name: impl AsRef, + name: impl Borrow, ) -> Result, ManifestModificationError> { - self.remove(&Id::from_name(name.as_ref())) + self.remove(&Id::from_name(name.borrow())) } /// Gets a mutable reference to an item from the manifest by its unique identifier. @@ -210,8 +210,8 @@ pub trait MutableManifest: Manifest { /// /// Returns [`None`] if no item with the given name is found. #[must_use] - fn get_mut_by_name(&mut self, name: impl AsRef) -> Option<&mut Self::Item> { - self.get_mut(Id::from_name(name.as_ref())) + fn get_mut_by_name(&mut self, name: impl Borrow) -> Option<&mut Self::Item> { + self.get_mut(Id::from_name(name.borrow())) } }