fix
This commit is contained in:
@@ -15,10 +15,26 @@ use crate::entities::cargo::{Cargo, Haulable};
|
|||||||
use crate::entities::item::constants::ITEM_Z_FIGHTING_OFFSET;
|
use crate::entities::item::constants::ITEM_Z_FIGHTING_OFFSET;
|
||||||
use crate::entities::item::inventory::constants::SIZE_LARGE;
|
use crate::entities::item::inventory::constants::SIZE_LARGE;
|
||||||
use crate::world::tiles::TileMap;
|
use crate::world::tiles::TileMap;
|
||||||
|
use crate::world::VisibleGameEntity;
|
||||||
|
|
||||||
/// Weight of a single log in kg. Enough to encumber a dorf carrying one.
|
/// Weight of a single log in kg. Enough to encumber a dorf carrying one.
|
||||||
pub const LOG_WEIGHT_KG: u32 = 15;
|
pub const LOG_WEIGHT_KG: u32 = 15;
|
||||||
|
|
||||||
|
/// Find the standable surface tile at world XY. Returns the tile one above
|
||||||
|
/// the highest floor tile where an entity can stand, or None if not found.
|
||||||
|
fn find_surface_at(world_x: i32, world_y: i32, tilemap: &TileMap) -> Option<IVec3> {
|
||||||
|
for z in -3i32..=4i32 {
|
||||||
|
let floor_pos = IVec3::new(world_x, world_y, z * ITILE_SIZE);
|
||||||
|
if tilemap.floor_tiles.contains_key(&floor_pos) {
|
||||||
|
let above = IVec3::new(world_x, world_y, floor_pos.z + ITILE_SIZE);
|
||||||
|
if tilemap.is_standable(above) {
|
||||||
|
return Some(above);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Spawn a Cargo log entity with a directional fall bias.
|
/// Spawn a Cargo log entity with a directional fall bias.
|
||||||
///
|
///
|
||||||
/// `tile_pos` — world position of the trunk tile this log came from.
|
/// `tile_pos` — world position of the trunk tile this log came from.
|
||||||
@@ -50,14 +66,18 @@ pub fn spawn_log_cargo(
|
|||||||
let jitter_tiles = rng.random_range(0u32..=2) as f32 - 1.0; // -1, 0, or +1
|
let jitter_tiles = rng.random_range(0u32..=2) as f32 - 1.0; // -1, 0, or +1
|
||||||
|
|
||||||
let offset = fall_direction * fall_tiles + perp * jitter_tiles;
|
let offset = fall_direction * fall_tiles + perp * jitter_tiles;
|
||||||
let biased_pos = IVec3::new(
|
let biased_xy_x = tile_pos.x + (offset.x * ITILE_SIZE as f32).round() as i32;
|
||||||
tile_pos.x + (offset.x * ITILE_SIZE as f32).round() as i32,
|
let biased_xy_y = tile_pos.y + (offset.y * ITILE_SIZE as f32).round() as i32;
|
||||||
tile_pos.y + (offset.y * ITILE_SIZE as f32).round() as i32,
|
|
||||||
tile_pos.z,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find nearest free tile from biased position
|
// Find the actual standable surface at this XY — logs land on the floor
|
||||||
let drop_pos = tilemap.find_nearest_free_cargo_tile(biased_pos, 4, &[])?;
|
let biased_pos = find_surface_at(biased_xy_x, biased_xy_y, tilemap).unwrap_or(IVec3::new(
|
||||||
|
biased_xy_x,
|
||||||
|
biased_xy_y,
|
||||||
|
tile_pos.z,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Find nearest free tile from biased position (increased radius for scattered logs)
|
||||||
|
let drop_pos = tilemap.find_nearest_free_cargo_tile(biased_pos, 8, &[])?;
|
||||||
|
|
||||||
// TODO: OuchEvent — if a living entity occupies drop_pos, they take impact damage.
|
// TODO: OuchEvent — if a living entity occupies drop_pos, they take impact damage.
|
||||||
// Check tilemap occupancy here when the combat/injury system exists.
|
// Check tilemap occupancy here when the combat/injury system exists.
|
||||||
@@ -87,6 +107,7 @@ pub fn spawn_log_cargo(
|
|||||||
))
|
))
|
||||||
.with_scale(Vec3::splat(PIXEL_RATIO)),
|
.with_scale(Vec3::splat(PIXEL_RATIO)),
|
||||||
Visibility::Visible,
|
Visibility::Visible,
|
||||||
|
VisibleGameEntity,
|
||||||
))
|
))
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
|
|||||||
+21
-16
@@ -26,7 +26,7 @@ use smallvec::SmallVec;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use crate::constants::ITILE_SIZE;
|
use crate::constants::ITILE_SIZE;
|
||||||
use crate::entities::cargo::{Cargo, Haulable};
|
use crate::entities::cargo::{Cargo, HaulSlot, Haulable};
|
||||||
use crate::entities::tasks::components::{
|
use crate::entities::tasks::components::{
|
||||||
ChopStep, HaulStep, Task, TaskQueue, TaskState, CHOP_TICKS_DEFAULT,
|
ChopStep, HaulStep, Task, TaskQueue, TaskState, CHOP_TICKS_DEFAULT,
|
||||||
};
|
};
|
||||||
@@ -53,17 +53,14 @@ pub enum DemoState {
|
|||||||
/// Tree has been felled. Working through the haul queue.
|
/// Tree has been felled. Working through the haul queue.
|
||||||
///
|
///
|
||||||
/// `unassigned` — logs not yet claimed, ordered nearest-to-origin first.
|
/// `unassigned` — logs not yet claimed, ordered nearest-to-origin first.
|
||||||
/// `in_progress` — log entities currently assigned to a dorf.
|
/// `in_progress` — dorf entities currently executing a HaulCargo task for this tree.
|
||||||
///
|
/// A dorf is removed when they return to idle (log dropped, task complete).
|
||||||
/// Each tick: assign idle dorfs from unassigned. When a log entity
|
|
||||||
/// disappears from cargo_tiles it has been dropped at destination —
|
|
||||||
/// remove it from in_progress. When both are empty, go Idle.
|
|
||||||
Hauling {
|
Hauling {
|
||||||
felled_trunk_pos: IVec3,
|
felled_trunk_pos: IVec3,
|
||||||
/// Queue of (log_entity, log_tile_pos) not yet assigned.
|
/// Queue of (log_entity, log_tile_pos) not yet assigned.
|
||||||
/// Front = highest priority (nearest to origin).
|
/// Front = highest priority (nearest to origin).
|
||||||
unassigned: VecDeque<(Entity, IVec3)>,
|
unassigned: VecDeque<(Entity, IVec3)>,
|
||||||
/// Log entities currently being hauled by a dorf.
|
/// Dorf entities currently hauling a log for this tree.
|
||||||
in_progress: FxHashSet<Entity>,
|
in_progress: FxHashSet<Entity>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -83,6 +80,7 @@ pub fn demo_system(
|
|||||||
chunk_map: Res<ChunkMap>,
|
chunk_map: Res<ChunkMap>,
|
||||||
tree_parts: Query<(Entity, &TreePart)>,
|
tree_parts: Query<(Entity, &TreePart)>,
|
||||||
cargo_query: Query<(Entity, &Cargo), With<Haulable>>,
|
cargo_query: Query<(Entity, &Cargo), With<Haulable>>,
|
||||||
|
haul_slot_query: Query<&HaulSlot>,
|
||||||
mut dorf_query: Query<(Entity, &mut TaskQueue, &mut TaskState, &Transform)>,
|
mut dorf_query: Query<(Entity, &mut TaskQueue, &mut TaskState, &Transform)>,
|
||||||
) {
|
) {
|
||||||
match &mut *demo_state {
|
match &mut *demo_state {
|
||||||
@@ -139,11 +137,20 @@ pub fn demo_system(
|
|||||||
.unwrap_or(trunk_pos);
|
.unwrap_or(trunk_pos);
|
||||||
|
|
||||||
// Find one idle dorf — prefer closest to the tree.
|
// Find one idle dorf — prefer closest to the tree.
|
||||||
|
// Don't interrupt a dorf still carrying cargo.
|
||||||
let mut best_dorf: Option<(Entity, i32)> = None;
|
let mut best_dorf: Option<(Entity, i32)> = None;
|
||||||
for (entity, queue, state, transform) in dorf_query.iter() {
|
for (entity, queue, state, transform) in dorf_query.iter() {
|
||||||
if !is_idle_dorf(&queue, &state) {
|
if !is_idle_dorf(&queue, &state) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Skip dorfs still carrying cargo
|
||||||
|
if haul_slot_query
|
||||||
|
.get(entity)
|
||||||
|
.map(|h| h.is_occupied())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let pos = transform.translation.as_ivec3();
|
let pos = transform.translation.as_ivec3();
|
||||||
let dx = (pos.x - lowest_trunk.x).abs() / ITILE_SIZE;
|
let dx = (pos.x - lowest_trunk.x).abs() / ITILE_SIZE;
|
||||||
let dy = (pos.y - lowest_trunk.y).abs() / ITILE_SIZE;
|
let dy = (pos.y - lowest_trunk.y).abs() / ITILE_SIZE;
|
||||||
@@ -252,14 +259,12 @@ pub fn demo_system(
|
|||||||
} => {
|
} => {
|
||||||
let felled_trunk_pos = *felled_trunk_pos;
|
let felled_trunk_pos = *felled_trunk_pos;
|
||||||
|
|
||||||
// Remove completed hauls from in_progress —
|
// Remove dorfs that have returned to idle — their haul is complete
|
||||||
// a log is done when it's no longer in cargo_tiles (picked up by dorf).
|
in_progress.retain(|&dorf_entity| {
|
||||||
// This is correct: once picked up, the haul is committed from demo's perspective.
|
dorf_query
|
||||||
in_progress.retain(|&log_entity| {
|
.get(dorf_entity)
|
||||||
cargo_query
|
.map(|(_, queue, state, _)| !is_idle_dorf(queue, state))
|
||||||
.get(log_entity)
|
.unwrap_or(false) // entity gone = treat as done
|
||||||
.map(|(_, cargo)| tilemap.cargo_tiles.contains_key(&cargo.tile_pos))
|
|
||||||
.unwrap_or(false)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assign idle dorfs to unassigned logs
|
// Assign idle dorfs to unassigned logs
|
||||||
@@ -318,7 +323,7 @@ pub fn demo_system(
|
|||||||
step: HaulStep::MovingToCargo,
|
step: HaulStep::MovingToCargo,
|
||||||
});
|
});
|
||||||
*state = TaskState::Pending;
|
*state = TaskState::Pending;
|
||||||
in_progress.insert(log_entity);
|
in_progress.insert(dorf_entity);
|
||||||
} else {
|
} else {
|
||||||
// Couldn't assign — put log back at front of queue
|
// Couldn't assign — put log back at front of queue
|
||||||
unassigned.push_front((log_entity, log_pos));
|
unassigned.push_front((log_entity, log_pos));
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ pub fn task_executor_system(
|
|||||||
if fall_dir == Vec2::ZERO {
|
if fall_dir == Vec2::ZERO {
|
||||||
fall_dir = Vec2::new(1.0, 0.0); // default: fall east
|
fall_dir = Vec2::new(1.0, 0.0); // default: fall east
|
||||||
}
|
}
|
||||||
let log_sprite = asset_server.load("tree_trunk.png");
|
let log_sprite: Handle<Image> = asset_server.load("log_cargo.png");
|
||||||
for &pos in trunk_positions.iter() {
|
for &pos in trunk_positions.iter() {
|
||||||
crate::entities::cargo::spawn_log_cargo(
|
crate::entities::cargo::spawn_log_cargo(
|
||||||
&mut commands,
|
&mut commands,
|
||||||
|
|||||||
Reference in New Issue
Block a user