example item

This commit is contained in:
2025-09-12 18:04:03 +01:00
parent ee7ac39ad5
commit 8a5884c6d5
19 changed files with 1009 additions and 53 deletions
+54
View File
@@ -0,0 +1,54 @@
use bevy::prelude::*;
#[derive(Component, Debug)]
pub struct Container {
// Maximum weight this container can hold
pub max_weight: u32,
// Maximum number of items (None = unlimited)
pub max_items: Option<u32>,
// Current weight of contents
pub current_weight: u32,
// List of item entities contained
pub contents: Vec<Entity>, // TODO - move away from Entity
// Whether the container is currently open/accessible
pub is_open: bool,
}
impl Container {
pub fn new(max_weight: u32, max_items: Option<u32>) -> Self {
Self {
max_weight,
max_items,
current_weight: 0,
contents: Vec::new(),
is_open: true,
}
}
pub fn can_fit(&self, weight: u32) -> bool {
self.current_weight + weight <= self.max_weight
&& self
.max_items
.map_or(true, |max| self.contents.len() < max as usize)
}
pub fn add_item(&mut self, item_entity: Entity, weight: u32) -> bool {
if self.can_fit(weight) {
self.contents.push(item_entity);
self.current_weight += weight;
true
} else {
false
}
}
pub fn remove_item(&mut self, item_entity: Entity, weight: u32) -> bool {
if let Some(pos) = self.contents.iter().position(|&e| e == item_entity) {
self.contents.remove(pos);
self.current_weight = self.current_weight.saturating_sub(weight);
true
} else {
false
}
}
}
+219
View File
@@ -0,0 +1,219 @@
use bevy::prelude::*;
use crate::entities::item::decorations::ItemDecorations;
use crate::entities::item::material::Material;
use crate::entities::item::quality::Quality;
use crate::entities::item::types::ItemType;
#[derive(Component, Debug)]
#[require(Transform, Visibility)]
pub struct Item {
// How worn/damaged the item is (0-255, where 255 is completely destroyed)
pub wear: u8,
// The quality of craftsmanship for the base item
pub quality: Quality,
// Time until stains fade (in game ticks/turns),
pub stain_time: u16,
// Temperature of the item (for hot/cold items)
pub temperature: i16, // Can be negative for very cold items
}
impl Default for Item {
fn default() -> Self {
Self {
wear: 0,
quality: Quality::default(),
stain_time: 0,
temperature: 20, // Room temperature
}
}
}
impl Item {
pub fn new() -> Self {
Self::default()
}
// Calculate weight based on materials and decorations
pub fn calculate_weight(
&self,
item_type: &ItemType,
base_material: &Material,
decorations: Option<&ItemDecorations>,
) -> u32 {
// Base weight from primary material
let base_volume = item_type.base_volume();
let base_weight = base_material.density() * base_volume;
// Additional weight from decorations
let decoration_weight = decorations
.map(|d| d.total_decoration_weight())
.unwrap_or(0.0);
let total_weight = base_weight + decoration_weight;
// Convert to u32 (multiply by 1000 to get grams if density was in kg/unit)
(total_weight * 1000.0) as u32
}
// Calculate total value considering all factors
pub fn calculate_value(
&self,
item_type: &ItemType,
base_material: &Material,
decorations: Option<&ItemDecorations>,
) -> u32 {
// Base value from item type and material
let base_value = item_type.base_value() + base_material.base_value();
// Apply quality multiplier to base
let quality_modified_value = base_value as f32 * self.quality.value_multiplier();
// Additional value from decorations
let decoration_value = decorations.map(|d| d.total_decoration_value()).unwrap_or(0);
// Reduce value based on wear
let wear_multiplier = (255 - self.wear) as f32 / 255.0;
let total_value = (quality_modified_value + decoration_value as f32) * wear_multiplier;
total_value as u32
}
// Check if the item is destroyed due to wear
pub fn is_destroyed(&self) -> bool {
self.wear >= 255
}
// Apply wear to the item
pub fn add_wear(&mut self, wear_amount: u8) {
self.wear = self.wear.saturating_add(wear_amount);
}
// Check if item has any stains
pub fn has_stains(&self) -> bool {
self.stain_time > 0
}
// Reduce stain time (call this periodically to fade stains)
pub fn reduce_stain_time(&mut self, amount: u16) {
self.stain_time = self.stain_time.saturating_sub(amount);
}
// Add staining to the item
pub fn add_stain(&mut self, duration: u16) {
self.stain_time = self.stain_time.saturating_add(duration);
}
}
// Bundle for creating item entities
#[derive(Bundle)]
pub struct ItemBundle {
pub item: Item,
pub item_type: ItemType,
pub base_material: Material,
pub decorations: ItemDecorations,
pub transform: Transform,
pub visibility: Visibility,
pub sprite: Sprite,
}
// Component for items that have names (can be generated or custom)
#[derive(Component, Debug, Clone)]
pub struct ItemName {
// The base name of the item type
pub base_name: String,
// Custom name given by player or generated
pub custom_name: Option<String>,
}
impl ItemName {
pub fn new(base_name: String) -> Self {
Self {
base_name,
custom_name: None,
}
}
// Generate full display name including materials and decorations
pub fn display_name(
&self,
item: &Item,
base_material: &Material,
decorations: Option<&ItemDecorations>,
) -> String {
let mut parts = Vec::new();
// Quality prefix
let quality_name = item.quality.display_name();
if !quality_name.is_empty() {
parts.push(quality_name.to_string());
}
// Base material
parts.push(base_material.name());
// Item name
parts.push(self.base_name.clone());
let mut result = parts.join(" ");
// Add decorations to the description
if let Some(decorations) = decorations {
let mut decoration_parts = Vec::new();
// Gem encrustings
for gem in &decorations.gem_encrustings {
let quality_prefix = if gem.quality != Quality::Ordinary {
format!("{} ", gem.quality.display_name())
} else {
String::new()
};
decoration_parts.push(format!(
"encrusted with {}{}",
quality_prefix,
gem.material.name()
));
}
// Trim materials
for trim in &decorations.trim_materials {
let quality_prefix = if trim.quality != Quality::Ordinary {
format!("{} ", trim.quality.display_name())
} else {
String::new()
};
let trim_name = if quality_prefix.is_empty() {
trim.material.name()
} else {
format!("{} {}", quality_prefix, trim.material.name())
};
decoration_parts.push(format!("with {} trim", trim_name));
}
// Engravings
for engraving in &decorations.engravings {
let quality_prefix = if engraving.quality != Quality::Ordinary {
format!("{} ", engraving.quality.display_name())
} else {
String::new()
};
decoration_parts.push(format!(
"engraved with {}images of {}",
quality_prefix, engraving.subject
));
}
if !decoration_parts.is_empty() {
result = format!("{}, {}", result, decoration_parts.join(", "));
}
}
// Custom name
if let Some(custom) = &self.custom_name {
result = format!("\"{}\" {}", custom, result);
}
result
}
}
+122
View File
@@ -0,0 +1,122 @@
use bevy::prelude::*;
use crate::entities::item::material::Material;
use crate::entities::item::quality::Quality;
#[derive(Component, Debug, Clone)]
pub struct ItemDecorations {
// Gems encrusted on the item
pub gem_encrustings: Vec<GemEncrusting>,
// Metal trim/bands
pub trim_materials: Vec<TrimMaterial>,
// Engravings on the item
pub engravings: Vec<Engraving>,
}
impl Default for ItemDecorations {
fn default() -> Self {
Self {
gem_encrustings: Vec::new(),
trim_materials: Vec::new(),
engravings: Vec::new(),
}
}
}
impl ItemDecorations {
pub fn new() -> Self {
Self::default()
}
// Calculate total additional weight from decorations
pub fn total_decoration_weight(&self) -> f32 {
let gem_weight: f32 = self
.gem_encrustings
.iter()
.map(|gem| gem.material.density() * gem.size)
.sum();
let trim_weight: f32 = self
.trim_materials
.iter()
.map(|trim| trim.material.density() * trim.amount)
.sum();
// Engravings don't add weight, they remove material
gem_weight + trim_weight
}
// Calculate total additional value from decorations
pub fn total_decoration_value(&self) -> u32 {
let gem_value: f32 = self
.gem_encrustings
.iter()
.map(|gem| gem.material.base_value() as f32 * gem.size * gem.quality.value_multiplier())
.sum();
let trim_value: f32 = self
.trim_materials
.iter()
.map(|trim| {
trim.material.base_value() as f32 * trim.amount * trim.quality.value_multiplier()
})
.sum();
let engraving_value: f32 = self
.engravings
.iter()
.map(|engraving| engraving.base_value() * engraving.quality.value_multiplier())
.sum();
(gem_value + trim_value + engraving_value) as u32
}
pub fn add_gem_encrusting(&mut self, material: Material, size: f32, quality: Quality) {
if let Material::Gem(_) = material {
self.gem_encrustings.push(GemEncrusting {
material,
size,
quality,
});
}
}
pub fn add_trim(&mut self, material: Material, amount: f32, quality: Quality) {
self.trim_materials.push(TrimMaterial {
material,
amount,
quality,
});
}
pub fn add_engraving(&mut self, subject: String, quality: Quality) {
self.engravings.push(Engraving { subject, quality });
}
}
#[derive(Debug, Clone)]
pub struct GemEncrusting {
pub material: Material, // Should be a gem, but using material to gain access to its wider functions
pub size: f32, // Size of the gem (affects weight and value)
pub quality: Quality, // Quality of the gem cut
}
#[derive(Debug, Clone)]
pub struct TrimMaterial {
pub material: Material, // Usually metal for bands/trim
pub amount: f32, // Amount of material used
pub quality: Quality, // Quality of the trim work
}
#[derive(Debug, Clone)]
pub struct Engraving {
pub subject: String, // What the engraving depicts
pub quality: Quality, // Quality of the engraving
}
impl Engraving {
pub fn base_value(&self) -> f32 {
// Base value for engravings (they're pure craftsmanship)
20.0
}
}
+156
View File
@@ -0,0 +1,156 @@
use bevy::prelude::*;
#[derive(Component, Debug, Clone, PartialEq, Eq, Hash)]
pub enum Material {
NA,
Wood(WoodType),
Stone(StoneType),
Metal(MetalType),
Cloth(ClothType),
Leather(LeatherType),
Bone,
Shell,
Glass,
Ceramic,
Gem(GemType),
Wax,
}
impl Material {
// Weight per unit volume (arbitrary units - adjust as needed)
pub fn density(&self) -> f32 {
match self {
Material::NA => 1.0,
Material::Wood(wood) => match wood {
WoodType::Oak => 0.7,
WoodType::Pine => 0.5,
WoodType::Birch => 0.6,
WoodType::Ironwood => 1.2,
},
Material::Stone(stone) => match stone {
StoneType::Granite => 2.7,
StoneType::Marble => 2.6,
StoneType::Sandstone => 2.2,
StoneType::Obsidian => 2.4,
},
Material::Metal(metal) => match metal {
MetalType::Iron => 7.9,
MetalType::Steel => 7.8,
MetalType::Copper => 8.9,
MetalType::Silver => 10.5,
MetalType::Gold => 19.3,
MetalType::Platinum => 21.5,
},
Material::Cloth(_) => 0.3,
Material::Leather(_) => 0.8,
Material::Bone => 1.8,
Material::Shell => 2.0,
Material::Glass => 2.5,
Material::Ceramic => 2.3,
Material::Gem(gem) => match gem {
GemType::Diamond => 3.5,
GemType::Ruby => 4.0,
GemType::Emerald => 2.7,
GemType::Sapphire => 4.0,
},
Material::Wax => 0.1,
}
}
// Base value per unit (before quality modifiers)
pub fn base_value(&self) -> u32 {
match self {
Material::NA => 1,
Material::Wood(_) => 1,
Material::Stone(_) => 2,
Material::Metal(metal) => match metal {
MetalType::Iron => 5,
MetalType::Steel => 8,
MetalType::Copper => 3,
MetalType::Silver => 15,
MetalType::Gold => 30,
MetalType::Platinum => 40,
},
Material::Cloth(_) => 2,
Material::Leather(_) => 3,
Material::Bone => 1,
Material::Shell => 2,
Material::Glass => 4,
Material::Ceramic => 3,
Material::Gem(gem) => match gem {
GemType::Diamond => 200,
GemType::Ruby => 150,
GemType::Emerald => 100,
GemType::Sapphire => 120,
},
Material::Wax => 3,
}
}
pub fn name(&self) -> String {
match self {
Material::NA => "N/A".to_string(),
Material::Wood(wood) => format!("{:?}", wood).to_lowercase(),
Material::Stone(stone) => format!("{:?}", stone).to_lowercase(),
Material::Metal(metal) => format!("{:?}", metal).to_lowercase(),
Material::Cloth(cloth) => format!("{:?}", cloth).to_lowercase(),
Material::Leather(leather) => {
format!("{} leather", format!("{:?}", leather).to_lowercase())
}
Material::Bone => "bone".to_string(),
Material::Shell => "shell".to_string(),
Material::Glass => "glass".to_string(),
Material::Ceramic => "ceramic".to_string(),
Material::Gem(gem) => format!("{:?}", gem).to_lowercase(),
Material::Wax => "wax".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum WoodType {
Oak,
Pine,
Birch,
Ironwood,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum StoneType {
Granite,
Marble,
Sandstone,
Obsidian,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum MetalType {
Iron,
Steel,
Copper,
Silver,
Gold,
Platinum,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClothType {
Cotton,
Silk,
Wool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum LeatherType {
Cow,
Pig,
Dragon,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum GemType {
Diamond,
Ruby,
Emerald,
Sapphire,
}
+19
View File
@@ -0,0 +1,19 @@
pub mod container;
pub mod core;
pub mod decorations;
pub mod material;
pub mod perishable;
pub mod prefabs;
pub mod quality;
pub mod systems;
pub mod types;
pub use container::*;
pub use core::*;
pub use decorations::*;
pub use material::*;
pub use perishable::*;
pub use prefabs::*;
pub use quality::*;
pub use systems::*;
pub use types::*;
+51
View File
@@ -0,0 +1,51 @@
use bevy::prelude::*;
use crate::entities::item::material::Material;
use crate::entities::item::types::ItemType;
use crate::entities::item::Item;
#[derive(Component, Debug)]
pub struct Perishable {
// How much the item has decayed (0-255)
pub decay: u8,
// How fast this item decays per tick
pub decay_rate: u8,
// What happens when fully decayed
pub decay_result: DecayResult,
}
#[derive(Debug, Clone)]
pub enum DecayResult {
Disappear,
Transform(ItemType, Material), // Transform into another item type with different material
}
pub fn decay_system(
time: Res<Time>,
mut perishable_items: Query<(Entity, &mut Perishable, &Item, &ItemType, &Material)>,
mut commands: Commands,
) {
for (entity, mut perishable, item, item_type, base_material) in perishable_items.iter_mut() {
// Simple decay - you might want to make this more sophisticated
if time.elapsed_secs_f64() as u32 % 60 == 0 {
// Decay every 60 seconds
perishable.decay = perishable.decay.saturating_add(perishable.decay_rate);
if perishable.decay >= 255 {
match &perishable.decay_result {
DecayResult::Disappear => {
commands.entity(entity).despawn();
}
DecayResult::Transform(new_item_type, new_material) => {
// Transform the item
commands
.entity(entity)
.insert(new_item_type.clone())
.insert(new_material.clone())
.remove::<Perishable>();
}
}
}
}
}
}
@@ -0,0 +1,63 @@
use bevy::prelude::*;
use crate::entities::item::{Item, ItemBundle, ItemDecorations, ItemType, Material, Quality};
use crate::world::VisibleGameEntity;
// Enum for misc item prefabs
#[derive(Debug, Clone, Copy)]
pub enum MiscPrefab {
RawMeat,
Coin,
}
// Spawns a misc prefab at a given position.
// These are all likely temporary to speed up development but will be replaced by semi-custom items everywhere.
pub fn spawn_prefab(
commands: &mut Commands,
asset_server: &Res<AssetServer>,
prefab: MiscPrefab,
position: Vec3,
) -> Entity {
match prefab {
MiscPrefab::RawMeat => {
let meat = commands
.spawn(ItemBundle {
item: Item {
quality: Quality::Ordinary,
..Item::default()
},
item_type: ItemType::Food(crate::entities::item::FoodType::Meat),
base_material: Material::NA,
decorations: ItemDecorations::new(),
transform: Transform::from_translation(position - Vec3::new(0.0, 0.0, 0.1)),
visibility: Visibility::Visible,
sprite: Sprite {
image: asset_server.load("raw_meat.png"),
..Default::default()
},
})
.id();
commands.entity(meat).insert(VisibleGameEntity).id()
}
MiscPrefab::Coin => {
let coin = commands
.spawn(ItemBundle {
item: Item {
quality: Quality::Ordinary,
..Item::default()
},
item_type: ItemType::Coin,
base_material: Material::Metal(crate::entities::item::MetalType::Copper),
decorations: ItemDecorations::new(),
transform: Transform::from_translation(position - Vec3::new(0.0, 0.0, 0.1)),
visibility: Visibility::Visible,
sprite: Sprite {
image: asset_server.load("coin.png"),
..Default::default()
},
})
.id();
commands.entity(coin).insert(VisibleGameEntity).id()
}
}
}
+3
View File
@@ -0,0 +1,3 @@
pub mod misc_prefabs;
pub use misc_prefabs::*;
+3
View File
@@ -0,0 +1,3 @@
pub mod misc; // miscellaneous items (raw meat, coins, etc.)
pub use misc::*; // re-export misc prefabs for easier access
+46
View File
@@ -0,0 +1,46 @@
use bevy::prelude::*;
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Quality {
// Standard qualities
Ordinary = 0,
WellCrafted = 1,
FinelyCrafted = 2,
Superior = 3,
Exceptional = 4,
Masterwork = 5,
// Special artifact quality
Artifact = 10,
}
impl Quality {
pub fn value_multiplier(&self) -> f32 {
match self {
Quality::Ordinary => 1.0,
Quality::WellCrafted => 1.2,
Quality::FinelyCrafted => 1.5,
Quality::Superior => 2.0,
Quality::Exceptional => 3.0,
Quality::Masterwork => 5.0,
Quality::Artifact => 10.0,
}
}
pub fn display_name(&self) -> &'static str {
match self {
Quality::Ordinary => "",
Quality::WellCrafted => "well-crafted",
Quality::FinelyCrafted => "finely-crafted",
Quality::Superior => "superior",
Quality::Exceptional => "exceptional",
Quality::Masterwork => "masterwork",
Quality::Artifact => "artifact",
}
}
}
impl Default for Quality {
fn default() -> Self {
Quality::Ordinary
}
}
+70
View File
@@ -0,0 +1,70 @@
use bevy::prelude::*;
use crate::entities::item::decorations::ItemDecorations;
use crate::entities::item::material::{GemType, Material, MetalType};
use crate::entities::item::quality::Quality;
use crate::entities::item::types::ItemType;
use crate::entities::item::Item;
use crate::entities::item::ItemBundle;
use crate::entities::item::ItemName;
// System to reduce stain time over time
pub fn stain_fade_system(time: Res<Time>, mut items: Query<&mut Item>) {
// Fade stains every 10 seconds
if time.elapsed_secs_f64() as u32 % 10 == 0 {
for mut item in items.iter_mut() {
item.reduce_stain_time(1);
}
}
}
// Helper function to create a decorated item
pub fn create_decorated_item(
commands: &mut Commands,
item_type: ItemType,
base_material: Material,
quality: Quality,
position: Vec3,
sprite: Sprite,
) -> Entity {
let mut decorations = ItemDecorations::new();
// Example: Add a ruby encrusting to valuable items
if quality >= Quality::Superior {
decorations.add_gem_encrusting(
Material::Gem(GemType::Ruby),
0.5, // Small gem
Quality::WellCrafted,
);
}
// Example: Add silver trim to masterwork items
if quality >= Quality::Masterwork {
decorations.add_trim(
Material::Metal(MetalType::Silver),
0.2, // Small amount of trim
quality,
);
}
// Example: Add engraving to exceptional+ items
if quality >= Quality::Exceptional {
decorations.add_engraving("a majestic dorf holding a pickaxe".to_string(), quality);
}
commands
.spawn(ItemBundle {
item: Item {
quality,
..Item::default()
},
item_type: item_type.clone(),
base_material,
decorations,
transform: Transform::from_translation(position),
visibility: Visibility::Visible,
sprite: sprite,
})
.insert(ItemName::new(item_type.name()))
.id()
}
+160
View File
@@ -0,0 +1,160 @@
use bevy::prelude::*;
#[derive(Component, Debug, Clone, PartialEq, Eq, Hash)]
pub enum ItemType {
// Weapons
Weapon(WeaponType),
// Armor/Clothing
Armor(ArmorType),
// Tools
Tool(ToolType),
// Consumables
Food(FoodType),
Drink(DrinkType),
Medicine,
// Crafting materials
RawMaterial,
// Trade goods
Gem,
Coin,
// Misc
Container,
Book,
Toy,
Decoration,
}
impl ItemType {
// Base volume for this item type (used for weight calculation)
pub fn base_volume(&self) -> f32 {
match self {
ItemType::Weapon(weapon) => match weapon {
WeaponType::Sword => 2.0,
WeaponType::Axe => 1.5,
WeaponType::Hammer => 3.0,
WeaponType::Spear => 1.8,
WeaponType::Bow => 0.8,
WeaponType::Crossbow => 2.5,
WeaponType::Dagger => 0.3,
},
ItemType::Armor(armor) => match armor {
ArmorType::Helmet => 1.0,
ArmorType::Chestplate => 4.0,
ArmorType::Leggings => 2.5,
ArmorType::Boots => 0.8,
ArmorType::Gloves => 0.4,
ArmorType::Shield => 2.0,
ArmorType::Cloak => 0.5,
ArmorType::Shirt => 0.3,
ArmorType::Pants => 0.4,
},
ItemType::Tool(tool) => match tool {
ToolType::Pickaxe => 2.0,
ToolType::Shovel => 1.5,
ToolType::Axe => 1.75,
ToolType::Hammer => 1.0,
ToolType::Anvil => 50.0, // Heavy!
ToolType::Furnace => 100.0, // Very heavy!
},
ItemType::Food(_) => 0.2,
ItemType::Drink(_) => 0.3,
ItemType::Medicine => 0.1,
ItemType::RawMaterial => 0.5,
ItemType::Gem => 0.1,
ItemType::Coin => 0.01,
ItemType::Container => 1.0,
ItemType::Book => 0.5,
ItemType::Toy => 0.2,
ItemType::Decoration => 0.3,
}
}
// Base value for this item type
pub fn base_value(&self) -> u32 {
match self {
ItemType::Weapon(_) => 50,
ItemType::Armor(_) => 30,
ItemType::Tool(_) => 25,
ItemType::Food(_) => 2,
ItemType::Drink(_) => 3,
ItemType::Medicine => 10,
ItemType::RawMaterial => 1,
ItemType::Gem => 100,
ItemType::Coin => 1,
ItemType::Container => 15,
ItemType::Book => 20,
ItemType::Toy => 5,
ItemType::Decoration => 10,
}
}
pub fn name(&self) -> String {
match self {
ItemType::Weapon(weapon) => format!("{:?}", weapon).to_lowercase(),
ItemType::Armor(armor) => format!("{:?}", armor).to_lowercase(),
ItemType::Tool(tool) => format!("{:?}", tool).to_lowercase(),
ItemType::Food(food) => format!("{:?}", food).to_lowercase(),
ItemType::Drink(drink) => format!("{:?}", drink).to_lowercase(),
ItemType::Medicine => "medicine".to_string(),
ItemType::RawMaterial => "raw material".to_string(),
ItemType::Gem => "gem".to_string(),
ItemType::Coin => "coin".to_string(),
ItemType::Container => "container".to_string(),
ItemType::Book => "book".to_string(),
ItemType::Toy => "toy".to_string(),
ItemType::Decoration => "decoration".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum WeaponType {
Sword,
Axe,
Hammer,
Spear,
Bow,
Crossbow,
Dagger,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ArmorType {
Helmet,
Chestplate,
Leggings,
Boots,
Gloves,
Shield,
Cloak,
Shirt,
Pants,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ToolType {
Pickaxe,
Shovel,
Axe,
Hammer,
Anvil,
Furnace,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum FoodType {
Meat,
Vegetable,
Fruit,
Grain,
Prepared, // cooked meals
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum DrinkType {
Water,
Juice,
Ale,
Wine,
Spirits,
}
+37 -3
View File
@@ -1,17 +1,24 @@
use crate::constants::TILE_SIZE;
use crate::constants::*;
use crate::entities::item::{
create_decorated_item, spawn_prefab, FoodType, Item, ItemBundle, ItemDecorations, ItemType,
Material, MiscPrefab, Quality, WoodType,
};
use crate::entities::shared_components::Ambulatory;
use crate::world::VisibleGameEntity;
use bevy::ecs::spawn;
use bevy::prelude::*;
use bevy_rand::prelude::*;
use rand::Rng;
// Pig bundle
#[derive(Bundle)]
pub struct Pig {
ambulatory: Ambulatory,
sprite: Sprite,
transform: Transform,
visibility: Visibility,
drop_timer: PigDropTimer, // NEW
}
impl Pig {
@@ -31,19 +38,24 @@ impl Pig {
},
transform: Transform::from_translation(position).with_scale(Vec3::splat(PIXEL_RATIO)),
visibility: Visibility::Hidden,
drop_timer: PigDropTimer(Timer::from_seconds(5.0, TimerMode::Repeating)), // NEW
}
}
}
// Timer component for pigs dropping items
#[derive(Component, Deref, DerefMut)]
pub struct PigDropTimer(pub Timer);
// Spawn a handful of pigs
pub fn spawn_pigs(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut rng_q: Query<&mut Entropy<WyRand>, With<Global>>,
) {
if let Ok(mut rng) = rng_q.single_mut() {
// Spawn a handful of pigs
for _ in 0..5 {
let cit = commands
let pig = commands
.spawn(Pig::new(
&asset_server,
Vec3::new(
@@ -53,7 +65,29 @@ pub fn spawn_pigs(
) * TILE_SIZE,
))
.id();
commands.entity(cit).insert(VisibleGameEntity);
commands.entity(pig).insert(VisibleGameEntity);
}
}
}
// System: make pigs drop items every 5 seconds
pub fn pig_drop_system(
mut commands: Commands,
asset_server: Res<AssetServer>,
time: Res<Time>,
mut pigs: Query<(&mut PigDropTimer, &Transform)>,
) {
for (mut timer, transform) in &mut pigs {
timer.tick(time.delta());
if timer.just_finished() {
// Example: pig drops raw meat
spawn_prefab(
&mut commands,
&asset_server,
MiscPrefab::RawMeat,
transform.translation,
);
}
}
}
+2 -2
View File
@@ -43,7 +43,7 @@ pub fn spawn_rabbits(
if let Ok(mut rng) = rng_q.single_mut() {
// Spawn a handful of rabbits
for _ in 0..15 {
let cit = commands
let rab = commands
.spawn(Rabbit::new(
&asset_server,
Vec3::new(
@@ -53,7 +53,7 @@ pub fn spawn_rabbits(
) * TILE_SIZE,
))
.id();
commands.entity(cit).insert(VisibleGameEntity);
commands.entity(rab).insert(VisibleGameEntity);
}
}
}
+1
View File
@@ -1,3 +1,4 @@
pub mod item;
pub mod livestock;
pub mod sentient;
pub mod shared_components;
-47
View File
@@ -1,47 +0,0 @@
use bevy::prelude::*;
#[derive(Component)]
#[require(Sprite, Transform, Visibility)]
pub struct Item {
wear: u8,
quality: u8,
weight: u8,
stain_time: u16,
}
impl Default for Item {
fn default() -> Self {
Self {
wear: 0,
quality: 0,
weight: 0,
stain_time: 16,
}
}
}
impl Item {
pub fn new() -> Self {
Self {
..Default::default()
}
}
}
#[derive(Bundle)]
pub struct ItemBundle {
pub item: Item,
pub transform: Transform,
pub sprite: Sprite,
pub visibility: Visibility,
}
#[derive(Component)]
pub struct Nameable {
pub name: String,
}
#[derive(Component)]
pub struct Decaying {
decay: u8,
}
+3 -1
View File
@@ -1,12 +1,13 @@
use bevy::prelude::*;
use bevy_rand::prelude::*;
use crate::entities::livestock::pig::pig_drop_system;
mod camera;
mod constants;
mod cursor;
mod entities;
mod game;
mod item;
mod world;
fn main() {
@@ -54,5 +55,6 @@ fn main() {
cursor::move_cursor,
),
)
.add_systems(Update, pig_drop_system)
.run();
}