diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 2619eea576..39110bb48c 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -153,6 +153,19 @@ pub fn make_potion_bag(quantity: u32) -> Item { bag } +#[must_use] +pub fn make_food_bag(quantity: u32) -> Item { + let mut bag = Item::new_from_asset_expect("common.items.armor.misc.bag.tiny_leather_pouch"); + if let Some(i) = bag.slots_mut().iter_mut().next() { + let mut food = Item::new_from_asset_expect("common.items.food.apple_stick"); + if let Err(e) = food.set_amount(quantity) { + warn!("Failed to set food quantity: {:?}", e); + } + *i = Some(food); + } + bag +} + #[must_use] // We have many species so this function is long // Also we are using default tools for un-specified species so diff --git a/common/src/mounting.rs b/common/src/mounting.rs index 6ffb7307bb..bc6c08ce4c 100644 --- a/common/src/mounting.rs +++ b/common/src/mounting.rs @@ -116,7 +116,7 @@ impl Link for Mounting { .map(|p| p.0.map(|e| e.floor())) .unwrap_or_else(|| terrain.find_space(old_pos).map(|e| e as f32)) + Vec3::new(0.5, 0.5, 0.0); - force_update.insert(rider, comp::ForceUpdate); + let _ = force_update.insert(rider, comp::ForceUpdate); }); } } diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 0286b377f4..8ece949691 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -1874,7 +1874,7 @@ fn resolve_e2e_collision( let diff = diff.normalized(); - *vel_delta += Vec3::from(diff) * force * step_delta * vel.0.xy().try_normalized().map_or(1.0, |dir| diff.dot(-dir).max(0.0)); + *vel_delta += Vec3::from(diff) * force * step_delta * vel.0.xy().try_normalized().map_or(1.0, |dir| diff.dot(-dir).max(0.025)); } *collision_registered = true; diff --git a/server/src/rtsim/entity.rs b/server/src/rtsim/entity.rs index 7c0e29a312..a617991f20 100644 --- a/server/src/rtsim/entity.rs +++ b/server/src/rtsim/entity.rs @@ -1,6 +1,6 @@ use super::*; use common::{ - comp::inventory::{loadout_builder::make_potion_bag, slot::ArmorSlot}, + comp::inventory::{loadout_builder::{make_potion_bag, make_food_bag}, slot::ArmorSlot}, resources::Time, rtsim::{Memory, MemoryItem}, store::Id, @@ -143,7 +143,9 @@ impl Entity { // give potions to traveler humanoids or return loadout as is otherwise match (body, kind) { (comp::Body::Humanoid(_), RtSimEntityKind::Random) => { - |l, _| l.bag(ArmorSlot::Bag1, Some(make_potion_bag(100))) + |l, _| l + .bag(ArmorSlot::Bag1, Some(make_potion_bag(100))) + .bag(ArmorSlot::Bag2, Some(make_food_bag(100))) }, (_, RtSimEntityKind::Merchant) => { |l, trade| l.with_creator(world::site::settlement::merchant_loadout, trade) diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 4d3da59403..300a4faab9 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -176,6 +176,7 @@ const MAX_FLEE_DIST: f32 = 20.0; const AVG_FOLLOW_DIST: f32 = 6.0; const RETARGETING_THRESHOLD_SECONDS: f64 = 10.0; const HEALING_ITEM_THRESHOLD: f32 = 0.5; +const IDLE_HEALING_ITEM_THRESHOLD: f32 = 0.999; const DEFAULT_ATTACK_RANGE: f32 = 2.0; const AWARENESS_INVESTIGATE_THRESHOLD: f32 = 1.0; const AWARENESS_DECREMENT_CONSTANT: f32 = 0.07; @@ -671,7 +672,7 @@ impl<'a> AgentData<'a> { read_data: &ReadData, event_emitter: &mut Emitter<'_, ServerEvent>, ) { - if self.damage < HEALING_ITEM_THRESHOLD && self.heal_self(agent, controller) { + if self.damage < HEALING_ITEM_THRESHOLD && self.heal_self(agent, controller, false) { agent.action_state.timer = 0.01; return; } @@ -809,7 +810,7 @@ impl<'a> AgentData<'a> { } }; - if self.damage < HEALING_ITEM_THRESHOLD && self.heal_self(agent, controller) { + if self.damage < IDLE_HEALING_ITEM_THRESHOLD && self.heal_self(agent, controller, true) { agent.action_state.timer = 0.01; return; } @@ -1427,30 +1428,32 @@ impl<'a> AgentData<'a> { /// Attempt to consume a healing item, and return whether any healing items /// were queued. Callers should use this to implement a delay so that - /// the healing isn't interrupted. - fn heal_self(&self, _agent: &mut Agent, controller: &mut Controller) -> bool { + /// the healing isn't interrupted. If `relaxed` is `true`, we allow eating food and prioritise healing. + fn heal_self(&self, _agent: &mut Agent, controller: &mut Controller, relaxed: bool) -> bool { let healing_value = |item: &Item| { let mut value = 0.0; if let ItemKind::Consumable { - kind: ConsumableKind::Drink, + kind, effects, .. } = &item.kind { - for effect in effects.iter() { - use BuffKind::*; - match effect { - Effect::Health(HealthChange { amount, .. }) => { - value += *amount; - }, - Effect::Buff(BuffEffect { kind, data, .. }) - if matches!(kind, Regeneration | Saturation | Potion) => - { - value += - data.strength * data.duration.map_or(0.0, |d| d.as_secs() as f32); - }, - _ => {}, + if matches!(kind, ConsumableKind::Drink) || (relaxed && matches!(kind, ConsumableKind::Food)) { + for effect in effects.iter() { + use BuffKind::*; + match effect { + Effect::Health(HealthChange { amount, .. }) => { + value += *amount; + }, + Effect::Buff(BuffEffect { kind, data, .. }) + if matches!(kind, Regeneration | Saturation | Potion) => + { + value += + data.strength * data.duration.map_or(0.0, |d| d.as_secs() as f32); + }, + _ => {}, + } } } } @@ -1466,7 +1469,11 @@ impl<'a> AgentData<'a> { }) .collect(); - consumables.sort_by_key(|(_, item)| healing_value(item)); + consumables.sort_by_key(|(_, item)| if relaxed { + -healing_value(item) + } else { + healing_value(item) + }); if let Some((id, _)) = consumables.last() { use comp::inventory::slot::Slot;