Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete items_by_name example #37

Merged
merged 4 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 104 additions & 2 deletions examples/items_by_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.

fn main() {}
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<Item> = Id::from_name("sword");
const SHIELD: Id<Item> = Id::from_name("shield");

/// The data for as single item that might be held in the player's inventory.
#[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,
}

/// A data-driven manifest, which contains the canonical data for all the items in the game.
#[derive(Debug, Resource, Asset, TypePath, Serialize, Deserialize, PartialEq)]
struct ItemManifest {
items: HashMap<Id<Item>, 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<Item>) -> Option<&Self::Item> {
self.items.get(&id)
}

fn from_raw_manifest(
raw_manifest: Self::RawManifest,
_world: &mut World,
) -> Result<Self, Self::ConversionError> {
Ok(raw_manifest)
}
}

fn main() {
App::new()
.add_plugins((MinimalPlugins, AssetPlugin::default(), LogPlugin::default()))
.init_state::<SimpleAssetState>()
.add_plugins(ManifestPlugin::<SimpleAssetState>::default())
.register_manifest::<ItemManifest>("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<ItemManifest>) {
// 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!");
}

if let Some(shield) = shield {
println!("Found shield by name: {:?}", shield);
} else {
println!("Shield not found by name!");
}
}
20 changes: 10 additions & 10 deletions src/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::error::Error;
use std::{borrow::Borrow, error::Error};

use bevy::{
asset::Asset,
Expand Down Expand Up @@ -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<String>) -> Option<&Self::Item> {
self.get(Id::from_name(name.as_ref()))
fn get_by_name(&self, name: impl Borrow<str>) -> Option<&Self::Item> {
self.get(Id::from_name(name.borrow()))
}
}

Expand Down Expand Up @@ -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<str>,
name: impl Borrow<str>,
item: Self::Item,
) -> Result<Id<Self::Item>, ManifestModificationError<Self>> {
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)
Expand All @@ -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<str>,
name: impl Borrow<str>,
) -> Result<Id<Self::Item>, ManifestModificationError<Self>> {
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.
Expand All @@ -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<str>) -> Option<&mut Self::Item> {
self.get_mut(Id::from_name(name.as_ref()))
fn get_mut_by_name(&mut self, name: impl Borrow<str>) -> Option<&mut Self::Item> {
self.get_mut(Id::from_name(name.borrow()))
}
}

Expand Down