diff --git a/assets/encounters/grasslands.encounters.toml b/assets/encounters/grasslands.encounters.toml
index 3f51b782ea06629e9ea2c9104de1c2d30d299bc6..4630ab0598e4c9024987872c35900f980a6783af 100644
--- a/assets/encounters/grasslands.encounters.toml
+++ b/assets/encounters/grasslands.encounters.toml
@@ -1,16 +1,124 @@
 [[encounters]]
 chance = 90
-title = "You hear a rustle in the bushes"
+title = "A rustle in the bushes"
 description = """
+While taking a rest along the road, you hear a suspicious rustle in the nearby bushes. All sorts of creatures and cretins live in the wilds; do you with to investigate?
+It will take some time to do so
+"""
+[[encounters.options]]
+label = "Investigate"
+outcomes = [
+	{ type = "Description", text = """You carefully poke through the bushes, keeping a close eye on your footing. Eventually, you enter a small clearing where some creatures must have been storing stolen food.
+You fill one of your bags before making your getaway.""" },
+	{ type = "GainResource", resource_type = "Corn", amount = [25, 35] }
+#	,{ type = "TickWorld", amount = 1 }
+
+]
+[[encounters.options]]
+label = "Ignore And Move On"
+outcomes = [
+	{ type = "Continue" }
+]
+
+[[encounters]]
+chance = 90
+title = "A rustle in the bushes"
+description = """
+While taking a rest along the road, you hear a suspicious rustle in the nearby bushes. All sorts of creatures and cretins live in the wilds; do you with to investigate?
+It will take some time to do so
+"""
+[[encounters.options]]
+label = "Investigate"
+outcomes = [
+	{ type = "Description", text = """You carefully poke through the bushes, keeping a close eye on your footing. Eventually, you enter a small clearing where some creatures must have been storing stolen food.
+You fill one of your bags before making your getaway.""" },
+	{ type = "GainResource", resource_type = "Wheat", amount = [25, 35] }
+#	,{ type = "TickWorld", amount = 1 }
+
+]
+[[encounters.options]]
+label = "Ignore And Move On"
+outcomes = [
+	{ type = "Continue" }
+]
+
+[[encounters]]
+chance = 90
+title = "A rustle in the bushes"
+description = """
+While taking a rest along the road, you hear a suspicious rustle in the nearby bushes. All sorts of creatures and cretins live in the wilds; do you with to investigate?
+It will take some time to do so
+"""
+[[encounters.options]]
+label = "Investigate"
+outcomes = [
+	{ type = "Description", text = """You carefully poke through the bushes, keeping a close eye on your footing. Eventually, you enter a small clearing where some creatures must have been storing stolen food.
+You fill one of your bags before making your getaway.""" },
+	{ type = "GainResource", resource_type = "Berries", amount = [25, 35] }
+#	,{ type = "TickWorld", amount = 1 }
+]
+[[encounters.options]]
+label = "Ignore And Move On"
+outcomes = [
+	{ type = "Continue" }
+]
 
+[[encounters]]
+chance = 90
+title = "A rustle in the bushes"
+description = """
+While taking a rest along the road, you hear a suspicious rustle in the nearby bushes. All sorts of creatures and cretins live in the wilds; do you with to investigate?
+It will take some time to do so
 """
