diff --git a/.gitignore b/.gitignore
index 044cdb598b05139bbaba6c6d2ca89c6f1d0de8d5..395679459596ccf00f20e10590691ed765d3c6bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,6 @@
 .idea/
 .vscode/
 dist/
-artifacts/
\ No newline at end of file
+artifacts/
+
+*.ods#
\ No newline at end of file
diff --git a/assets/encounters/grasslands.encounters.toml b/assets/encounters/grasslands.encounters.toml
new file mode 100644
index 0000000000000000000000000000000000000000..3f51b782ea06629e9ea2c9104de1c2d30d299bc6
--- /dev/null
+++ b/assets/encounters/grasslands.encounters.toml
@@ -0,0 +1,16 @@
+[[encounters]]
+chance = 90
+title = "You hear a rustle in the bushes"
+description = """
+
+"""
+
+[[encounters]]
+chance = 10
+title = "You see a small shrine"
+description = """
+Off to the side of the path is located a small offering shrine to some forgotten god, chipped and covered in moss.
+
+Around the base of the shrine, you see some small totems and other miscellanious offerings. Do you want to leave anything for these ancient gods?"""
+
+[[encounters.options]]
\ No newline at end of file
diff --git a/assets/resources.apack b/assets/resources.apack
index c2669d0b236ff87a757b1a5f33cff77febb1dbfc..3ad723412d4cdd5561d095a77ba3bb17e1554d75 100644
--- a/assets/resources.apack
+++ b/assets/resources.apack
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c7d3705a1bcb0534de927d60743ca9e4f33352bbefeaf1618d6eb91f102639fc
-size 1930588
+oid sha256:08fbd17915eea98e0e3e077c32249781bb0b33d821aeab9c9dac5196eaab6527
+size 1955812
diff --git a/assets/trade_manifests/monastery.manifest.toml b/assets/trade_manifests/monastery.manifest.toml
index e909957b5cd813175998e5fd07eb7d661cf5a3d3..88bc5033b02cde1a5f7d4e63bf2d3f91aec36cd0 100644
--- a/assets/trade_manifests/monastery.manifest.toml
+++ b/assets/trade_manifests/monastery.manifest.toml
@@ -61,16 +61,15 @@ cost_multipliers = [
 ]
 
 [Milk]
-tick_decay = 0.25
-natural_limit = 0
+tick_decay = 1
+natural_limit = 15
 cost_multipliers = [
 	[5, 1.4],
 	[9, 1.25],
-	[10, 1],
-	[15, 0.8],
-	[30, 0.7],
-	[99999, 0.6]
+	[30, 1.1],
+	[99999, 1]
 ]
+oversupply_trend = "halt"
 
 [Cheese]
 tick_decay = 0.25
@@ -97,16 +96,15 @@ cost_multipliers = [
 ]
 
 [Corn]
-tick_decay = 0.25
-natural_limit = 0
+tick_decay = 1.5
+natural_limit = 30
 cost_multipliers = [
 	[5, 1.4],
 	[9, 1.25],
-	[10, 1],
-	[15, 0.8],
-	[30, 0.7],
-	[99999, 0.6]
+	[30, 1.1],
+	[99999, 1]
 ]
+oversupply_trend = "halt"
 
 [Tomato]
 tick_decay = 0.25
@@ -133,25 +131,34 @@ cost_multipliers = [
 ]
 
 [Grapes]
-tick_decay = 0.25
+tick_decay = 1.5
+natural_limit = 30
+cost_multipliers = [
+	[5, 1.4],
+	[9, 1.25],
+	[30, 1.1],
+	[99999, 1]
+]
+oversupply_trend = "halt"
+
+[Wine]
+tick_decay = 0.0
 natural_limit = 0
 cost_multipliers = [
 	[5, 1.4],
 	[9, 1.25],
-	[10, 1],
-	[15, 0.8],
-	[30, 0.7],
-	[99999, 0.6]
+	[30, 1.1],
+	[99999, 1]
 ]
+oversupply_trend = "halt"
 
 [Wheat]
-tick_decay = 0.25
-natural_limit = 0
+tick_decay = 1.5
+natural_limit = 30
 cost_multipliers = [
 	[5, 1.4],
 	[9, 1.25],
-	[10, 1],
-	[15, 0.8],
-	[30, 0.7],
-	[99999, 0.6]
+	[30, 1.1],
+	[99999, 1]
 ]
