Made NPCs prefer food when idle

This commit is contained in:
Joshua Barretto
2022-01-15 22:44:16 +00:00
parent 5c37786185
commit 2923d3cd2b
5 changed files with 45 additions and 23 deletions

View File

@ -153,6 +153,19 @@ pub fn make_potion_bag(quantity: u32) -> Item {
bag 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] #[must_use]
// We have many species so this function is long // We have many species so this function is long
// Also we are using default tools for un-specified species so // Also we are using default tools for un-specified species so

View File

@ -116,7 +116,7 @@ impl Link for Mounting {
.map(|p| p.0.map(|e| e.floor())) .map(|p| p.0.map(|e| e.floor()))
.unwrap_or_else(|| terrain.find_space(old_pos).map(|e| e as f32)) .unwrap_or_else(|| terrain.find_space(old_pos).map(|e| e as f32))
+ Vec3::new(0.5, 0.5, 0.0); + Vec3::new(0.5, 0.5, 0.0);
force_update.insert(rider, comp::ForceUpdate); let _ = force_update.insert(rider, comp::ForceUpdate);
}); });
} }
} }

View File

@ -1874,7 +1874,7 @@ fn resolve_e2e_collision(
let diff = diff.normalized(); 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; *collision_registered = true;

View File

@ -1,6 +1,6 @@
use super::*; use super::*;
use common::{ 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, resources::Time,
rtsim::{Memory, MemoryItem}, rtsim::{Memory, MemoryItem},
store::Id, store::Id,
@ -143,7 +143,9 @@ impl Entity {
// give potions to traveler humanoids or return loadout as is otherwise // give potions to traveler humanoids or return loadout as is otherwise
match (body, kind) { match (body, kind) {
(comp::Body::Humanoid(_), RtSimEntityKind::Random) => { (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) => { (_, RtSimEntityKind::Merchant) => {
|l, trade| l.with_creator(world::site::settlement::merchant_loadout, trade) |l, trade| l.with_creator(world::site::settlement::merchant_loadout, trade)

View File

@ -176,6 +176,7 @@ const MAX_FLEE_DIST: f32 = 20.0;
const AVG_FOLLOW_DIST: f32 = 6.0; const AVG_FOLLOW_DIST: f32 = 6.0;
const RETARGETING_THRESHOLD_SECONDS: f64 = 10.0; const RETARGETING_THRESHOLD_SECONDS: f64 = 10.0;
const HEALING_ITEM_THRESHOLD: f32 = 0.5; const HEALING_ITEM_THRESHOLD: f32 = 0.5;
const IDLE_HEALING_ITEM_THRESHOLD: f32 = 0.999;
const DEFAULT_ATTACK_RANGE: f32 = 2.0; const DEFAULT_ATTACK_RANGE: f32 = 2.0;
const AWARENESS_INVESTIGATE_THRESHOLD: f32 = 1.0; const AWARENESS_INVESTIGATE_THRESHOLD: f32 = 1.0;
const AWARENESS_DECREMENT_CONSTANT: f32 = 0.07; const AWARENESS_DECREMENT_CONSTANT: f32 = 0.07;
@ -671,7 +672,7 @@ impl<'a> AgentData<'a> {
read_data: &ReadData, read_data: &ReadData,
event_emitter: &mut Emitter<'_, ServerEvent>, 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; agent.action_state.timer = 0.01;
return; 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; agent.action_state.timer = 0.01;
return; return;
} }
@ -1427,30 +1428,32 @@ impl<'a> AgentData<'a> {
/// Attempt to consume a healing item, and return whether any healing items /// Attempt to consume a healing item, and return whether any healing items
/// were queued. Callers should use this to implement a delay so that /// were queued. Callers should use this to implement a delay so that
/// the healing isn't interrupted. /// 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) -> bool { fn heal_self(&self, _agent: &mut Agent, controller: &mut Controller, relaxed: bool) -> bool {
let healing_value = |item: &Item| { let healing_value = |item: &Item| {
let mut value = 0.0; let mut value = 0.0;
if let ItemKind::Consumable { if let ItemKind::Consumable {
kind: ConsumableKind::Drink, kind,
effects, effects,
.. ..
} = &item.kind } = &item.kind
{ {
for effect in effects.iter() { if matches!(kind, ConsumableKind::Drink) || (relaxed && matches!(kind, ConsumableKind::Food)) {
use BuffKind::*; for effect in effects.iter() {
match effect { use BuffKind::*;
Effect::Health(HealthChange { amount, .. }) => { match effect {
value += *amount; Effect::Health(HealthChange { amount, .. }) => {
}, value += *amount;
Effect::Buff(BuffEffect { kind, data, .. }) },
if matches!(kind, Regeneration | Saturation | Potion) => 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); value +=
}, data.strength * data.duration.map_or(0.0, |d| d.as_secs() as f32);
_ => {}, },
_ => {},
}
} }
} }
} }
@ -1466,7 +1469,11 @@ impl<'a> AgentData<'a> {
}) })
.collect(); .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() { if let Some((id, _)) = consumables.last() {
use comp::inventory::slot::Slot; use comp::inventory::slot::Slot;