| @@ -114,6 +114,11 @@ pub enum Task { |
| 114 |
114 |
kind: AnimalKind, |
| 115 |
115 |
progress: u32, |
| 116 |
116 |
}, |
|
117 |
+ Cook { |
|
118 |
+ pickup: Option<UVec2>, |
|
119 |
+ station: UVec2, |
|
120 |
+ progress: u32, |
|
121 |
+ }, |
| 117 |
122 |
} |
| 118 |
123 |
|
| 119 |
124 |
pub fn work_rate(light: &LightMap, tile: UVec2) -> u32 { |
| @@ -226,6 +231,7 @@ pub enum Target { |
| 226 |
231 |
StockpileExpansion, |
| 227 |
232 |
SleepSpot(UVec2), |
| 228 |
233 |
Animal(Entity), |
|
234 |
+ Station(UVec2), |
| 229 |
235 |
} |
| 230 |
236 |
|
| 231 |
237 |
#[derive(Resource, Default, Debug)] |
| @@ -367,6 +373,14 @@ fn reserve_task(reservations: &mut Reservations, task: Task, by: Entity) { |
| 367 |
373 |
} |
| 368 |
374 |
} |
| 369 |
375 |
Task::Hunt { animal, .. } => reservations.reserve(Target::Animal(animal), by), |
|
376 |
+ Task::Cook { |
|
377 |
+ pickup, station, .. |
|
378 |
+ } => { |
|
379 |
+ reservations.reserve(Target::Station(station), by); |
|
380 |
+ if let Some(pickup) = pickup { |
|
381 |
+ reservations.reserve(Target::Pickup(pickup), by); |
|
382 |
+ } |
|
383 |
+ } |
| 370 |
384 |
Task::Build { site, pickup, .. } => { |
| 371 |
385 |
reservations.reserve(Target::Site(site), by); |
| 372 |
386 |
if let Some(pickup) = pickup { |
| @@ -656,6 +670,51 @@ impl BuildContext<'_> { |
| 656 |
670 |
} |
| 657 |
671 |
} |
| 658 |
672 |
|
|
673 |
+pub const COOK_INPUT: u32 = 6; |
|
674 |
+pub const COOK_WORK: u32 = 300; |
|
675 |
+pub const COOK_MIN_FUEL: u32 = crate::sim::TICKS_PER_HOUR as u32; |
|
676 |
+ |
|
677 |
+pub fn find_campfire(ctx: &BuildContext, from: UVec2) -> Option<Task> { |
|
678 |
+ let home = ctx.zones.home(from); |
|
679 |
+ if !ctx.campfire_needed(home) { |
|
680 |
+ return None; |
|
681 |
+ } |
|
682 |
+ let site = ctx.site_near(home, HOME_RADIUS)?; |
|
683 |
+ ctx.build_task(from, site, BuildKind::Structure(StructureKind::Campfire)) |
|
684 |
+} |
|
685 |
+ |
|
686 |
+pub fn raw_food_source(ctx: &BuildContext, from: UVec2) -> Option<UVec2> { |
|
687 |
+ nearest_tile(ctx.map, from, SEARCH_TILES, |p| { |
|
688 |
+ ctx.reservations.is_free(Target::Pickup(p)) |
|
689 |
+ && ctx.items.iter().any(|(item, pos)| { |
|
690 |
+ pos.0 == p && item.kind.is_raw_food() && item.count >= COOK_INPUT |
|
691 |
+ }) |
|
692 |
+ }) |
|
693 |
+} |
|
694 |
+ |
|
695 |
+pub fn find_cooking(ctx: &BuildContext, from: UVec2) -> Option<Task> { |
|
696 |
+ let station = ctx |
|
697 |
+ .structures |
|
698 |
+ .iter() |
|
699 |
+ .filter(|&&(pos, kind, fuel)| { |
|
700 |
+ kind == StructureKind::Campfire |
|
701 |
+ && fuel >= COOK_MIN_FUEL |
|
702 |
+ && ctx.reservations.is_free(Target::Station(pos)) |
|
703 |
+ }) |
|
704 |
+ .map(|&(pos, ..)| pos) |
|
705 |
+ .min_by(|a, b| { |
|
706 |
+ a.as_vec2() |
|
707 |
+ .distance_squared(from.as_vec2()) |
|
708 |
+ .total_cmp(&b.as_vec2().distance_squared(from.as_vec2())) |
|
709 |
+ })?; |
|
710 |
+ let pickup = raw_food_source(ctx, from)?; |
|
711 |
+ Some(Task::Cook { |
|
712 |
+ pickup: Some(pickup), |
|
713 |
+ station, |
|
714 |
+ progress: 0, |
|
715 |
+ }) |
|
716 |
+} |
|
717 |
+ |
| 659 |
718 |
pub fn find_lighting(ctx: &BuildContext, from: UVec2) -> Option<Task> { |
| 660 |
719 |
let home = ctx.zones.home(from); |
| 661 |
720 |
let (kind, site) = if ctx.campfire_needed(home) { |
| @@ -764,8 +823,10 @@ fn choose_jobs( |
| 764 |
823 |
.collect(); |
| 765 |
824 |
let mut pending = on_the_way(&map, colonists.iter().map(|c| c.3.0)); |
| 766 |
825 |
for job in colonists.iter().filter_map(|c| c.3.0) { |
| 767 |
|
- if let Task::Hunt { kind, .. } = job.task { |
| 768 |
|
- *pending.entry(ItemKind::Meat).or_insert(0) += kind.meat(); |
|
826 |
+ match job.task { |
|
827 |
+ Task::Hunt { kind, .. } => *pending.entry(ItemKind::Meat).or_insert(0) += kind.meat(), |
|
828 |
+ Task::Cook { .. } => *pending.entry(ItemKind::Meal).or_insert(0) += 1, |
|
829 |
+ _ => {} |
| 769 |
830 |
} |
| 770 |
831 |
} |
| 771 |
832 |
let hour = hour_of_day(*tick); |
| @@ -856,6 +917,15 @@ fn choose_jobs( |
| 856 |
917 |
}; |
| 857 |
918 |
find_expansion(&map, &zones, &reservations, &snapshot, &blueprints) |
| 858 |
919 |
.or_else(|| dark.then(|| find_lighting(&build, tile.0)).flatten()) |
|
920 |
+ .or_else(|| { |
|
921 |
+ let cooking = |
|
922 |
+ wanted(ItemKind::Meal) && raw_food_source(&build, tile.0).is_some(); |
|
923 |
+ cooking |
|
924 |
+ .then(|| { |
|
925 |
+ find_cooking(&build, tile.0).or_else(|| find_campfire(&build, tile.0)) |
|
926 |
+ }) |
|
927 |
+ .flatten() |
|
928 |
+ }) |
| 859 |
929 |
.or_else(|| find_work(&map, &reservations, tile.0, wanted)) |
| 860 |
930 |
.or_else(|| { |
| 861 |
931 |
wanted(ItemKind::Meat) |
| @@ -873,8 +943,12 @@ fn choose_jobs( |
| 873 |
943 |
{ |
| 874 |
944 |
*pending.entry(work.item).or_insert(0) += work.count; |
| 875 |
945 |
} |
| 876 |
|
- if let Task::Hunt { kind, .. } = task { |
| 877 |
|
- *pending.entry(ItemKind::Meat).or_insert(0) += kind.meat(); |
|
946 |
+ match task { |
|
947 |
+ Task::Hunt { kind, .. } => { |
|
948 |
+ *pending.entry(ItemKind::Meat).or_insert(0) += kind.meat() |
|
949 |
+ } |
|
950 |
+ Task::Cook { .. } => *pending.entry(ItemKind::Meal).or_insert(0) += 1, |
|
951 |
+ _ => {} |
| 878 |
952 |
} |
| 879 |
953 |
if let Task::Build { |
| 880 |
954 |
site, |
| @@ -933,7 +1007,7 @@ fn run_jobs( |
| 933 |
1007 |
tick: Res<SimTick>, |
| 934 |
1008 |
mut blueprints: ResMut<Blueprints>, |
| 935 |
1009 |
mut items: ItemQuery, |
| 936 |
|
- structures: Query<&TilePos, (With<Structure>, Without<Colonist>)>, |
|
1010 |
+ structures: Query<(&TilePos, &Structure), Without<Colonist>>, |
| 937 |
1011 |
animals: Query<&TilePos, (With<Animal>, Without<Colonist>)>, |
| 938 |
1012 |
mut changed: MessageWriter<TileChanged>, |
| 939 |
1013 |
mut colonists: Query< |
| @@ -963,6 +1037,72 @@ fn run_jobs( |
| 963 |
1037 |
} |
| 964 |
1038 |
let step = match &mut job.task { |
| 965 |
1039 |
Task::MoveTo(_) => Step::Done, |
|
1040 |
+ Task::Cook { |
|
1041 |
+ pickup, |
|
1042 |
+ station, |
|
1043 |
+ progress, |
|
1044 |
+ } => { |
|
1045 |
+ let station = *station; |
|
1046 |
+ let fire = structures |
|
1047 |
+ .iter() |
|
1048 |
+ .any(|(pos, s)| pos.0 == station && s.kind == StructureKind::Campfire); |
|
1049 |
+ let ours = reservations.holder(Target::Station(station)) == Some(entity); |
|
1050 |
+ if !fire || !ours { |
|
1051 |
+ Step::Done |
|
1052 |
+ } else if let Some(from) = *pickup { |
|
1053 |
+ if reservations.holder(Target::Pickup(from)) != Some(entity) { |
|
1054 |
+ Step::Done |
|
1055 |
+ } else if tile.0 != from { |
|
1056 |
+ walk_to(&map, tile.0, from, &mut path) |
|
1057 |
+ } else { |
|
1058 |
+ let load = take_items(&mut commands, &mut items, from, COOK_INPUT) |
|
1059 |
+ .filter(|load| load.kind.is_raw_food() && load.count > 0); |
|
1060 |
+ carrying.0 = load; |
|
1061 |
+ reservations.release(Target::Pickup(from)); |
|
1062 |
+ match load { |
|
1063 |
+ Some(load) if load.count >= COOK_INPUT => { |
|
1064 |
+ *pickup = None; |
|
1065 |
+ Step::Continue |
|
1066 |
+ } |
|
1067 |
+ _ => Step::Done, |
|
1068 |
+ } |
|
1069 |
+ } |
|
1070 |
+ } else if !carrying |
|
1071 |
+ .0 |
|
1072 |
+ .is_some_and(|load| load.kind.is_raw_food() && load.count >= COOK_INPUT) |
|
1073 |
+ { |
|
1074 |
+ Step::Done |
|
1075 |
+ } else if !is_adjacent(tile.0.as_ivec2(), station.as_ivec2()) { |
|
1076 |
+ match find_path_adjacent(&map, tile.0, station) { |
|
1077 |
+ Some(p) if !p.is_empty() => { |
|
1078 |
+ path.0 = p.into(); |
|
1079 |
+ Step::Continue |
|
1080 |
+ } |
|
1081 |
+ _ => Step::Done, |
|
1082 |
+ } |
|
1083 |
+ } else { |
|
1084 |
+ *progress += work_rate(&light, tile.0); |
|
1085 |
+ if *progress < COOK_WORK { |
|
1086 |
+ Step::Continue |
|
1087 |
+ } else { |
|
1088 |
+ carrying.0 = carrying.0.and_then(|load| { |
|
1089 |
+ (load.count > COOK_INPUT).then_some(Item { |
|
1090 |
+ count: load.count - COOK_INPUT, |
|
1091 |
+ ..load |
|
1092 |
+ }) |
|
1093 |
+ }); |
|
1094 |
+ drop_items( |
|
1095 |
+ &mut commands, |
|
1096 |
+ &mut items, |
|
1097 |
+ &item_assets, |
|
1098 |
+ tile.0, |
|
1099 |
+ ItemKind::Meal, |
|
1100 |
+ 1, |
|
1101 |
+ ); |
|
1102 |
+ Step::Done |
|
1103 |
+ } |
|
1104 |
+ } |
|
1105 |
+ } |
| 966 |
1106 |
Task::Hunt { |
| 967 |
1107 |
animal, |
| 968 |
1108 |
kind, |
| @@ -1127,7 +1267,7 @@ fn run_jobs( |
| 1127 |
1267 |
_ => blueprints.get(site) == Some(kind), |
| 1128 |
1268 |
}; |
| 1129 |
1269 |
let blocked = map.plant(site.as_ivec2()).is_some() |
| 1130 |
|
- || (kind != BuildKind::Roof && structures.iter().any(|pos| pos.0 == site)); |
|
1270 |
+ || (kind != BuildKind::Roof && structures.iter().any(|(pos, _)| pos.0 == site)); |
| 1131 |
1271 |
let (material, cost) = kind.cost(); |
| 1132 |
1272 |
if !ours || blocked || !planned { |
| 1133 |
1273 |
Step::Done |