Skip job searches that cannot succeed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

author
Marijn Besseling <njirambem@gmail.com> · 2026-10-24 22:48 UTC
commit
2102323480f9007abf132d375759f3b56050fd83
parent
37868d8245
tree
browse at this commit

1 file changed +52 -13

src/jobs/mod.rs +52 -13

@@ -62,6 +62,7 @@ const DARK_WORK_RATE: u32 = 1;
62 62 pub const EXPANSION_TILES: usize = 8;
63 63 pub const MIN_EMPTY_STOCKPILE_TILES: usize = 3;
64 64 const SEARCH_TILES: usize = 15000;
65 +const FORAGE_SEARCH_TILES: usize = 5000;
65 66 const WANDER_RADIUS: i32 = 8;
66 67 const IDLE_SECS: Range<f32> = 1.0..4.0;
67 68 const IDLE_AFTER_ORDER_SECS: f32 = 10.0;
@@ -434,8 +435,18 @@ pub fn find_work(
434 435 from: UVec2,
435 436 wanted: impl Fn(UVec2, ItemKind) -> bool,
436 437 ) -> Option<Task> {
438 + find_work_within(map, reservations, from, SEARCH_TILES, wanted)
439 +}
440 +
441 +pub fn find_work_within(
442 + map: &WorldMap,
443 + reservations: &Reservations,
444 + from: UVec2,
445 + max_tiles: usize,
446 + wanted: impl Fn(UVec2, ItemKind) -> bool,
447 +) -> Option<Task> {
437 448 let mut found = None;
438 - nearest_tile(map, from, SEARCH_TILES, |stand| {
449 + nearest_tile(map, from, max_tiles, |stand| {
439 450 found = NEIGHBORS.iter().find_map(|&d| {
440 451 let site = stand.as_ivec2() + d;
441 452 if site.x < 0 || site.y < 0 {
@@ -552,12 +563,11 @@ pub fn find_sowing(
552 563 reservations: &Reservations,
553 564 from: UVec2,
554 565 ) -> Option<Task> {
555 - if !zones.has_farm() {
556 - return None;
557 - }
558 - let site = nearest_tile(map, from, SEARCH_TILES, |p| {
559 - sowable(map, zones, p) && reservations.is_free(Target::Site(p))
560 - })?;
566 + let open: HashSet<UVec2> = zones
567 + .farm_tiles()
568 + .filter(|&p| sowable(map, zones, p) && reservations.is_free(Target::Site(p)))
569 + .collect();
570 + let site = nearest_of(map, from, SEARCH_TILES, &open)?;
561 571 Some(Task::Work {
562 572 site,
563 573 kind: WorkKind::Sow,
@@ -674,6 +684,7 @@ pub struct BuildContext<'a> {
674 684 pub structures: &'a [(UVec2, StructureKind, u32)],
675 685 pub planned: &'a [(UVec2, StructureKind)],
676 686 pub blueprints: &'a Blueprints,
687 + pub occupied: &'a HashSet<UVec2>,
677 688 }
678 689
679 690 impl BuildContext<'_> {
@@ -682,10 +693,8 @@ impl BuildContext<'_> {
682 693 && !self.zones.is_stockpile(tile)
683 694 && !self.zones.is_farm(tile)
684 695 && self.map.plant(tile.as_ivec2()).is_none()
685 - && !self.structures.iter().any(|(pos, ..)| *pos == tile)
686 - && !self.planned.iter().any(|(pos, _)| *pos == tile)
696 + && !self.occupied.contains(&tile)
687 697 && self.blueprints.get(tile).is_none()
688 - && !self.items.iter().any(|(_, pos)| pos.0 == tile)
689 698 && self.reservations.is_free(Target::Site(tile))
690 699 }
691 700
@@ -983,6 +992,18 @@ fn choose_jobs(
983 992 plans: (Res<Homes>, Res<Designations>),
984 993 ) {
985 994 let (homes, designations) = plans;
995 + let mut anyone_ready = false;
996 + for (_, _, path, job, mut idle, _, rest, hunger, ..) in &mut colonists {
997 + let idle_now = job.0.is_none() && path.0.is_empty();
998 + if idle_now {
999 + idle.0.tick(time.delta());
1000 + }
1001 + anyone_ready |=
1002 + (idle_now && idle.0.is_finished()) || rest.0 < EXHAUSTED_REST || hunger.0 < STARVING;
1003 + }
1004 + if !anyone_ready {
1005 + return;
1006 + }
986 1007 let wildlife: Vec<Wildlife> = animals
987 1008 .iter()
988 1009 .map(|(e, pos, a, tame)| (e, pos.0, a.kind, tame))
@@ -1057,7 +1078,7 @@ fn choose_jobs(
1057 1078 job.0 = None;
1058 1079 path.0.truncate(1);
1059 1080 }
1060 - let rested = idle.0.tick(time.delta()).is_finished();
1081 + let rested = idle.0.is_finished();
1061 1082 if job.0.is_some() || !path.0.is_empty() || !(rested || urgent) {
1062 1083 continue;
1063 1084 }
@@ -1105,6 +1126,12 @@ fn choose_jobs(
1105 1126 }
1106 1127 storage.map(|to| Task::Haul { pickup: None, to })
1107 1128 } else {
1129 + let occupied: HashSet<UVec2> = structures_list
1130 + .iter()
1131 + .map(|(pos, ..)| *pos)
1132 + .chain(planned.iter().map(|(pos, _)| *pos))
1133 + .chain(snapshot.iter().map(|(_, pos)| pos.0))
1134 + .collect();
1108 1135 let build = BuildContext {
1109 1136 map: &map,
1110 1137 zones: &zones,
@@ -1113,6 +1140,7 @@ fn choose_jobs(
1113 1140 structures: &structures_list,
1114 1141 planned: &planned,
1115 1142 blueprints: &blueprints,
1143 + occupied: &occupied,
1116 1144 };
1117 1145 let cooking = || {
1118 1146 (wanted(ItemKind::Meal) && raw_food_source(&build, tile.0).is_some())
@@ -1141,7 +1169,14 @@ fn choose_jobs(
1141 1169 find_butchering(&map, &reservations, &snapshot, tile.0)
1142 1170 }),
1143 1171 (WorkType::Food, &|| {
1144 - find_work(&map, &reservations, tile.0, |_, k| k.is_food() && wanted(k))
1172 + ItemKind::ALL
1173 + .iter()
1174 + .any(|&k| k.is_food() && wanted(k))
1175 + .then(|| {
1176 + let food = |_, k: ItemKind| k.is_food() && wanted(k);
1177 + find_work_within(&map, &reservations, tile.0, FORAGE_SEARCH_TILES, food)
1178 + })
1179 + .flatten()
1145 1180 }),
1146 1181 (WorkType::Gather, &designated),
1147 1182 (WorkType::Food, &|| {
@@ -1152,7 +1187,11 @@ fn choose_jobs(
1152 1187 (WorkType::Build, &|| find_bed_site(&build, adults, tile.0)),
1153 1188 (WorkType::Build, &|| find_blueprint_work(&build, tile.0)),
1154 1189 (WorkType::Gather, &|| {
1155 - find_work(&map, &reservations, tile.0, |_, k| wanted(k))
1190 + ItemKind::ALL
1191 + .iter()
1192 + .any(|&k| wanted(k))
1193 + .then(|| find_work(&map, &reservations, tile.0, |_, k| wanted(k)))
1194 + .flatten()
1156 1195 }),
1157 1196 (WorkType::Food, &|| {
1158 1197 wanted(ItemKind::Meat)