1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::abi::EntityPlace;
use crate::ecs::Entity;
use super::{ComponentType, ContainerKinds, Placed, World, WorldError};
impl World {
/// Every entity with a component of type `ty` and a place in the world,
/// such as every bed or every chest; with [`World::transform_type`],
/// every placed entity. The engine walks every such archetype, so call
/// it now and then, not every step. On the game thread only; empty
/// elsewhere.
///
/// # Examples
///
/// ```
/// fn count(load: &enshwick::Load<'_>, world: &enshwick::world::World) -> Result<usize, enshwick::Off> {
/// let beds = world.component_type(load, "keen::ecs::NpcBed")?;
/// Ok(world.entities(beds).len())
/// }
/// ```
#[must_use]
pub fn entities(&self, ty: ComponentType) -> Vec<Placed> {
let entities = self.0.host.api().entities;
let mut capacity = 1024u32;
loop {
let mut places = vec![EntityPlace::default(); capacity as usize];
let mut count = 0u32;
let status = entities(
self.0.host.handle(),
ty.0,
places.as_mut_ptr(),
capacity,
&raw mut count,
);
if !status.is_ok() {
return Vec::new();
}
if count <= capacity {
places.truncate(count as usize);
return places.iter().map(Placed::from_abi).collect();
}
capacity = count;
}
}
/// The entities with a component of type `ty` within `radius` metres of
/// `point`, nearest first; with [`World::transform_type`], every placed
/// entity there. On the game thread only; empty elsewhere.
///
/// # Examples
///
/// ```
/// fn nearby(world: &enshwick::world::World, player: enshwick::ecs::Entity) -> usize {
/// let Some(at) = world.position(player) else { return 0 };
/// world.near(at, 20.0, world.transform_type()).len()
/// }
/// ```
#[must_use]
pub fn near(self, point: [f64; 3], radius: f64, ty: ComponentType) -> Vec<Placed> {
let mut found: Vec<(f64, Placed)> = self
.entities(ty)
.into_iter()
.map(|placed| (placed.distance(point), placed))
.filter(|&(distance, _)| distance <= radius)
.collect();
found.sort_by(|a, b| a.0.total_cmp(&b.0));
found.into_iter().map(|(_, placed)| placed).collect()
}
/// The component type every placed entity has, `CurrentTransform`.
///
/// # Examples
///
/// See [`World::near`].
#[must_use]
pub const fn transform_type(self) -> ComponentType {
self.0.transform
}
/// The world's containers of a kind in `kinds`, with their places: by
/// default what players build, such as storage chests; see
/// [`ContainerKinds`] for the rest. Each kind is told apart by a
/// component the game gives it. Call it now and then, as
/// [`World::entities`]. On the game thread only; empty elsewhere and on
/// a build that lacks any of those components.
///
/// # Examples
///
/// ```
/// use enshwick::actions::{Action, Outcome};
/// use enshwick::world::ContainerKinds;
///
/// fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
/// let world = load.world()?;
/// let actions = load.actions(enshwick::i18n::Texts::default())?;
/// actions.add(Action::new("chests", "Count the built chests"), move |_request| {
/// let built = world.containers(ContainerKinds::default()).len();
/// Outcome::done(format!("{built} built containers"))
/// })
/// }
/// ```
#[must_use]
pub fn containers(self, kinds: ContainerKinds) -> Vec<Placed> {
self.0
.containers
.map(|containers| containers.of(self, kinds))
.unwrap_or_default()
}
/// Asks the server to destroy `entity` after the systems of the next
/// step, as the game removes things itself: no loot and no death.
///
/// # Errors
///
/// [`WorldError::Refused`] for a player's character,
/// [`WorldError::Gone`] for an entity not in the world,
/// [`WorldError::WrongThread`] off the game thread and
/// [`WorldError::Missing`] on any other refusal.
///
/// # Examples
///
/// ```
/// fn clear(world: &enshwick::world::World, drop: enshwick::ecs::Entity) {
/// if let Err(why) = world.destroy(drop) {
/// enshwick::warn!("entity {} stays: {why}", drop.0);
/// }
/// }
/// ```
pub fn destroy(self, entity: Entity) -> Result<(), WorldError> {
WorldError::check(
(self.0.host.api().destroy)(self.0.host.handle(), entity.0),
WorldError::Gone,
)
}
}