+[[encounters.options]]
+label = "Investigate"
+outcomes = [
+	{ type = "Description", text = """You carefully poke through the bushes, keeping a close eye on your footing. Despite your searching, you can't seem to find the source.
+When you return to your caravan, you discover that some of your supplies have been pilfered!""" },
+	{ type = "LoseGold", amount = [45, 90] }
+#	,{ type = "TickWorld", amount = 1 }
+
+]
+[[encounters.options]]
+label = "Ignore And Move On"
+outcomes = [
+	{ type = "Continue" }
+]
 
 [[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
+[[encounters.options]]
+label = "Leave 100 Gold"
+hide_missing_reqs = true
+outcomes = [
+	{ type = "Description", text = "After depositing the gold at the shrine, a quiet thud sound catches your attention. Looking around for the source of the noise, you notice a small golden statue nestled within some shrubs. You pick it up and place it in your pack." },
+	{ type = "GainResource", resource_type = "Religious Artifact", amount = 1 },
+	{ type = "LoseGold", amount = 100 }
+]
+requirements = [
+	{ type = "MinimumGoldAmount", amount = 100 },
+]
+
+[[encounters.options]]
+label = "Leave a Gold Piece"
+hide_missing_reqs = false
+outcomes = [
+	{ type = "Description", text = "You walk up to the shrine and leave a single gold piece. As you walk away, you feel a warmth inside." },
+	{ type = "GainSustenance", amount = [2, 4] },
+	{ type = "LoseGold", amount = 1 }
+]
+requirements = [
+	{ type = "MinimumGoldAmount", amount = 1 },
+]
+
+[[encounters.options]]
+label = "Ignore The Shrine"
+outcomes = [
+	{ type = "Continue" }
+]
+requirements = []
diff --git a/assets/resources.apack b/assets/resources.apack
index 3ad723412d4cdd5561d095a77ba3bb17e1554d75..e6ebb1425b826aa9b947ff3fbe6264e2c440b47f 100644
--- a/assets/resources.apack
+++ b/assets/resources.apack
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:08fbd17915eea98e0e3e077c32249781bb0b33d821aeab9c9dac5196eaab6527
-size 1955812
+oid sha256:6451023934a9b45a940ff0ec5f5a75bedcbaa34b66f349514ee3136f080355ce
+size 1955948
diff --git a/assets/trade_manifests/monastery.manifest.toml b/assets/trade_manifests/monastery.manifest.toml
index 88bc5033b02cde1a5f7d4e63bf2d3f91aec36cd0..d412d6073533e6ecb8b00accc8339726a2fbad05 100644
--- a/assets/trade_manifests/monastery.manifest.toml
+++ b/assets/trade_manifests/monastery.manifest.toml
@@ -151,6 +151,7 @@ cost_multipliers = [
 	[99999, 1]
 ]
 oversupply_trend = "halt"
+undersupply_trend = "halt"
 
 [Wheat]
 tick_decay = 1.5
@@ -161,4 +162,18 @@ cost_multipliers = [
 	[30, 1.1],
 	[99999, 1]
 ]
-oversupply_trend = "halt"
\ No newline at end of file
+oversupply_trend = "halt"
+
+["Religious Artifact"]
+tick_decay = 0.0
+natural_limit = 0
+oversupply_trend = "halt"
+undersupply_trend = "halt"
+cost_multipliers = [
+	[5, 2],
+	[10, 1.75],
+	[20, 1.5],
+	[30, 1.25],
+	[100, 1],
+	[99999, 0.75],
+]
\ No newline at end of file
diff --git a/game_core/src/assets/asset_types/encounter_set.rs b/game_core/src/assets/asset_types/encounter_set.rs
new file mode 100644
index 0000000000000000000000000000000000000000..cebbd1b52a9441a575688cd5ba0953fedcce5778
--- /dev/null
+++ b/game_core/src/assets/asset_types/encounter_set.rs
@@ -0,0 +1,58 @@
+use anyhow::Error;
+use bevy::asset::{AssetLoader, BoxedFuture, LoadContext, LoadedAsset};
+use bevy::reflect::TypeUuid;
+use serde::{Deserialize, Serialize};
+
+use crate::world::Encounter;
+
+pub struct EncounterSetJsonLoader;
+pub struct EncounterSetTomlLoader;
+
+#[derive(Debug, Default, Clone, TypeUuid, Serialize, Deserialize)]
+#[uuid = "87953518-7969-11ed-9ec9-d7c0b4b5e357"]
+pub struct EncounterSet {
+	pub encounters: Vec<Encounter>,
+}
+
+impl EncounterSet {
+	pub fn select(&self) -> Encounter {
+		let val = fastrand::usize(0..self.encounters.len());
+		self.encounters[val].clone()
+	}
+}
+
+impl AssetLoader for EncounterSetJsonLoader {
+	fn load<'a>(
+		&'a self,
+		bytes: &'a [u8],
+		load_context: &'a mut LoadContext,
+	) -> BoxedFuture<'a, anyhow::Result<(), Error>> {
+		Box::pin(async move {
+			let manifest: EncounterSet = serde_json::from_slice(bytes)?;
+			load_context.set_default_asset(LoadedAsset::new(manifest));
+			Ok(())
+		})
+	}
+
+	fn extensions(&self) -> &[&str] {
+		&["encounters.json"]
+	}
+}
+
+impl AssetLoader for EncounterSetTomlLoader {
+	fn load<'a>(
+		&'a self,
+		bytes: &'a [u8],
+		load_context: &'a mut LoadContext,
+	) -> BoxedFuture<'a, anyhow::Result<(), Error>> {
+		Box::pin(async move {
+			let manifest: EncounterSet = toml::from_slice(bytes)?;
+			load_context.set_default_asset(LoadedAsset::new(manifest));
+			Ok(())
+		})
+	}
+
+	fn extensions(&self) -> &[&str] {
+		&["encounters.toml"]
+	}
+}
diff --git a/game_core/src/assets/asset_types/mod.rs b/game_core/src/assets/asset_types/mod.rs
index ecb2c415973c934243541dcee86696b6eaaa967d..dd19a0f1bc3b13a382761770d01e5c1458ab100a 100644
--- a/game_core/src/assets/asset_types/mod.rs
+++ b/game_core/src/assets/asset_types/mod.rs
@@ -1,2 +1,3 @@
+pub mod encounter_set;
 pub mod ldtk_project;
 pub mod trade_manifest;
diff --git a/game_core/src/assets/loader.rs b/game_core/src/assets/loader.rs
index 01abd6336892755a98e4896a4ea57341332fbc9a..2c3ab81fb097cfb0c7efa30c00973b5f7195c44a 100644
--- a/game_core/src/assets/loader.rs
+++ b/game_core/src/assets/loader.rs
@@ -8,6 +8,7 @@ use kayak_font::KayakFont;
 use micro_asset_io::APack;
 use micro_musicbox::prelude::AudioSource;
 
