Skip to main content

nil_core/behavior/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4pub mod r#impl;
5pub mod score;
6
7use crate::error::Result;
8use crate::world::World;
9use r#impl::idle::IdleBehavior;
10use rand::seq::{IndexedRandom, SliceRandom};
11use score::BehaviorScore;
12use std::any::Any;
13use std::collections::HashSet;
14use std::fmt::Debug;
15use std::ops::ControlFlow;
16
17pub trait Behavior: Any + Debug {
18  fn score(&self, world: &World) -> Result<BehaviorScore>;
19
20  fn behave(&self, world: &mut World) -> Result<ControlFlow<()>>;
21
22  fn boxed(self) -> Box<dyn Behavior>
23  where
24    Self: Sized + 'static,
25  {
26    Box::new(self)
27  }
28}
29
30#[must_use]
31pub struct BehaviorProcessor<'a> {
32  world: &'a mut World,
33  behaviors: Vec<Box<dyn Behavior>>,
34  buffer: Vec<(usize, BehaviorScore)>,
35  candidates: Vec<usize>,
36  broken: HashSet<usize>,
37}
38
39impl<'a> BehaviorProcessor<'a> {
40  pub(crate) fn new(world: &'a mut World, mut behaviors: Vec<Box<dyn Behavior>>) -> Self {
41    let buffer = Vec::with_capacity(behaviors.len());
42    let candidates = Vec::new();
43    let broken = HashSet::new();
44
45    behaviors.shuffle(&mut rand::rng());
46
47    Self {
48      world,
49      behaviors,
50      buffer,
51      candidates,
52      broken,
53    }
54  }
55
56  fn is_idle(&self, idx: usize) -> bool {
57    (self.behaviors[idx].as_ref() as &dyn Any).is::<IdleBehavior>()
58  }
59}
60
61impl Iterator for BehaviorProcessor<'_> {
62  type Item = Result<()>;
63
64  fn next(&mut self) -> Option<Self::Item> {
65    self.buffer.clear();
66    self.candidates.clear();
67
68    for (idx, behavior) in self.behaviors.iter().enumerate() {
69      if !self.broken.contains(&idx) {
70        match behavior.score(self.world) {
71          Ok(score) => {
72            if score > BehaviorScore::MIN {
73              self.buffer.push((idx, score));
74            }
75          }
76          Err(err) => return Some(Err(err)),
77        }
78      }
79    }
80
81    self
82      .buffer
83      .sort_unstable_by_key(|(_, score)| *score);
84
85    let highest = self.buffer.last()?;
86    if self.is_idle(highest.0) {
87      return None;
88    }
89
90    self
91      .buffer
92      .iter()
93      .filter(|(_, score)| highest.1.is_within_range(*score, 0.2))
94      .map(|(idx, _)| *idx)
95      .collect_into(&mut self.candidates);
96
97    let idx = self
98      .candidates
99      .choose(&mut rand::rng())
100      .copied()
101      .filter(|idx| !self.is_idle(*idx))?;
102
103    let behavior = &self.behaviors[idx];
104    match behavior.behave(self.world) {
105      Ok(cf) => {
106        if let ControlFlow::Break(()) = cf {
107          self.broken.insert(idx);
108        }
109
110        Some(Ok(()))
111      }
112      Err(err) => Some(Err(err)),
113    }
114  }
115}