77 lines
2.9 KiB
Rust
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,
|
|
);
|
|
}
|
|
}
|