1use flecs_ecs::prelude::*;
5
6#[derive(Component, Debug)]
12struct Item;
13
14#[derive(Component, Debug)]
16struct Container;
17
18#[derive(Component, Debug)]
20struct Inventory;
21
22#[derive(Component, Debug)]
23struct ContainedBy;
24
25#[derive(Component, Debug)]
29struct Active;
30
31#[derive(Component, Debug, Clone, Copy, PartialEq, Eq)]
33struct Amount {
34 amount: i32,
35}
36
37#[derive(Component, Debug, Clone)]
39struct Health {
40 value: i32,
41}
42
43#[derive(Component, Debug, Clone, Copy, PartialEq, Eq)]
45struct Attack {
46 value: i32,
47}
48
49#[derive(Component, Debug)]
51struct Sword;
52#[derive(Component, Debug)]
53struct Armor;
54#[derive(Component, Debug)]
55struct Coin;
56
57#[derive(Component, Debug)]
60struct WoodenSword;
61#[derive(Component, Debug)]
62struct IronSword;
63#[derive(Component, Debug)]
64struct WoodenArmor;
65#[derive(Component, Debug)]
66struct IronArmor;
67
68fn item_kind(item: EntityView<'_>) -> Option<Entity> {
73 let world = item.world();
74 let mut result_entity: Option<Entity> = None;
75
76 item.each_component(|comp| {
77 if comp.is_entity() {
78 if comp.entity_view().has((id::<flecs::IsA>(), Item)) {
81 result_entity = Some(comp.entity_view().id());
82 }
83 } else if comp.is_pair() {
84 if comp.first_id() == flecs::IsA::ID
87 && let Some(base_kind) = item_kind(comp.second_id())
88 {
89 result_entity = Some(base_kind);
90 }
91 }
92 });
93
94 result_entity
95}
96
97fn item_name(item: EntityView<'_>) -> Option<String> {
100 let world = item.world();
101 let mut result_name: Option<String> = None;
102
103 item.each_component(|comp| {
104 if comp.is_entity() {
105 if comp.entity_view().has((id::<flecs::IsA>(), Item)) {
106 result_name = comp.entity_view().get_name();
107 }
108 } else if comp.is_pair()
109 && comp.first_id() == flecs::IsA::ID
110 && let Some(base_kind) = item_kind(comp.second_id())
111 {
112 result_name = comp.second_id().get_name();
113 }
114 });
115
116 result_name
117}
118
119fn get_container(container: EntityView<'_>) -> Entity {
121 let world = container.world();
122 if container.has(Container) {
123 return container.id();
124 }
125 container.target(Inventory, 0).unwrap().id()
126}
127
128fn for_each_item<F>(container: EntityView<'_>, mut func: F)
130where
131 F: FnMut(flecs_ecs::core::EntityView<'_>, ()),
132{
133 let world = container.world();
134 world
135 .query::<()>()
136 .with((ContainedBy, container))
137 .build()
138 .each_entity(func);
139}
140
141fn find_item_w_kind(
144 container: EntityView<'_>,
145 kind: Entity,
146 active_required: bool,
147) -> Option<Entity> {
148 let world = container.world();
149
150 let mut result: Option<Entity> = None;
151
152 let container = world.entity_from_id(get_container(container));
153
154 for_each_item(container, |item, _| {
155 if active_required && !item.has(Active) {
158 return;
159 }
160
161 if let Some(ik) = item_kind(item)
162 && ik == kind
163 {
164 result = Some(item.id());
165 }
166 });
167
168 result
169}
170
171fn transfer_item(container: EntityView<'_>, item: EntityView<'_>) {
173 let world = container.world();
174
175 let amt = item.try_cloned::<&Amount>().unwrap_or(Amount { amount: 1 });
176
177 #[allow(clippy::redundant_else)]
178 if amt.amount > 0 {
179 let ik = item_kind(item).unwrap();
182 let dst_item = find_item_w_kind(container, ik, false);
183
184 if let Some(dst_item) = dst_item {
185 world
187 .entity_from_id(dst_item)
188 .get::<&mut Amount>(|dst_amt| {
189 dst_amt.amount += amt.amount;
190 });
191 item.destruct();
192 return;
193 } else {
194 }
197 }
198
199 item.add((ContainedBy, container));
201}
202
203fn transfer_items(dst: EntityView<'_>, src: EntityView<'_>) {
205 let world = dst.world();
206 println!(">> Transfer items from {} to {}\n", src.name(), dst.name());
207
208 world.defer(|| {
211 let dst_container = world.entity_from_id(get_container(dst));
212 let src_container = world.entity_from_id(get_container(src));
213
214 for_each_item(src_container, |item, _| {
215 transfer_item(dst_container, item);
216 });
217 });
218}
219
220fn attack(player: EntityView<'_>, weapon: EntityView<'_>) {
222 let world = player.world();
223
224 println!(
225 ">> {} is attacked with a {}!",
226 player.name(),
227 item_name(weapon).unwrap_or("UnknownItem".to_string())
228 );
229
230 let atk = weapon.try_cloned::<&Attack>();
231
232 if atk.is_none() {
233 println!(" - the weapon is a dud");
235 return;
236 }
237
238 let mut att_value = atk.unwrap().value;
239
240 if let Some(armor_e) = find_item_w_kind(player, world.component_id::<Armor>(), true) {
242 let armor_e = world.entity_from_id(armor_e);
243 armor_e.get::<Option<&mut Health>>(|health| {
244 if let Some(armor_health) = health {
245 println!(
246 " - {} defends with {} ({} health)",
247 player.name(),
248 item_name(armor_e).unwrap(),
249 armor_health.value
250 );
251
252 armor_health.value -= att_value;
255
256 if armor_health.value <= 0 {
257 att_value = -armor_health.value;
258 armor_e.destruct();
259 println!(" - {} is destroyed!", item_name(armor_e).unwrap());
260 } else {
261 println!(
262 " - {} has {} health left after taking {} damage",
263 item_name(armor_e).unwrap(),
264 armor_health.value,
265 att_value
266 );
267 att_value = 0;
268 }
269 } else {
270 println!(" - the {} armor is a dud", item_name(armor_e).unwrap());
272 }
273 });
274 } else {
275 println!(" - {} fights without armor!", player.name());
277 }
278
279 weapon.get::<&mut Health>(|weapon_health| {
281 if weapon_health.value > 0 {
282 weapon_health.value -= 1;
283 if weapon_health.value == 0 {
284 println!(" - {} is destroyed!", item_name(weapon).unwrap());
285 weapon.destruct();
286 } else {
287 println!(
288 " - {} has {} uses left",
289 item_name(weapon).unwrap(),
290 weapon_health.value
291 );
292 }
293 }
294 });
295
296 if att_value > 0 {
298 player.get::<&mut Health>(|player_health| {
299 player_health.value -= att_value;
300 if player_health.value <= 0 {
301 println!(" - {} died!", player.name());
302 player.destruct();
303 } else {
304 println!(
305 " - {} has {} health left after taking {} damage",
306 player.name(),
307 player_health.value,
308 att_value
309 );
310 }
311 });
312 }
313
314 println!();
315}
316
317fn print_items(container: EntityView<'_>) {
319 println!("-- {}'s inventory:", container.name());
320
321 let world = container.world();
322 let mut count = 0;
323
324 let container = world.entity_from_id(get_container(container));
327
328 for_each_item(container, |item, _| {
329 let amount = item
332 .try_cloned::<&Amount>()
333 .unwrap_or(Amount { amount: 1 })
334 .amount;
335 println!(
336 " - {} {} ({})",
337 amount,
338 item_name(item).unwrap_or("UnknownItem".to_string()),
339 world.entity_from_id(item_kind(item).unwrap()).name()
340 );
341
342 count += 1;
343 });
344
345 if count == 0 {
346 println!(" - << empty >>");
347 }
348
349 println!();
350}
351
352#[derive(Component)]
355pub struct InventoryComponentsModule;
356
357#[derive(Component)]
358pub struct InventoryModule;
359
360impl Module for InventoryComponentsModule {
361 fn module(world: &World) {
362 world.module::<InventoryComponentsModule>("inventory::components");
363
364 world.component::<Item>();
365 world.component::<Container>();
366 world.component::<Inventory>();
367 world
369 .component::<ContainedBy>()
370 .add_trait::<flecs::Exclusive>();
371 }
372}
373
374impl Module for InventoryModule {
375 fn module(world: &World) {
376 world.module::<InventoryModule>("inventory::systems");
377 world.import::<InventoryComponentsModule>();
378 }
379}
380
381#[derive(Component)]
382pub struct ItemComponentsModule;
383
384impl Module for ItemComponentsModule {
385 fn module(world: &World) {
386 world.module::<ItemComponentsModule>("item::components");
387
388 world.component::<Active>();
389 world.component::<Amount>();
390
391 world.component::<Health>();
393
394 world
395 .component::<Attack>()
396 .add_trait::<(flecs::OnInstantiate, flecs::Inherit)>();
397
398 world
399 .component::<Sword>()
400 .is_a(Item)
401 .add_trait::<(flecs::OnInstantiate, flecs::Inherit)>();
402
403 world
404 .component::<Armor>()
405 .is_a(Item)
406 .add_trait::<(flecs::OnInstantiate, flecs::Inherit)>();
407 world
408 .component::<Coin>()
409 .is_a(Item)
410 .add_trait::<(flecs::OnInstantiate, flecs::Inherit)>();
411
412 world
414 .prefab_type::<WoodenSword>()
415 .add(Sword)
416 .set(Attack { value: 1 })
417 .set(Health { value: 5 });
419
420 world
421 .prefab_type::<IronSword>()
422 .add(Sword)
423 .set(Attack { value: 4 })
424 .set(Health { value: 10 });
425
426 world
427 .prefab_type::<WoodenArmor>()
428 .add(Armor)
429 .set(Health { value: 10 });
430
431 world
432 .prefab_type::<IronArmor>()
433 .add(Armor)
434 .set(Health { value: 20 });
435 }
436}
437
438#[derive(Component)]
439pub struct ItemModule;
440
441impl Module for ItemModule {
442 fn module(world: &World) {
443 world.module::<ItemModule>("item::systems");
444 world.import::<ItemComponentsModule>();
445 }
446}
447
448fn main() {
451 let mut world = World::new();
452
453 world.import::<InventoryModule>();
454 world.import::<ItemModule>();
455
456 let loot_box = world
458 .entity_named("Chest")
459 .add(Container)
460 .with_first(ContainedBy, || {
461 world.entity().is_a(IronSword);
462 world.entity().is_a(WoodenArmor);
463 world.entity().add(Coin).set(Amount { amount: 30 });
464 });
465
466 let player = world.entity_named("Player").set(Health { value: 10 }).add((
468 Inventory,
469 world.entity().add(Container).with_first(ContainedBy, || {
470 world.entity().add(Coin).set(Amount { amount: 20 });
471 }),
472 ));
473
474 print_items(loot_box);
476
477 print_items(player);
479
480 transfer_items(player, loot_box);
482
483 print_items(player);
485
486 print_items(loot_box);
488
489 if let Some(armor) = find_item_w_kind(player, world.component_id::<Armor>(), false) {
491 world.entity_from_id(armor).add(Active);
492 }
493
494 let my_sword = world.entity().is_a(IronSword);
496
497 attack(player, my_sword);
499 attack(player, my_sword);
500 attack(player, my_sword);
501 attack(player, my_sword);
502 attack(player, my_sword);
503
504 }