+oversupply_trend = "halt"
\ No newline at end of file
diff --git a/assets/trade_manifests/ship.manifest.toml b/assets/trade_manifests/ship.manifest.toml
new file mode 100644
index 0000000000000000000000000000000000000000..14e7eea09ceef1785f8cef74e2536f22dbc385f9
--- /dev/null
+++ b/assets/trade_manifests/ship.manifest.toml
@@ -0,0 +1,8 @@
+[Bread]
+tick_decay = 50.0
+natural_limit = 50
+cost_multipliers = [
+	[99999, 0]
+]
+oversupply_trend = "halt"
+undersupply_trend = "produce"
\ No newline at end of file
diff --git a/assets/trade_manifests/whitestone.manifest.toml b/assets/trade_manifests/whitestone.manifest.toml
index dec88c9b36c865d7f0cebc0464a02459205dea46..9beb037c10f2b7e1bc2e9db81ac18abe0ac5a019 100644
--- a/assets/trade_manifests/whitestone.manifest.toml
+++ b/assets/trade_manifests/whitestone.manifest.toml
@@ -167,3 +167,26 @@ cost_multipliers = [
 	[30, 0.7],
 	[99999, 0.6]
 ]
+
+[Bread]
+tick_decay = 0.25
+natural_limit = 0
+cost_multipliers = [
+	[5, 1.4],
+	[9, 1.25],
+	[10, 1],
+	[15, 0.8],
+	[30, 0.7],
+	[99999, 0.6]
+]
+
+[Wine]
+tick_decay = 0.0
+natural_limit = 0
+cost_multipliers = [
+	[5, 1.8],
+	[9, 1.6],
+	[30, 1.4],
+	[99999, 1]
+]
+oversupply_trend = "halt"
\ No newline at end of file
diff --git a/game_core/src/assets/startup.rs b/game_core/src/assets/startup.rs
index 5c607ed230fcd210435be99f527502875eb2c50d..74d4a5bcdc7813fa7dc8343a20cce5a9ac15fbfe 100644
--- a/game_core/src/assets/startup.rs
+++ b/game_core/src/assets/startup.rs
@@ -18,6 +18,7 @@ pub fn start_load_resources(mut loader: AssetTypeLoader) {
 	loader.load_trade_manifest(&[
 		("trade_manifests/whitestone.manifest.toml", "whitestone"),
 		("trade_manifests/monastery.manifest.toml", "monastery"),
+		("trade_manifests/ship.manifest.toml", "ship"),
 	]);
 }
 