+use crate::assets::asset_types::encounter_set::EncounterSet;
 use crate::assets::asset_types::ldtk_project::LdtkProject;
 use crate::assets::{AssetHandles, FixedAssetNameMapping, SpriteSheetConfig};
 use crate::world::TradeManifest;
@@ -64,6 +65,7 @@ impl<'w, 's> AssetTypeLoader<'w, 's> {
 	load_basic_type!(load_ldtk, LdtkProject => ldtk);
 	load_basic_type!(load_kayak_font, KayakFont => kayak_fonts);
 	load_basic_type!(load_trade_manifest, TradeManifest => trade_manifests);
+	load_basic_type!(load_encounter_set, EncounterSet => encounter_sets);
 
 	pub fn load_spritesheet(
 		&mut self,
diff --git a/game_core/src/assets/mod.rs b/game_core/src/assets/mod.rs
index bd42743e9bf00651ded6a64ae54284c076f4e670..3a6933b60320a1f83657c8cc0cad99c3e7159d4f 100644
--- a/game_core/src/assets/mod.rs
+++ b/game_core/src/assets/mod.rs
@@ -4,6 +4,7 @@ mod loader;
 mod resources;
 mod startup;
 
+pub use asset_types::encounter_set::EncounterSet;
 pub use asset_types::ldtk_project::{LdtkLoader, LdtkProject, LevelIndex, TilesetIndex};
 use bevy::app::{App, Plugin};
 use bevy::prelude::AddAsset;
@@ -13,6 +14,7 @@ pub use loader::AssetTypeLoader;
 pub use resources::{AssetHandles, AssetNameMapping, FixedAssetNameMapping, SpriteSheetConfig};
 
 use crate::assets::apack_handler::handle_apack_process_events;
+use crate::assets::asset_types::encounter_set::{EncounterSetJsonLoader, EncounterSetTomlLoader};
 use crate::assets::asset_types::ldtk_project::handle_ldtk_project_events;
 use crate::assets::asset_types::trade_manifest::{
 	TradeManifestJsonLoader, TradeManifestTomlLoader,
@@ -28,9 +30,12 @@ impl Plugin for AssetsPlugin {
 			.init_resource::<TilesetIndex>()
 			.add_asset::<LdtkProject>()
 			.add_asset::<TradeManifest>()
+			.add_asset::<EncounterSet>()
 			.add_asset_loader(LdtkLoader)
 			.add_asset_loader(TradeManifestJsonLoader)
 			.add_asset_loader(TradeManifestTomlLoader)
+			.add_asset_loader(EncounterSetJsonLoader)
+			.add_asset_loader(EncounterSetTomlLoader)
 			.add_enter_system(AppState::Preload, startup::start_preload_resources)
 			.add_enter_system(AppState::Preload, startup::start_load_resources)
 			.add_system(handle_apack_process_events)
diff --git a/game_core/src/assets/resources.rs b/game_core/src/assets/resources.rs
index ba4ed40f931b47a75f7e3cab1c53e4e7e4f3b35b..e4a87b2cac912b8f6906feed9e63cd151f54c638 100644
--- a/game_core/src/assets/resources.rs
+++ b/game_core/src/assets/resources.rs
@@ -5,6 +5,7 @@ use micro_asset_io::APack;
 use micro_musicbox::prelude::AudioSource;
 use micro_musicbox::utilities::{SuppliesAudio, TrackType};
 
+use crate::assets::asset_types::encounter_set::EncounterSet;
 use crate::assets::asset_types::ldtk_project::LdtkProject;
 use crate::world::TradeManifest;
 
@@ -46,6 +47,7 @@ pub struct AssetHandles {
 	pub ldtk: HashMap<String, Handle<LdtkProject>>,
 	pub kayak_fonts: HashMap<String, Handle<KayakFont>>,
 	pub trade_manifests: HashMap<String, Handle<TradeManifest>>,
+	pub encounter_sets: HashMap<String, Handle<EncounterSet>>,
 }
 
 macro_rules! fetch_wrapper {
@@ -79,6 +81,7 @@ impl AssetHandles {
 	fetch_wrapper!(ldtk, LdtkProject => ldtk);
 	fetch_wrapper!(kayak_font, KayakFont => kayak_fonts);
 	fetch_wrapper!(trade_manifest, TradeManifest => trade_manifests);
+	fetch_wrapper!(encounter_set, EncounterSet => encounter_sets);
 }
 
 impl SuppliesAudio for AssetHandles {
diff --git a/game_core/src/assets/startup.rs b/game_core/src/assets/startup.rs
index 74d4a5bcdc7813fa7dc8343a20cce5a9ac15fbfe..69c4b20e8ea77029d17d684792df4b4a33255046 100644
--- a/game_core/src/assets/startup.rs
+++ b/game_core/src/assets/startup.rs
@@ -20,6 +20,7 @@ pub fn start_load_resources(mut loader: AssetTypeLoader) {
 		("trade_manifests/monastery.manifest.toml", "monastery"),
 		("trade_manifests/ship.manifest.toml", "ship"),
 	]);
+	loader.load_encounter_set(&[("encounters/grasslands.encounters.toml", "grasslands")]);
 }
 
 pub fn check_load_resources(mut commands: Commands, loader: AssetTypeLoader) {
diff --git a/game_core/src/ui/widgets/encounter_panel.rs b/game_core/src/ui/widgets/encounter_panel.rs
index c0583bf423419324a064ee45e9c9e6d6bfe14422..8d6a38d4ef6b28486e26e81c128b05dc7571bf0f 100644
--- a/game_core/src/ui/widgets/encounter_panel.rs
+++ b/game_core/src/ui/widgets/encounter_panel.rs
@@ -8,7 +8,7 @@ use kayak_ui::widgets::{
 use crate::ui::components::*;
 use crate::ui::prelude::*;
 use crate::ui::widgets::*;
-use crate::world::EncounterState;
+use crate::world::{EncounterOption, EncounterOutcome, EncounterState, SelectEncounterOptionEvent};
 use crate::{basic_widget, empty_props, on_button_click};
 
 empty_props!(EncounterPanelProps);
@@ -47,113 +47,129 @@ pub fn render_encounter_panel(
 		..Default::default()
 	};
 
-	match &*ui_data {
-		EncounterState::Choice(encounter) => {
-			rsx! {
-				<ElementBundle styles={panel_style}>
-					<PanelWidget>
-						<BackgroundBundle
-							styles={KStyle {
+	let encounter_data = match &*ui_data {
+		EncounterState::NoEncounter => None,
+		EncounterState::Choice(encounter) => Some((
+			encounter.title.clone(),
+			encounter.description.clone(),
+			encounter.options.clone(),
+		)),
+		EncounterState::Consequence(encounter, choice) => Some((
+			encounter.title.clone(),
+			choice.describe(),
+			vec![EncounterOption {
+				label: String::from("Continue"),
+				requirements: vec![],
+				hide_missing_reqs: false,
+				outcomes: vec![EncounterOutcome::Continue],
+			}],
+		)),
+	};
+
+	if let Some((title, description, options)) = encounter_data {
+		rsx! {
+			<ElementBundle styles={panel_style}>
+				<PanelWidget>
+					<BackgroundBundle
+						styles={KStyle {
 
-								height: value(Units::Auto),
+							height: value(Units::Auto),
+							color: value(Color::BLACK),
+							padding_left: px(20.0),
+							padding_right: px(20.0),
+							padding_top: px(10.0),
+							padding_bottom: px(10.0),
+							..Default::default()
+						}}
+					>
+						<TextWidgetBundle
+							text={TextProps {
+								font: Some(String::from("header")),
+								content: format!("Trepidation! {}", title),
+								size: 36.0,
+								..Default::default()
+							}}
+							styles={KStyle {
 								color: value(Color::BLACK),
-								padding_left: px(20.0),
-								padding_right: px(20.0),
-								padding_top: px(10.0),
-								padding_bottom: px(10.0),
+								left: stretch(1.0),
+								right: stretch(1.0),
 								..Default::default()
 							}}
-						>
-							<TextWidgetBundle
-								text={TextProps {
-									font: Some(String::from("header")),
-									content: format!("Trepidation! {}", encounter.title),
-									size: 36.0,
-									..Default::default()
-								}}
-								styles={KStyle {
-									color: value(Color::BLACK),
-									left: stretch(1.0),
-									right: stretch(1.0),
-									..Default::default()
-								}}
-							/>
-						</BackgroundBundle>
-						<VDividerWidget props={VDividerWidgetProps { height: 4.0, padding: 10.0, color: Color::rgb(0.52, 0.369, 0.18)}} />
-						<ScrollContextProviderBundle>
-							<ScrollBoxBundle
-								scroll_box_props={ScrollBoxProps {
-									track_color: Some(Color::rgb(0.827, 0.482, 0.353)),
-									thumb_color: Some(Color::rgb(0.451, 0.224, 0.063)),
-									..Default::default()
-								} } styles={KStyle { padding: edge_px(5.0), ..Default::default() }}>
-									{ for line in encounter.description.lines() {
-										constructor! {
-											<BackgroundBundle
-												styles={KStyle {
+						/>
+					</BackgroundBundle>
+					<VDividerWidget props={VDividerWidgetProps { height: 4.0, padding: 10.0, color: Color::rgb(0.52, 0.369, 0.18)}} />
+					<ScrollContextProviderBundle>
+						<ScrollBoxBundle
+							scroll_box_props={ScrollBoxProps {
+								track_color: Some(Color::rgb(0.827, 0.482, 0.353)),
+								thumb_color: Some(Color::rgb(0.451, 0.224, 0.063)),
+								..Default::default()
+							} } styles={KStyle { padding: edge_px(5.0), ..Default::default() }}>
+								{ for line in description.lines() {
+									constructor! {
+										<BackgroundBundle
+											styles={KStyle {
 
-													pointer_events: value(PointerEvents::None),
-													render_command: value(RenderCommand::Quad),
-													layout_type: value(LayoutType::Row),
-													height: value(Units::Auto),
-													width: pct(90.0),
-													left: stretch(1.0),
-													right: stretch(1.0),
-													bottom: px(15.0),
+												pointer_events: value(PointerEvents::None),
+												render_command: value(RenderCommand::Quad),
+												layout_type: value(LayoutType::Row),
+												height: value(Units::Auto),
+												width: pct(90.0),
+												left: stretch(1.0),
+												right: stretch(1.0),
+												bottom: px(15.0),
+												..Default::default()
+											}}
+										>
+											<TextWidgetBundle
+												text={TextProps {
+													content: line.to_string(),
+													line_height: Some(34.0),
+													size: 32.0,
+													..Default::default()
+												}}
+												styles={KStyle {
+													color: value(Color::BLACK),
 													..Default::default()
 												}}
-											>
-												<TextWidgetBundle
-													text={TextProps {
-														content: line.to_string(),
-														line_height: Some(34.0),
-														size: 32.0,
-														..Default::default()
-													}}
-													styles={KStyle {
-														color: value(Color::BLACK),
-														..Default::default()
-													}}
-												/>
-											</BackgroundBundle>
-										}
-									} }
-							</ScrollBoxBundle>
-						</ScrollContextProviderBundle>
-					</PanelWidget>
+											/>
+										</BackgroundBundle>
+									}
+								} }
+						</ScrollBoxBundle>
+					</ScrollContextProviderBundle>
+				</PanelWidget>
 
-					<ElementBundle styles={KStyle {
-						layout_type: value(LayoutType::Row),
-						padding_top: stretch(1.0),
-						padding_bottom: stretch(1.0),
-						padding_left: stretch(1.0),
-						padding_right: stretch(1.0),
-						height: px(60.0),
-						col_between: px(15.0),
-						..Default::default()
-					}}>
-						{
-							for option in &encounter.options {
-								let label = format!("{}", option.label);
-								let on_click = on_button_click!(ResMut<EncounterState>, |mut state: ResMut<EncounterState>| {
-									*state = EncounterState::NoEncounter;
-								});
+				<ElementBundle styles={KStyle {
+					layout_type: value(LayoutType::Row),
+					padding_top: stretch(1.0),
+					padding_bottom: stretch(1.0),
+					padding_left: stretch(1.0),
+					padding_right: stretch(1.0),
+					height: px(60.0),
+					col_between: px(15.0),
+					..Default::default()
+				}}>
+					{
+						for option in &options {
+							let label = format!("{}", option.label);
+							let owned = option.clone();
+							let on_click = on_button_click!(EventWriter<SelectEncounterOptionEvent>, |mut state: EventWriter<SelectEncounterOptionEvent>| {
+								state.send(SelectEncounterOptionEvent { option: owned.clone() });
+							});
 
-								constructor! {
-									<ButtonWidget
-										styles={KStyle { ..Default::default() }}
-										props={ButtonWidgetProps::text(label, 28.0)}
-										on_event={on_click}
-									/>
-								}
+							constructor! {
+								<ButtonWidget
+									styles={KStyle { ..Default::default() }}
+									props={ButtonWidgetProps::text(label, 28.0)}
+									on_event={on_click}
+								/>
 							}
 						}
-					</ElementBundle>
+					}
 				</ElementBundle>
-			};
-		}
-		EncounterState::Consequence(..) => {}
-		EncounterState::NoEncounter => {}
+			</ElementBundle>
+		};
 	}
 
 	true
diff --git a/game_core/src/ui/widgets/shop_panel.rs b/game_core/src/ui/widgets/shop_panel.rs
index 074d0868a7dc9bb292470ff2c2359029a83a3da5..c0579259ee8ec5c3bf7983e68388b2f9789fc219 100644
--- a/game_core/src/ui/widgets/shop_panel.rs
+++ b/game_core/src/ui/widgets/shop_panel.rs
@@ -145,8 +145,8 @@ pub fn render_shop_panel(
 											}}
 										>
 											<InsetIconWidget props={InsetIconProps { image: icon.clone(), size: 32.0}} />
-											<TextWidgetBundle styles={KStyle { width: stretch(1.5), ..Default::default() }} text={TextProps { content: name.clone(), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
-											<TextWidgetBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} text={TextProps { content: format!("{}g", current_cost), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
+											<TextWidgetBundle styles={KStyle { width: stretch(1.75), ..Default::default() }} text={TextProps { content: name.clone(), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
+											<TextWidgetBundle styles={KStyle { width: stretch(0.6), ..Default::default() }} text={TextProps { content: format!("{}g", current_cost), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
 											<TextWidgetBundle styles={KStyle { width: stretch(0.5), ..Default::default() }} text={TextProps { content: format!("{}", town_amount), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
 											<TextWidgetBundle styles={KStyle { width: stretch(0.5), ..Default::default() }} text={TextProps { content: format!("{}", player_amount), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
 											<ButtonWidget on_event={buysell_button_factory(name.clone(), true)} props={ButtonWidgetProps { font_size: 20.0, is_disabled: ui_data.player_gold < *current_cost || town_amount == &0, text: String::from("Buy"), ..Default::default() }} />
diff --git a/game_core/src/world/encounters.rs b/game_core/src/world/encounters.rs
index 66ac733d46d6353e5fba62cbe28ec76d2e46d1ab..8a3ac8fc5d1cfb82f1c2510138be67eb226f2d72 100644
--- a/game_core/src/world/encounters.rs
+++ b/game_core/src/world/encounters.rs
@@ -8,8 +8,11 @@ use num_traits::AsPrimitive;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 
+use crate::assets::{AssetHandles, EncounterSet};
 use crate::states::Player;
+use crate::world::travel::WorldTickEvent;
 use crate::world::utils::entity_to_worldspace;
+use crate::world::{HungerState, TradingState};
 
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum EncounterType {
@@ -37,6 +40,9 @@ impl EncounterType {
 			_ => None,
 		}
 	}
+	pub fn get_asset_name(&self) -> String {
+		String::from("grasslands")
+	}
 }
 
 impl TryFrom<String> for EncounterType {
@@ -133,22 +139,42 @@ impl From<Vec<EncounterZone>> for WorldZones {
 	}
 }
 
+#[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(untagged)]
+pub enum RewardAmount {
+	Exact(usize),
+	Between(usize, usize),
+}
+
 #[derive(Clone, Debug, Serialize, Deserialize)]
 #[serde(tag = "type")]
 pub enum EncounterOutcome {
 	Continue,
 	Description {
-		description: String,
+		text: String,
 	},
 	GainResource {
-		description: String,
 		resource_type: String,
-		amount: (usize, usize),
+		amount: RewardAmount,
+	},
+	GainSustenance {
+		amount: RewardAmount,
+	},
+	LoseSustenance {
+		amount: RewardAmount,
+	},
+	GainGold {
+		amount: RewardAmount,
+	},
+	LoseGold {
+		amount: RewardAmount,
 	},
 	LoseResource {
-		description: String,
 		resource_type: String,
-		amount: (usize, usize),
+		amount: RewardAmount,
+	},
+	TickWorld {
+		amount: RewardAmount,
 	},
 	Ambush {
 		description: String,
@@ -170,8 +196,8 @@ pub struct ChanceOutcome {
 pub enum EncounterRequirement {
 	MinimumItemAmount { item: String, amount: usize },
 	MaximumItemAmount { item: String, amount: usize },
-	MinimumGoldAmount { item: String, amount: usize },
-	MaximumGoldAmount { item: String, amount: usize },
+	MinimumGoldAmount { amount: usize },
+	MaximumGoldAmount { amount: usize },
 	AnyFoodLessThan { amount: usize, or_equal: bool },
 	AnyFoodGreaterThan { amount: usize, or_equal: bool },
 }
@@ -190,6 +216,44 @@ pub struct EncounterOption {
 	pub hide_missing_reqs: bool,
 }
 
+impl EncounterOption {
+	pub fn describe(&self) -> String {
+		let mut description = Vec::with_capacity(self.outcomes.len());
+		for outcome in &self.outcomes {
+			match outcome {
+				EncounterOutcome::Continue => {}
+				EncounterOutcome::Description { text } => {
+					description.push(text.clone());
+				}
+				EncounterOutcome::GainResource { resource_type, .. } => {
+					description.push(format!("Gained {}", resource_type));
+				}
+				EncounterOutcome::GainSustenance { .. } => {
+					description.push(String::from("Your Hunger Has Decreased"));
+				}
+				EncounterOutcome::LoseSustenance { .. } => {
+					description.push(String::from("Your Hunger Has Decreased"));
+				}
+				EncounterOutcome::GainGold { .. } => {
+					description.push(String::from("Gained Some Gold"));
+				}
+				EncounterOutcome::LoseGold { .. } => {
+					description.push(String::from("Lost Some Gold"));
+				}
+				EncounterOutcome::LoseResource { resource_type, .. } => {
+					description.push(format!("Gained {}", resource_type));
+				}
+				EncounterOutcome::TickWorld { .. } => {
+					description.push(String::from("Some time has passed"))
+				}
+				EncounterOutcome::Ambush { .. } => {}
+			}
+		}
+
+		description.join("\n")
+	}
+}
+
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct Encounter {
 	pub title: String,
@@ -205,7 +269,6 @@ pub fn gen_encounter() -> Encounter {
 			EncounterOption {
 				label: BsVerb().fake(),
 				outcomes: vec![EncounterOutcome::GainResource {
-					description: Paragraph(3..5).fake(),
 					resource_type: format!(
 						"{} {}",
 						BsAdj().fake::<String>(),
@@ -214,7 +277,7 @@ pub fn gen_encounter() -> Encounter {
 					amount: {
 						let b = fastrand::usize(2..5);
 						let inc = fastrand::usize(2..5);
-						(b, b + inc)
+						RewardAmount::Between(b, b + inc)
 					},
 				}],
 				requirements: Vec::new(),
@@ -223,7 +286,6 @@ pub fn gen_encounter() -> Encounter {
 			EncounterOption {
 				label: BsVerb().fake(),
 				outcomes: vec![EncounterOutcome::LoseResource {
-					description: Paragraph(3..5).fake(),
 					resource_type: format!(
 						"{} {}",
 						BsAdj().fake::<String>(),
@@ -232,7 +294,7 @@ pub fn gen_encounter() -> Encounter {
 					amount: {
 						let b = fastrand::usize(2..5);
 						let inc = fastrand::usize(2..5);
-						(b, b + inc)
+						RewardAmount::Between(b, b + inc)
 					},
 				}],
 				requirements: Vec::new(),
@@ -256,11 +318,116 @@ impl EncounterState {
 	}
 }
 
+#[derive(Clone, Debug)]
+pub struct SelectEncounterOptionEvent {
+	pub option: EncounterOption,
+}
+
+pub fn handle_encounter_option_event(
+	mut events: ResMut<Events<SelectEncounterOptionEvent>>,
+	mut time_ticks: EventWriter<WorldTickEvent>,
+	mut player_query: Query<(&mut TradingState, &mut HungerState), With<Player>>,
+	mut encounter_state: ResMut<EncounterState>,
+) {
+	for event in events.drain() {
+		for (mut trading, mut hunger) in &mut player_query {
+			for outcome in &event.option.outcomes {
+				log::info!("Processing outcome: {:?}", outcome);
+
+				match outcome {
+					EncounterOutcome::Continue => {
+						*encounter_state = EncounterState::NoEncounter;
+					}
+					EncounterOutcome::Description { .. } => {}
+					EncounterOutcome::GainResource {
+						resource_type,
+						amount,
+					} => match amount {
+						RewardAmount::Exact(number) => {
+							trading.add_items(resource_type, *number);
+						}
+						RewardAmount::Between(min, max) => {
+							trading.add_items(resource_type, fastrand::usize(*min..*max));
+						}
+					},
+					EncounterOutcome::LoseResource {
+						resource_type,
+						amount,
+					} => match amount {
+						RewardAmount::Exact(number) => {
+							trading.force_remove_items(resource_type, *number);
+						}
+						RewardAmount::Between(min, max) => {
+							trading.force_remove_items(resource_type, fastrand::usize(*min..*max));
+						}
+					},
+					EncounterOutcome::GainSustenance { amount } => match amount {
+						RewardAmount::Exact(number) => {
+							hunger.sustenance += *number;
+						}
+						RewardAmount::Between(min, max) => {
+							hunger.sustenance += fastrand::usize(*min..*max);
+						}
+					},
+					EncounterOutcome::LoseSustenance { amount } => match amount {
+						RewardAmount::Exact(number) => {
+							hunger.sustenance = hunger.sustenance.saturating_sub(*number);
+						}
+						RewardAmount::Between(min, max) => {
+							hunger.sustenance = hunger
+								.sustenance
+								.saturating_sub(fastrand::usize(*min..*max));
+						}
+					},
+					EncounterOutcome::GainGold { amount } => match amount {
+						RewardAmount::Exact(number) => {
+							trading.adjust_gold(*number);
+						}
+						RewardAmount::Between(min, max) => {
+							trading.adjust_gold(fastrand::usize(*min..*max));
+						}
+					},
+					EncounterOutcome::LoseGold { amount } => match amount {
+						RewardAmount::Exact(number) => {
+							trading.adjust_gold(-(*number as isize));
+						}
+						RewardAmount::Between(min, max) => {
+							trading.adjust_gold(-(fastrand::usize(*min..*max) as isize));
+						}
+					},
+					EncounterOutcome::TickWorld { amount } => {
+						// match amount {
+						// RewardAmount::Exact(number) => {
+						// 	for _ in 0..*number {
+						// 		time_ticks.send(WorldTickEvent);
+						// 	}
+						// }
+						// RewardAmount::Between(min, max) => {
+						// 	for _ in 0..fastrand::usize(*min..*max) {
+						// 		time_ticks.send(WorldTickEvent);
+						// 	}
+						// }
+						// }
+					}
+					EncounterOutcome::Ambush { .. } => {}
+				}
+			}
+		}
+
+		if let EncounterState::Choice(encounter) = &*encounter_state {
+			*encounter_state = EncounterState::Consequence(encounter.clone(), event.option.clone());
+		}
+		log::info!("Finished processing");
+	}
+}
+
 pub fn notify_new_zone(
 	zones: Res<WorldZones>,
 	player_query: Query<&Transform, With<Player>>,
 	mut last_zone: Local<Option<EncounterZone>>,
 	mut encounter_state: ResMut<EncounterState>,
+	assets: Res<AssetHandles>,
+	sets: Res<Assets<EncounterSet>>,
 ) {
 	for position in &player_query {
 		if let Some(zone) = zones.get_container(position.translation.xy()) {
@@ -275,9 +442,13 @@ pub fn notify_new_zone(
 								*encounter_state = EncounterState::Choice(encounter);
 							}
 							None => {
+								let asset_name = zone.zone_type.get_asset_name();
+								let handle = assets.encounter_set(asset_name);
+								let set = sets.get(&handle).unwrap();
+
 								log::info!("New Zone: {:?}", zone.zone_type);
 								*last_zone = Some(zone);
-								*encounter_state = EncounterState::Choice(gen_encounter());
+								*encounter_state = EncounterState::Choice(set.select());
 							}
 						}
 					}
@@ -290,9 +461,13 @@ pub fn notify_new_zone(
 						*encounter_state = EncounterState::Choice(encounter);
 					}
 					None => {
+						let asset_name = zone.zone_type.get_asset_name();
+						let handle = assets.encounter_set(asset_name);
+						let set = sets.get(&handle).unwrap();
+
 						log::info!("New Zone: {:?}", zone.zone_type);
 						*last_zone = Some(zone);
-						*encounter_state = EncounterState::Choice(gen_encounter());
+						*encounter_state = EncounterState::Choice(set.select());
 					}
 				},
 			}
diff --git a/game_core/src/world/mod.rs b/game_core/src/world/mod.rs
index 23131befa50b65ee9efb40bfeda2e3b8cf6b2da4..e8fe8f908a5cb382837ee9d4319dc666721e21db 100644
--- a/game_core/src/world/mod.rs
+++ b/game_core/src/world/mod.rs
@@ -16,7 +16,10 @@ mod travel;
 mod utils;
 mod world_query;
 
-pub use encounters::{EncounterState, WorldZones};
+pub use encounters::{
+	Encounter, EncounterOption, EncounterOutcome, EncounterRequirement, EncounterState,
+	EncounterType, SelectEncounterOptionEvent, WorldZones,
+};
 pub use hunger::{HungerState, StarvationMarker};
 pub use spawning::PendingLoadState;
 pub use specialism::CraftSpecialism;
@@ -46,6 +49,7 @@ impl Plugin for WorldPlugin {
 			.init_resource::<EncounterState>()
 			.add_event::<PopulateWorldEvent>()
 			.add_event::<WorldTickEvent>()
+			.add_event::<SelectEncounterOptionEvent>()
 			.add_enter_system(AppState::InGame, |mut commands: Commands| {
 				commands.insert_resource(ActiveLevel {
 					map: String::from("Grantswaith"),
@@ -91,6 +95,7 @@ impl Plugin for WorldPlugin {
 					.run_in_state(AppState::InGame)
 					.with_system(encounters::notify_new_zone)
 					.with_system(hunger::handle_entity_starvation)
+					.with_system(encounters::handle_encounter_option_event)
 					.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 73fa009fe038187eaf61b8481c8a733dfa2301c1..c680d3559ae6006eef6642521eef26904d520424 100644
--- a/game_core/src/world/spawning.rs
+++ b/game_core/src/world/spawning.rs
@@ -368,7 +368,7 @@ pub fn populate_world(
 				}
 			})
 			.for_each(|(maybe_spec, bundle)| {
-				let is_royal_lampoon = &bundle.town_name.0 == &String::from("The Royal Lampoon");
+				let is_royal_lampoon = bundle.town_name.0 == String::from("The Royal Lampoon");
 				let ent = commands.spawn((bundle, VisibilityBundle::default())).id();
 				if let Some(spec) = maybe_spec {
 					commands.entity(ent).insert(spec);
diff --git a/game_core/src/world/trading.rs b/game_core/src/world/trading.rs
index 643fab9c6850c83b0724d77ac6275d28978f1e6f..8d16d76a0befae2dc637a624898b271699475212 100644
--- a/game_core/src/world/trading.rs
+++ b/game_core/src/world/trading.rs
@@ -144,6 +144,7 @@ impl TradingState {
 
 	pub fn adjust_gold(&mut self, adjustment: impl AsPrimitive<isize>) {
 		self.gold += adjustment.as_();
+		self.gold = self.gold.max(0);
 	}
 
 	pub fn remove_items(
@@ -168,6 +169,29 @@ impl TradingState {
 		}
 	}
 
+	pub fn force_remove_items(
+		&mut self,
+		identifier: impl ToString,
+		amount: impl AsPrimitive<usize>,
+	) -> bool {
+		match self.items.entry(identifier.to_string()) {
+			Entry::Occupied(mut e) => {
+				let amount = amount.as_();
+				if e.get() >= &amount {
+					*e.get_mut() -= amount;
+					if e.get() == &0 {
+						e.remove();
+					}
+					true
+				} else {
+					e.remove();
+					true
+				}
+			}
+			Entry::Vacant(_) => false,
+		}
+	}
+
 	pub fn add_items(&mut self, identifier: impl ToString, amount: impl AsPrimitive<usize>) {
 		*self.items.entry(identifier.to_string()).or_insert(0) += amount.as_()
 	}
diff --git a/raw_assets/trade_goods.toml b/raw_assets/trade_goods.toml
index f7ad6a676fd43b6d5c9a6c94b382ca9124437fce..f50a002393bd8958a8a7e04ecc72bd09f7e6927c 100644
--- a/raw_assets/trade_goods.toml
+++ b/raw_assets/trade_goods.toml
@@ -133,14 +133,14 @@ gold_value = 19
 
 ["Religious Artifact"]
 name = "Religious Artifact"
-icon = ["icons", 34]
+icon = ["icons", 40]
 food_value = 0
 gold_value = 125
 
 
 ["Strange Doll"]
 name = "Polished Gemstone"
-icon = ["icons", 34]
+icon = ["icons", 41]
 food_value = 0
 gold_value = 19