Files
dorf/src/entities/cargo/systems.rs
T
2026-03-23 19:31:30 +00:00

77 lines
2.9 KiB
Rust

//! Cargo systems: carry visual cycling, position sync, and haul encumbrance.
use bevy::prelude::*;
use crate::entities::cargo::components::{Cargo, CarryVisualState, HaulSlot};
use crate::entities::item::constants::ITEM_Z_FIGHTING_OFFSET;
use crate::entities::item::inventory::constants::ENCUMBERED_SPEED_MULTIPLIER;
use crate::entities::item::inventory::InventoryChangedEvent;
use crate::entities::shared_components::Ambulatory;
/// Returns true if any entity has an occupied HaulSlot.
/// Used to gate carry_visual_system — skips entire system when no one is hauling.
pub fn any_hauling(query: Query<&HaulSlot>) -> bool {
query.iter().any(|slot| slot.is_occupied())
}
/// Cycle the carry visual sprite while the entity's HaulSlot is occupied.
/// When empty, ensure the normal sprite is restored.
///
/// Runs in Update (visual only, no game state change).
/// Only runs when at least one entity has an occupied HaulSlot.
pub fn carry_visual_system(
time: Res<Time>,
mut query: Query<(&HaulSlot, &mut CarryVisualState, &mut Sprite)>,
) {
for (haul_slot, mut visual, mut sprite) in query.iter_mut() {
if haul_slot.is_empty() {
if visual.showing_cargo {
sprite.image = visual.normal_sprite.clone();
visual.showing_cargo = false;
}
continue;
}
visual.timer.tick(time.delta());
if visual.timer.just_finished() {
visual.showing_cargo = !visual.showing_cargo;
sprite.image = if visual.showing_cargo {
visual.carry_sprite.clone()
} else {
visual.normal_sprite.clone()
};
}
}
}
/// Apply encumbrance when HaulSlot is occupied, remove when empty.
/// Fires InventoryChangedEvent so update_encumbrance recalculates walk_speed.
///
/// Uses Changed<HaulSlot> so this only runs for entities whose slot changed this frame.
pub fn haul_encumbrance_system(
mut query: Query<(Entity, &HaulSlot, &mut Ambulatory), Changed<HaulSlot>>,
mut events: MessageWriter<InventoryChangedEvent>,
) {
for (entity, haul_slot, mut ambulatory) in query.iter_mut() {
if haul_slot.is_occupied() {
ambulatory.walk_speed = ambulatory.base_walk_speed * ENCUMBERED_SPEED_MULTIPLIER;
} else {
ambulatory.walk_speed = ambulatory.base_walk_speed;
}
events.write(InventoryChangedEvent { carrier: entity });
}
}
/// Sync cargo visual position when tile_pos changes.
/// When cargo is picked up or dropped, tile_pos updates but Transform doesn't.
/// This system keeps Transform in sync with the logical position.
pub fn cargo_position_sync_system(mut query: Query<(&Cargo, &mut Transform), Changed<Cargo>>) {
for (cargo, mut transform) in query.iter_mut() {
transform.translation = Vec3::new(
cargo.tile_pos.x as f32,
cargo.tile_pos.y as f32,
cargo.tile_pos.z as f32 - ITEM_Z_FIGHTING_OFFSET,
);
}
}