Count only stock within the colony's reach

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

author
Marijn Besseling <njirambem@gmail.com> · 2026-10-24 21:34 UTC
commit
588a85c4565cbcae8d657e76a27f7180e916d90f
parent
f85e87cb45
tree
browse at this commit

5 files changed +64 -8

src/economy.rs +5 -0

@@ -17,6 +17,7 @@ impl Plugin for EconomyPlugin {
17 17 }
18 18 }
19 19
20 +pub const COLONY_RADIUS: i32 = 60;
20 21 const HOURS_OF_MEMORY: f32 = 24.0;
21 22 const MAX_GOAL: f32 = 300.0;
22 23
@@ -177,6 +178,10 @@ impl ColonyGoals {
177 178 }
178 179 }
179 180
181 +pub fn in_colony(home: UVec2, tile: UVec2) -> bool {
182 + (tile.as_ivec2() - home.as_ivec2()).abs().max_element() <= COLONY_RADIUS
183 +}
184 +
180 185 pub fn planned_needs(blueprints: &Blueprints) -> HashMap<Stock, u32> {
181 186 let mut needs = HashMap::new();
182 187 for kind in blueprints.0.values() {

src/jobs/mod.rs +7 -2

@@ -10,7 +10,7 @@ use crate::{
10 10 animals::{Animal, AnimalKind},
11 11 building::{Blueprints, BuildKind},
12 12 colonist::{Colonist, Path, repath},
13 - economy::ColonyGoals,
13 + economy::{ColonyGoals, in_colony},
14 14 food::{EAT_TICKS, Hunger, STARVING, find_food, portion, wants_food},
15 15 items::{
16 16 CARRY_CAPACITY, Carrying, Item, ItemAssets, ItemKind, ItemQuery, drop_items, room_for,
@@ -807,10 +807,15 @@ fn choose_jobs(
807 807 .collect();
808 808 let dark = dark_soon(*tick);
809 809 let carried: Vec<Item> = colonists.iter().filter_map(|c| c.5.0).collect();
810 + let home = zones.home(map.size / 2);
810 811 let stocks: HashMap<ItemKind, u32> = ItemKind::ALL
811 812 .iter()
812 813 .map(|&kind| {
813 - let all = items.iter().map(|(_, item, _)| item).chain(carried.iter());
814 + let all = items
815 + .iter()
816 + .filter(|(_, _, pos)| in_colony(home, pos.0))
817 + .map(|(_, item, _)| item)
818 + .chain(carried.iter());
814 819 (kind, stock(all, kind))
815 820 })
816 821 .collect();

src/jobs/tests.rs +34 -0

@@ -4,6 +4,7 @@ use super::*;
4 4 use crate::{
5 5 animals::{Animal, AnimalKind},
6 6 colonist::follow_path,
7 + economy::COLONY_RADIUS,
7 8 food::Hunger,
8 9 items::STACK_LIMIT,
9 10 sim::{SimPos, sim_pos},
@@ -939,6 +940,39 @@ fn ripe_crops_are_harvested_into_vegetables_and_resown() {
939 940 assert!(map.growth(farm[0].as_ivec2()) < 0.1);
940 941 }
941 942
943 +// Food the colony cannot reach must not count towards its stock: a stray stack far from home
944 +// once satisfied the raw food target, so nobody harvested ripe crops while everyone starved.
945 +#[test]
946 +fn stock_out_of_the_colonys_reach_does_not_stop_harvesting() {
947 + let farm = UVec2::new(8, 8);
948 + let mut map = WorldMap::filled(UVec2::splat(150), Terrain::Grass);
949 + map.set(farm, Terrain::Soil);
950 + map.set_plant(farm, Some(Plant::Crop));
951 + map.set_growth(farm, 1.0);
952 + let mut app = test_app(map.clone());
953 + set_goals(&mut app, &[(ItemKind::Vegetables, 10)]);
954 + put_beds(&mut app, 1);
955 + let mut zones = app.world_mut().resource_mut::<Zones>();
956 + zones.add_farm_tiles(&map, [farm]);
957 + zones.add_tiles(&map, [UVec2::new(4, 4)]);
958 + let far = UVec2::new(4, 4) + UVec2::splat(COLONY_RADIUS as u32 + 1);
959 + put_items(&mut app, far, ItemKind::Meat, 20);
960 + spawn_colonist(&mut app, UVec2::new(2, 2));
961 + run_ticks(&mut app, 60 * 15);
962 + assert!(items_of(&mut app, ItemKind::Vegetables) > 0);
963 +}
964 +
965 +#[test]
966 +fn colonist_searches_cover_the_colony_radius() {
967 + let map = WorldMap::filled(UVec2::splat(200), Terrain::Grass);
968 + let home = UVec2::splat(100);
969 + let corner = home + UVec2::splat(COLONY_RADIUS as u32);
970 + assert_eq!(
971 + nearest_tile(&map, home, SEARCH_TILES, |p| p == corner),
972 + Some(corner)
973 + );
974 +}
975 +
942 976 #[test]
943 977 fn colonists_cook_raw_food_into_meals_at_a_campfire() {
944 978 let mut app = test_app(map_with_trees(&[]));

src/population.rs +10 -3

@@ -4,6 +4,7 @@ use bevy::prelude::*;
4 4
5 5 use crate::{
6 6 colonist::{Colonist, ColonistAssets, NAMES, spawn_colonist},
7 + economy::in_colony,
7 8 food::Hunger,
8 9 items::{Carrying, Item, ItemAssets, ItemQuery, drop_items},
9 10 jobs::{CurrentJob, Job, Reservations, Task},
@@ -123,7 +124,7 @@ fn arrivals(
123 124 mut materials: ResMut<Assets<ColorMaterial>>,
124 125 mut rng: ResMut<SimRng>,
125 126 mut log: ResMut<ColonyLog>,
126 - items: Query<&Item>,
127 + items: Query<(&Item, &TilePos)>,
127 128 structures: Query<&Structure>,
128 129 colonists: Query<&Colonist>,
129 130 ) {
@@ -135,12 +136,18 @@ fn arrivals(
135 136 .iter()
136 137 .filter(|s| s.kind == StructureKind::Bed)
137 138 .count();
138 - let wood = items
139 + let home = zones.home(map.size / 2);
140 + let stored: Vec<&Item> = items
141 + .iter()
142 + .filter(|(_, pos)| in_colony(home, pos.0))
143 + .map(|(item, _)| item)
144 + .collect();
145 + let wood = stored
139 146 .iter()
140 147 .filter(|i| i.kind == crate::items::ItemKind::Wood)
141 148 .map(|i| i.count)
142 149 .sum();
143 - let food = food_in_store(items.iter());
150 + let food = food_in_store(stored.iter().copied());
144 151 let lucky = rng.range_f32(0.0..1.0) < ARRIVAL_CHANCE;
145 152 if !lucky || !prosperous(count, food, beds.saturating_sub(count), wood) {
146 153 return;

src/selection.rs +8 -3

@@ -6,7 +6,7 @@ use crate::{
6 6 building::Blueprints,
7 7 camera::CameraController,
8 8 colonist::{Colonist, Path},
9 - economy::{ColonyGoals, Stock},
9 + economy::{ColonyGoals, Stock, in_colony},
10 10 food::Hunger,
11 11 items::{Carrying, Item, stock},
12 12 jobs::{COOK_WORK, CurrentJob, EXPAND_WORK, Job, Order, Task, WorkKind},
@@ -289,8 +289,9 @@ fn update_tile_info(
289 289 format_clock(*tick),
290 290 time_of_day(hour_of_day(*tick))
291 291 );
292 - let speed = describe_speed(*speed, time.is_paused());
292 + let speed = describe_speed(*speed, time.is_paused(), time.relative_speed());
293 293 let carried: Vec<Item> = colonists.iter().filter_map(|c| c.4.0).collect();
294 + let home = zones.home(map.size / 2);
294 295 let stock_line = |food: bool| {
295 296 Stock::ALL
296 297 .iter()
@@ -300,7 +301,11 @@ fn update_tile_info(
300 301 .members()
301 302 .map(|kind| {
302 303 stock(
303 - items.iter().map(|(item, _)| item).chain(carried.iter()),
304 + items
305 + .iter()
306 + .filter(|(_, pos)| in_colony(home, pos.0))
307 + .map(|(item, _)| item)
308 + .chain(carried.iter()),
304 309 kind,
305 310 )
306 311 })