diff --git a/game_core/src/persistance/fs_utils.rs b/game_core/src/persistance/fs_utils.rs
index f5b9a76f21a8750cc8f7ac53a78a767e63b0159c..6130e2ac4bbacaaece130ca4e8aabb75c1f69c2f 100644
--- a/game_core/src/persistance/fs_utils.rs
+++ b/game_core/src/persistance/fs_utils.rs
@@ -34,7 +34,21 @@ pub fn read_config_file() -> Option<Options> {
 
 pub fn save_config_file(options: Options) -> Option<()> {
 	let root_dir = get_root_save_dir()?;
+	std::fs::create_dir_all(&root_dir)
+		.inspect_err(|e| {
+			log::error!("{}", e);
+		})
+		.ok()?;
 	let save_path = root_dir.join("config.toml");
 
-	std::fs::write(save_path, toml::to_string_pretty(&options).ok()?).ok()
+	std::fs::write(
+		save_path,
+		toml::to_string_pretty(&options)
+			.inspect_err(|e| {
+				log::error!("{}", e);
+			})
+			.ok()?,
+	)
+	.map(|()| log::info!("SAVED"))
+	.ok()
 }
diff --git a/game_core/src/persistance/save_file.rs b/game_core/src/persistance/save_file.rs
index 681702d2351b8eb64bcc2d15f5f6dcf8ebbce591..65d7cf2a7e00bd1be90877876644b2bda9a6e6c7 100644
--- a/game_core/src/persistance/save_file.rs
+++ b/game_core/src/persistance/save_file.rs
@@ -11,11 +11,19 @@ use crate::persistance::fs_utils::{get_root_save_dir, AUTOSAVE_NAME};
 use crate::states::Player;
 use crate::system::flow::AppState;
 use crate::world::{
-	ActiveLevel, CurrentResidence, DistanceTravelled, EncounterState, HungerState,
+	ActiveLevel, CraftSpecialism, CurrentResidence, DistanceTravelled, EncounterState, HungerState,
 	PendingLoadState, TownName, TradeManifestTickState, TradingState, TravelPath, TravelTarget,
 };
 
-pub type TownPersistenceData = HashMap<String, (TradingState, HungerState, TradeManifestTickState)>;
+pub type TownPersistenceData = HashMap<
+	String,
+	(
+		TradingState,
+		HungerState,
+		TradeManifestTickState,
+		Option<CraftSpecialism>,
+	),
+>;
 
 #[derive(Serialize, Deserialize, Debug, Resource)]
 pub struct PersistenceState {
@@ -80,16 +88,22 @@ pub fn sync_state_to_persistence(
 		&TradingState,
 		&HungerState,
 		&TradeManifestTickState,
+		Option<&CraftSpecialism>,
 	)>,
 	encounters: Res<EncounterState>,
 	active_map: Res<ActiveLevel>,
 ) {
 	let town_states = town_query
 		.iter()
-		.map(|(name, trading, hunger, manifest)| {
+		.map(|(name, trading, hunger, manifest, maybe_spec)| {
 			(
 				name.0.clone(),
-				(trading.clone(), hunger.clone(), manifest.clone()),
+				(
+					trading.clone(),
+					hunger.clone(),
+					manifest.clone(),
+					maybe_spec.cloned(),
+				),
 			)
 		})
 		.collect::<TownPersistenceData>();
diff --git a/game_core/src/ui/sync/sync_trade.rs b/game_core/src/ui/sync/sync_trade.rs
index 7cd58dd4a043a01913f99f0adad2d86118cb27e1..74561248fe038b98fb5af478213521bde4678c0b 100644
--- a/game_core/src/ui/sync/sync_trade.rs
+++ b/game_core/src/ui/sync/sync_trade.rs
@@ -6,7 +6,8 @@ use bevy::prelude::{Entity, Query, Res, ResMut, Resource, With};
 use crate::const_data::{get_goods_from_name, get_goods_from_name_checked, TRADE_GOODS};
 use crate::states::Player;
 use crate::world::{
-	CurrentResidence, HungerState, TownName, TradeGood, TradeManifest, TradingState, TravelTarget,
+	CurrentResidence, HungerState, StarvationMarker, TownName, TradeGood, TradeManifest,
+	TradingState, TravelTarget,
 };
 
 #[derive(Clone)]
@@ -25,6 +26,7 @@ pub struct UITradeData {
 	pub player_entity: Option<Entity>,
 	pub shop_items: Vec<ItemTradeData>,
 	pub hunger_descriptor: String,
+	pub is_starved: bool,
 }
 
 pub fn sync_ui_trade_data(
@@ -43,6 +45,7 @@ pub fn sync_ui_trade_data(
 		&TradingState,
 		&HungerState,
 		&Handle<TradeManifest>,
+		Option<&StarvationMarker>,
 	)>,
 	manifests: Res<Assets<TradeManifest>>,
 	mut trade_data: ResMut<UITradeData>,
@@ -64,21 +67,23 @@ pub fn sync_ui_trade_data(
 			CurrentResidence::TravellingFrom(..) => String::new(),
 		});
 
-	let (town_entity, _, town_trading_state, hunger_state, manifest_handle) = match town_query
-		.into_iter()
-		.find(|(_, name, _, _, _)| town_name == *name.0)
-	{
-		Some(v) => v,
-		None => {
-			trade_data.shop_items = vec![];
-			trade_data.town_entity = None;
-			return;
-		}
-	};
+	let (town_entity, _, town_trading_state, hunger_state, manifest_handle, starvation) =
+		match town_query
+			.into_iter()
+			.find(|(_, name, _, _, _, _)| town_name == *name.0)
+		{
+			Some(v) => v,
+			None => {
+				trade_data.shop_items = vec![];
+				trade_data.town_entity = None;
+				return;
+			}
+		};
 
 	trade_data.town_entity = Some(town_entity);
 	trade_data.town_gold = town_trading_state.gold.max(0) as usize;
 	trade_data.hunger_descriptor = hunger_state.get_town_descriptor();
+	trade_data.is_starved = starvation.is_some();
 
 	for (item_name, trade_good) in TRADE_GOODS.iter() {
 		let manifest = match manifests.get(manifest_handle) {
diff --git a/game_core/src/ui/widgets/settings_panel.rs b/game_core/src/ui/widgets/settings_panel.rs
index eafbf71377eb5894c74ae821d2bde5fb61977400..51a0c36f65cba7ae11697278e923cdcea9c7e5f4 100644
--- a/game_core/src/ui/widgets/settings_panel.rs
+++ b/game_core/src/ui/widgets/settings_panel.rs
@@ -121,6 +121,9 @@ pub fn render_settings_panel(
 							AudioSettings,
 						>| {
 							settings.master_volume = (settings.master_volume - 0.05).max(0.0);
+							save_config_file(Options {
+								audio: *settings
+							});
 						})}
 					/>
 					<BackgroundBundle
@@ -151,6 +154,9 @@ pub fn render_settings_panel(
 							AudioSettings,
 						>| {
 							settings.master_volume = (settings.master_volume + 0.05).min(1.0);
+							save_config_file(Options {
+								audio: *settings
+							});
 						})}
 					/>
 				</BackgroundBundle>
@@ -236,6 +242,9 @@ pub fn render_settings_panel(
 							AudioSettings,
 						>| {
 							settings.ui_volume = (settings.ui_volume - 0.05).max(0.0);
+							save_config_file(Options {
+								audio: *settings
+							});
 						})}
 					/>
 					<BackgroundBundle
@@ -266,6 +275,9 @@ pub fn render_settings_panel(
 							AudioSettings,
 						>| {
 							settings.ui_volume = (settings.ui_volume + 0.05).min(1.0);
+							save_config_file(Options {
+								audio: *settings
+							});
 						})}
 					/>
 				</BackgroundBundle>
diff --git a/game_core/src/ui/widgets/shop_panel.rs b/game_core/src/ui/widgets/shop_panel.rs
index bca69bafa561b203e2f47dd5b21ad7be2cdd69c2..074d0868a7dc9bb292470ff2c2359029a83a3da5 100644
--- a/game_core/src/ui/widgets/shop_panel.rs
+++ b/game_core/src/ui/widgets/shop_panel.rs
@@ -76,7 +76,28 @@ pub fn render_shop_panel(
 ) -> bool {
 	let parent_id = Some(entity);
 
-	if let Ok(props) = query.get(entity) {
+	if ui_data.is_starved {
+		rsx! {
+			<ElementBundle>
+				<TextWidgetBundle
+					text={TextProps {
+						content: "As you wander into the merchant's store, you find only ".to_string(),
+						size: 32.0,
+						..Default::default()
+					}}
+					styles={KStyle {
+						color: value(Color::BLACK),
+						padding: edge_px(20.0),
+						top: stretch(1.0),
+						bottom: stretch(1.0),
+						left: stretch(1.0),
+						right: stretch(1.0),
+						..Default::default()
+					}}
+				/>
+			</ElementBundle>
+		};
+	} else if let Ok(props) = query.get(entity) {
 		rsx! {
 			<ElementBundle>
 				<TextWidgetBundle
diff --git a/game_core/src/ui/widgets/tavern_panel.rs b/game_core/src/ui/widgets/tavern_panel.rs
index dbd1f45865757314538afe30ee250226e994bee5..078e114f022149e717015260c850f9dd8e22f909 100644
--- a/game_core/src/ui/widgets/tavern_panel.rs
+++ b/game_core/src/ui/widgets/tavern_panel.rs
@@ -12,21 +12,68 @@ use crate::ui::components::*;
 use crate::ui::prelude::*;
 use crate::ui::sync::{UITradeData, UITravelInfo};
 use crate::ui::widgets::*;
-use crate::world::{CurrentResidence, MapQuery, TownPaths};
+use crate::world::{
+	CraftSpecialism, CurrentResidence, MapQuery, TownName, TownPaths, TradingState,
+};
 use crate::{basic_widget, empty_props, on_button_click};
 
 empty_props!(TavernPanelProps);
 basic_widget!(TavernPanelProps => TavernPanel);
 
+#[derive(Eq, Default, PartialEq, Debug, Clone, Component)]
+pub struct TavernPanelState {
+	pub hint: Option<String>,
+}
+
 pub fn render_tavern_panel(
 	In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
 	mut commands: Commands,
 	ui_data: Res<UITradeData>,
 	query: Query<&TavernPanelProps>,
+	state_query: Query<&TavernPanelState>,
+	foo: Query<(&TownName, &CraftSpecialism)>,
 ) -> bool {
 	let parent_id = Some(entity);
+	let state_entity = widget_context.use_state(&mut commands, entity, TavernPanelState::default());
+
+	let buy_hint = on_button_click!(
+		ParamSet<(
+			Query<&mut TradingState, With<Player>>,
+			Query<(&TownName, &CraftSpecialism)>,
+			Query<&mut TavernPanelState>
+		)>,
+		|mut params: ParamSet<(
+			Query<&mut TradingState, With<Player>>,
+			Query<(&TownName, &CraftSpecialism)>,
+			Query<&mut TavernPanelState>
+		)>| {
+			let success = if let Ok(mut trade_state) = params.p0().get_single_mut() {
+				trade_state.spend_gold(50)
+			} else {
+				false
+			};
 
-	if let Ok(props) = query.get(entity) {
+			let rumour = if success {
+				let query = params.p1();
+				let mut viable_iter = query.iter();
+				let viable_count = viable_iter.len();
+				if viable_count < 1 {
+					None
+				} else {
+					let selected = viable_iter.nth(fastrand::usize(0..viable_count));
+					selected.map(|(name, craft)| craft.format_rumour(&name.0))
+				}
+			} else {
+				None
+			};
+
+			if let Ok(mut state_state) = params.p2().get_mut(state_entity) {
+				state_state.hint = rumour.clone();
+			}
+		}
+	);
+
+	if let (Ok(props), Ok(state)) = (query.get(entity), state_query.get(state_entity)) {
 		rsx! {
 			<ElementBundle>
 				<TextWidgetBundle
@@ -38,12 +85,35 @@ pub fn render_tavern_panel(
 					}}
 					text={TextProps {
 						size: 32.0,
-						content: String::from("A strange bartender eyes you suspiciously"),
+						content: if ui_data.is_starved { String::from("The corpse of a bartender slumps over the bar") } else { String::from("A strange bartender eyes you suspiciously") },
 						..Default::default()
 					}}
 				/>
+				{ if let Some(hint) = &state.hint {
+					constructor! {
+						<TextWidgetBundle
+							styles={KStyle {
+								top: px(20.0),
+								bottom: px(20.0),
+								left: stretch(1.0),
+								right: stretch(1.0),
+								color: value(Color::BLACK),
+								..Default::default()
+							}}
+							text={TextProps {
+								size: 28.0,
+								content: format!("\"{}\"", &hint),
+								..Default::default()
+							}}
+						/>
+					}
+				}}
+
+
 				<ScrollContextProviderBundle>
 					<ScrollBoxBundle>
+			{ if state.hint.is_none() {
+				constructor! {
 						<TextWidgetBundle
 							styles={KStyle {
 								top: px(20.0),
@@ -59,8 +129,30 @@ pub fn render_tavern_panel(
 								..Default::default()
 							}}
 						/>
+				}
+			}}
 					</ScrollBoxBundle>
 				</ScrollContextProviderBundle>
+
+				{if !ui_data.is_starved {
+					constructor! {
+						<ButtonWidget
+							props={ButtonWidgetProps {
+								font_size: 24.0,
+								left_icon: IconContent::Atlas(String::from("icons"), 8),
+								text: String::from("50g: Tip Barkeep"),
+								is_disabled: ui_data.player_gold < 50,
+								..Default::default()
+							}}
+							styles={KStyle {
+								left: stretch(1.0),
+								right: stretch(1.0),
+								..Default::default()
+							}}
+							on_event={buy_hint}
+						/>
+					}
+				}}
 			</ElementBundle>
 		};
 	}
diff --git a/game_core/src/world/encounters.rs b/game_core/src/world/encounters.rs
index 92ddbf9e154f40411833e76993adc7cee1e28242..66ac733d46d6353e5fba62cbe28ec76d2e46d1ab 100644
--- a/game_core/src/world/encounters.rs
+++ b/game_core/src/world/encounters.rs
@@ -28,8 +28,10 @@ impl EncounterType {
 				title: String::from("Your journey begins"),
 				description: String::from(include_str!("../assets/static/intro_text.txt")),
 				options: vec![EncounterOption {
+					requirements: Vec::new(),
 					label: String::from("Journey Forth"),
-					outcome: vec![],
+					outcomes: vec![],
+					hide_missing_reqs: true,
 				}],
 			}]),
 			_ => None,
@@ -132,12 +134,19 @@ impl From<Vec<EncounterZone>> for WorldZones {
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(tag = "type")]
 pub enum EncounterOutcome {
+	Continue,
+	Description {
+		description: String,
+	},
 	GainResource {
+		description: String,
 		resource_type: String,
 		amount: (usize, usize),
 	},
 	LoseResource {
+		description: String,
 		resource_type: String,
 		amount: (usize, usize),
 	},
@@ -150,10 +159,35 @@ pub enum EncounterOutcome {
 	},
 }
 
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct ChanceOutcome {
+	pub chance: usize,
+	pub outcome: EncounterOutcome,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(tag = "type")]
+pub enum EncounterRequirement {
+	MinimumItemAmount { item: String, amount: usize },
+	MaximumItemAmount { item: String, amount: usize },
+	MinimumGoldAmount { item: String, amount: usize },
+	MaximumGoldAmount { item: String, amount: usize },
+	AnyFoodLessThan { amount: usize, or_equal: bool },
+	AnyFoodGreaterThan { amount: usize, or_equal: bool },
+}
+
+fn default_false() -> bool {
+	false
+}
+
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct EncounterOption {
 	pub label: String,
-	pub outcome: Vec<EncounterOutcome>,
+	pub outcomes: Vec<EncounterOutcome>,
+	#[serde(default = "Vec::new")]
+	pub requirements: Vec<EncounterRequirement>,
+	#[serde(default = "default_false")]
+	pub hide_missing_reqs: bool,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -170,7 +204,8 @@ pub fn gen_encounter() -> Encounter {
 		options: vec![
 			EncounterOption {
 				label: BsVerb().fake(),
-				outcome: vec![EncounterOutcome::GainResource {
+				outcomes: vec![EncounterOutcome::GainResource {
+					description: Paragraph(3..5).fake(),
 					resource_type: format!(
 						"{} {}",
 						BsAdj().fake::<String>(),
@@ -182,10 +217,13 @@ pub fn gen_encounter() -> Encounter {
 						(b, b + inc)
 					},
 				}],
+				requirements: Vec::new(),
+				hide_missing_reqs: true,
 			},
 			EncounterOption {
 				label: BsVerb().fake(),
-				outcome: vec![EncounterOutcome::LoseResource {
+				outcomes: vec![EncounterOutcome::LoseResource {
+					description: Paragraph(3..5).fake(),
 					resource_type: format!(
 						"{} {}",
 						BsAdj().fake::<String>(),
@@ -197,6 +235,8 @@ pub fn gen_encounter() -> Encounter {
 						(b, b + inc)
 					},
 				}],
+				requirements: Vec::new(),
+				hide_missing_reqs: true,
 			},
 		],
 	}
diff --git a/game_core/src/world/hunger.rs b/game_core/src/world/hunger.rs
index 67fd48b6a58a368ef61caa3bc7cdcba797ccbc12..ea6ca40d117374e64139f2fbb8394a935597d726 100644
--- a/game_core/src/world/hunger.rs
+++ b/game_core/src/world/hunger.rs
@@ -1,10 +1,12 @@
 use std::collections::HashMap;
 
-use bevy::prelude::{Component, Entity, EventReader, Mut, Query, With, Without};
+use bevy::prelude::{Commands, Component, Entity, EventReader, Mut, Query, Res, With, Without};
 use serde::{Deserialize, Serialize};
 
+use crate::assets::AssetHandles;
 use crate::const_data::get_goods_from_name_checked;
 use crate::states::Player;
+use crate::world::spawning::apply_skull_marker;
 use crate::world::travel::WorldTickEvent;
 use crate::world::{TownName, TradeGood, TradingState};
 
@@ -46,6 +48,7 @@ impl HungerState {
 	}
 }
 
+#[derive(Component)]
 pub struct StarvationMarker;
 
 pub const PLAYER_FOOD_TARGET: usize = 25;
@@ -144,9 +147,20 @@ fn process_hunger_state<'a>(
 	}
 }
 
+#[derive(Default)]
+pub struct PlayerStarvedEvent;
+
 pub fn handle_entity_starvation(
-	food_query: Query<(Entity, &HungerState)>,
+	mut commands: Commands,
+	assets: Res<AssetHandles>,
+	food_query: Query<(Entity, &HungerState), Without<StarvationMarker>>,
 	player_query: Query<(), With<Player>>,
 	town_query: Query<&TownName>,
 ) {
+	for (entity, hunger) in &food_query {
+		if hunger.sustenance == 0 {
+			apply_skull_marker(&mut commands, &assets, entity);
+			commands.entity(entity).insert(StarvationMarker);
+		}
+	}
 }
diff --git a/game_core/src/world/mod.rs b/game_core/src/world/mod.rs
index 7d2d877ebd7218c1b61825029a8ca1b991b53e28..23131befa50b65ee9efb40bfeda2e3b8cf6b2da4 100644
--- a/game_core/src/world/mod.rs
+++ b/game_core/src/world/mod.rs
@@ -90,6 +90,7 @@ impl Plugin for WorldPlugin {
 				ConditionSet::new()
 					.run_in_state(AppState::InGame)
 					.with_system(encounters::notify_new_zone)
+					.with_system(hunger::handle_entity_starvation)
 					.with_system(|mut events: ResMut<Events<WorldTickEvent>>| {
 						events.clear();
 					})
diff --git a/game_core/src/world/spawning.rs b/game_core/src/world/spawning.rs
index 016b38ba63c7382df467fe4c729342cf6d4b06e5..1625ab15de4635a3e2694e79a3eda5654608ba29 100644
--- a/game_core/src/world/spawning.rs
+++ b/game_core/src/world/spawning.rs
@@ -293,6 +293,9 @@ pub fn populate_world(
 		}
 
 		let world_zones = WorldZones::from_entities(level.px_hei, MapQuery::get_entities_of(level));
+		let mut town_entities_a = Vec::new();
+		let mut town_entities_b = Vec::new();
+
 		MapQuery::get_entities_of(level)
 			.into_iter()
 			.filter(|instance| instance.identifier == *"Town")
@@ -331,39 +334,70 @@ pub fn populate_world(
 					.as_ref()
 					.and_then(|pl| pl.0.town_states.get(&name))
 				{
-					Some((trade, hunger, tick)) => TownBundle {
-						town_name: TownName(name.clone()),
-						manifest: handle,
-						trade_state: trade.clone(),
-						hunger_state: hunger.clone(),
-						manifest_state: tick.clone(),
-						world_linked: WorldLinked,
-						location: TransformBundle::from_transform(Transform::from_translation(
-							transform,
-						)),
-					},
-					None => TownBundle {
-						town_name: TownName(name.clone()),
-						manifest: handle,
-						trade_state: TradingState {
-							gold: fastrand::isize(0..250) + 250,
-							items: asset.create_randomised_inventory(),
+					Some((trade, hunger, tick, maybe_spec)) => (
+						maybe_spec.clone(),
+						TownBundle {
+							town_name: TownName(name.clone()),
+							manifest: handle,
+							trade_state: trade.clone(),
+							hunger_state: hunger.clone(),
+							manifest_state: tick.clone(),
+							world_linked: WorldLinked,
+							location: TransformBundle::from_transform(Transform::from_translation(
+								transform,
+							)),
 						},
-						hunger_state: HungerState::initial_town(),
-						manifest_state: TradeManifestTickState::default(),
-						world_linked: WorldLinked,
-						location: TransformBundle::from_transform(Transform::from_translation(
-							transform,
-						)),
-					},
+					),
+					None => (
+						None,
+						TownBundle {
+							town_name: TownName(name.clone()),
+							manifest: handle,
+							trade_state: TradingState {
+								gold: fastrand::isize(0..250) + 250,
+								items: asset.create_randomised_inventory(),
+							},
+							hunger_state: HungerState::initial_town(),
+							manifest_state: TradeManifestTickState::default(),
+							world_linked: WorldLinked,
+							location: TransformBundle::from_transform(Transform::from_translation(
+								transform,
+							)),
+						},
+					),
 				}
 			})
-			.for_each(|bundle| {
+			.for_each(|(maybe_spec, bundle)| {
+				let is_royal_lampoon = &bundle.town_name.0 == &String::from("The Royal Lampoon");
 				let ent = commands.spawn((bundle, VisibilityBundle::default())).id();
-				apply_skull_marker(&mut commands, &assets, ent);
+				if let Some(spec) = maybe_spec {
+					commands.entity(ent).insert(spec);
+				} else if is_royal_lampoon {
+					town_entities_a.push(ent);
+				}
 			});
 
-		log::info!("Spec {:#?}", *SPECIALISMS);
+		if pending_load.is_none() {
+			let mut specialisms = (*SPECIALISMS).clone();
+			let mut handling_a = true;
+			for spec in specialisms {
+				if town_entities_a.is_empty() && !town_entities_b.is_empty() {
+					handling_a = false;
+				} else if town_entities_b.is_empty() && !town_entities_a.is_empty() {
+					handling_a = true;
+				}
+
+				if handling_a {
+					let entity = town_entities_a.remove(fastrand::usize(0..town_entities_a.len()));
+					commands.entity(entity).insert(spec);
+					town_entities_b.push(entity);
+				} else {
+					let entity = town_entities_b.remove(fastrand::usize(0..town_entities_b.len()));
+					commands.entity(entity).insert(spec);
+					town_entities_a.push(entity);
+				}
+			}
+		}
 
 		commands.insert_resource(trade_routes);
 		commands.insert_resource(world_zones);
diff --git a/game_core/src/world/trading.rs b/game_core/src/world/trading.rs
index 21cff5fa104dc72ab8bf3642dca1ec6ca439c42a..643fab9c6850c83b0724d77ac6275d28978f1e6f 100644
--- a/game_core/src/world/trading.rs
+++ b/game_core/src/world/trading.rs
@@ -100,6 +100,9 @@ impl TradeManifest {
 			}
 		}
 
+		inventory
+			.entry(String::from("Bread"))
+			.or_insert_with(|| fastrand::usize(8..15));
 		inventory
 	}
 }
diff --git a/raw_assets/calculator.ods b/raw_assets/calculator.ods
new file mode 100644
index 0000000000000000000000000000000000000000..ac7f2d3c4e2b6649eadb0e5e4e2a11d3035e7223
Binary files /dev/null and b/raw_assets/calculator.ods differ
diff --git a/raw_assets/specialisms.toml b/raw_assets/specialisms.toml
index 1b8c9023fb20e7b4ec446ca58c338cd428a07a47..a4a926b921b5cc6f00a970c648a91c74be4366a8 100644
--- a/raw_assets/specialisms.toml
+++ b/raw_assets/specialisms.toml
@@ -1,23 +1,29 @@
 [[specialism]]
 [specialism.requirements]
-Tomato = 3
-Cheese = 2
 Dough = 2
+Cheese = 2
+Tomato = 3
 [specialism.production]
 Pizza = 2
 
 [[specialism]]
 [specialism.requirements]
 Coal = 4
-"Iron Ore" = 3
+"Iron Ore" = 5
+[specialism.production]
+"Iron Ingot" = 5
+
+[[specialism]]
+[specialism.requirements]
+"Rough Gemstone" = 5
 [specialism.production]
-"Iron Ingot" = 3
+"Polished Gemstone" = 5
 
 [[specialism]]
 [specialism.requirements]
 Lumber = 10
 [specialism.production]
-"Spare Wheel" = 3
+"Spare Wheel" = 2
 
 [[specialism]]
 [specialism.requirements]
@@ -36,3 +42,9 @@ Dough = 3
 Dough = 4
 [specialism.production]
 Bread = 3
+
+[[specialism]]
+[specialism.requirements]
+Grapes = 4
+[specialism.production]
+Wine = 6
diff --git a/raw_assets/trade_goods.toml b/raw_assets/trade_goods.toml
index a92a5d5485603c743570b002db62b7ec6cfde9fa..f7ad6a676fd43b6d5c9a6c94b382ca9124437fce 100644
--- a/raw_assets/trade_goods.toml
+++ b/raw_assets/trade_goods.toml
@@ -1,10 +1,10 @@
-["Armour"]
+[Armour]
 name = "Armour"
 icon = ["icons", 12]
 food_value = 0
 gold_value = 12
 
-["Weapons"]
+[Weapons]
 name = "Weapons"
 icon = ["icons", 1]
 food_value = 0
@@ -16,23 +16,23 @@ icon = ["icons", 13]
 food_value = 0
 gold_value = 27
 
-["Ale"]
+[Ale]
 name = "Ale"
 icon = ["icons", 11]
 food_value = 0
 gold_value = 6
 
-["Milk"]
+[Milk]
 name = "Milk"
 icon = ["icons", 2]
-food_value = 2
-gold_value = 5
+food_value = 3
+gold_value = 6
 
-["Cheese"]
+[Cheese]
 name = "Cheese"
 icon = ["icons", 3]
-food_value = 3
-gold_value = 6
+food_value = 4
+gold_value = 9
 
 ["Spare Wheel"]
 name = "Spare Wheel"
@@ -40,38 +40,113 @@ icon = ["icons", 28]
 food_value = 0
 gold_value = 125
 
-["Berries"]
+[Berries]
 name = "Berries"
 icon = ["icons", 27]
-food_value = 1
+food_value = 2
 gold_value = 4
 
-["Corn"]
+[Corn]
 name = "Corn"
 icon = ["icons", 26]
 food_value = 2
-gold_value = 6
+gold_value = 4
 
-["Tomato"]
+[Tomato]
 name = "Tomato"
 icon = ["icons", 25]
-food_value = 2
+food_value = 3
 gold_value = 6
 
-["Chili"]
+[Chili]
 name = "Chili"
 icon = ["icons", 24]
-food_value = 2
+food_value = 4
 gold_value = 8
 
-["Grapes"]
+[Grapes]
 name = "Grapes"
 icon = ["icons", 23]
-food_value = 1
-gold_value = 9
+food_value = 4
+gold_value = 8
 
-["Wheat"]
+[Wheat]
 name = "Wheat"
 icon = ["icons", 22]
-food_value = 1
-gold_value = 3
+food_value = 2
+gold_value = 4
+
+[Dough]
+name = "Dough"
+icon = ["icons", 38]
+food_value = 0
+gold_value = 9
+
+[Bread]
+name = "Bread"
+icon = ["icons", 35]
+food_value = 9
+gold_value = 14
+
+[Pizza]
+name = "Pizza"
+icon = ["icons", 36]
+food_value = 18
+gold_value = 36
+
+[Lumber]
+name = "Lumber"
+icon = ["icons", 29]
+food_value = 0
+gold_value = 10
+
+[Coal]
+name = "Coal"
+icon = ["icons", 30]
+food_value = 0
+gold_value = 13
+
+["Iron Ore"]
+name = "Iron Ore"
+icon = ["icons", 31]
+food_value = 0
+gold_value = 8
+
+[Iron]
+name = "Iron"
+icon = ["icons", 32]
+food_value = 0
+gold_value = 33
+
+["Rough Gemstone"]
+name = "Rough Gemstone"
+icon = ["icons", 33]
+food_value = 0
+gold_value = 12
+
+["Polished Gemstone"]
+name = "Polished Gemstone"
+icon = ["icons", 34]
+food_value = 0
+gold_value = 19
+
+
+["Religious Artifact"]
+name = "Religious Artifact"
+icon = ["icons", 34]
+food_value = 0
+gold_value = 125
+
+
+["Strange Doll"]
+name = "Polished Gemstone"
+icon = ["icons", 34]
+food_value = 0
+gold_value = 19
+
+["Wine"]
+name = "Wine"
+icon = ["icons", 37]
+food_value = 7
+gold_value = 12
+