Skip to content

Commit 66c0e28

Browse files
authored
feat: add people and naming logic (#79)
1 parent b71c14d commit 66c0e28

File tree

7 files changed

+318
-9
lines changed

7 files changed

+318
-9
lines changed

assets/names.toml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
neutral_names = [
2+
"William",
3+
"Robert",
4+
"Richard",
5+
"John",
6+
"Geoffrey",
7+
"Henry",
8+
"Thomas",
9+
"Edmund",
10+
"Edward",
11+
"Roger",
12+
"Ralph",
13+
"Walter",
14+
"Hugh",
15+
"Baldwin",
16+
"Gilbert",
17+
"Simon",
18+
"Peter",
19+
"Philip",
20+
"Stephen",
21+
"Reynold",
22+
"Alan",
23+
"Nicholas",
24+
"Martin",
25+
"Andrew",
26+
"Michael",
27+
"Bernard",
28+
"Godfrey",
29+
"Humphrey",
30+
"Oswald",
31+
"Anselm",
32+
"Cedric",
33+
"Alaric",
34+
"Percival",
35+
"Tristan",
36+
"Gawain",
37+
"Benedict",
38+
"Frederick",
39+
"Conrad",
40+
"Otto",
41+
"Ludwig",
42+
"Klaus",
43+
"Bruno",
44+
"Gunther",
45+
"Dietrich",
46+
"Albrecht",
47+
"Reinhard",
48+
"Matthias",
49+
"Gregor",
50+
"Casimir",
51+
"Thibault",
52+
"Eleanor",
53+
"Margaret",
54+
"Isabella",
55+
"Catherine",
56+
"Joan",
57+
"Alice",
58+
"Agnes",
59+
"Matilda",
60+
"Emma",
61+
"Beatrice",
62+
"Cecilia",
63+
"Constance",
64+
"Edith",
65+
"Maud",
66+
"Rose",
67+
"Anne",
68+
"Elizabeth",
69+
"Mary",
70+
"Jane",
71+
"Philippa",
72+
"Eleanor",
73+
"Blanche",
74+
"Isabeau",
75+
"Alienor",
76+
"Giselle",
77+
"Hildegard",
78+
"Brunhilde",
79+
"Gertrude",
80+
"Adelaide",
81+
"Clothilde",
82+
"Griselda",
83+
"Rowena",
84+
"Guinevere",
85+
"Vivienne",
86+
"Morgana",
87+
"Yseult",
88+
"Mathilde",
89+
"Bertha",
90+
"Ingrid",
91+
"Astrid",
92+
"Freya",
93+
"Signy",
94+
"Helga",
95+
"Ursula",
96+
"Brigitte",
97+
"Greta",
98+
"Katarina",
99+
"Lenore",
100+
"Rosamund",
101+
"Sybil",
102+
]
Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,90 @@
11
use bevy::prelude::*;
22

3-
use crate::gameplay::hud::tome::{TomeTab, UITomeLeftPageRoot, widgets};
3+
use crate::gameplay::{
4+
hud::tome::{TomeTab, UIEntryList, UITomeLeftPageRoot, widgets},
5+
people::Person,
6+
player::Player,
7+
storage::{Storage, StoredBy},
8+
};
49

510
pub(super) fn plugin(app: &mut App) {
611
app.add_systems(OnEnter(TomeTab::People), spawn_people_grid);
12+
13+
app.add_systems(
14+
Update,
15+
(backfill_people_grid, refresh_person_badges).run_if(in_state(TomeTab::People)),
16+
);
17+
}
18+
19+
#[derive(Component, Reflect, Debug)]
20+
#[reflect(Component)]
21+
pub struct PersonBadge(pub Entity);
22+
23+
#[derive(Component, Reflect, Debug)]
24+
#[reflect(Component)]
25+
pub struct PersonName;
26+
27+
fn person_badge(person: Entity) -> impl Bundle {
28+
(
29+
PersonBadge(person),
30+
Node::default(),
31+
children![(Text::default(), PersonName,)],
32+
)
33+
}
34+
35+
fn refresh_person_badges(
36+
badges: Query<(Entity, &PersonBadge)>,
37+
children: Query<&Children>,
38+
people: Query<&Name, With<Person>>,
39+
mut components: ParamSet<(Query<&mut Text, With<PersonName>>,)>,
40+
) {
41+
for (badge, PersonBadge(person)) in badges {
42+
let Ok(name) = people.get(*person) else {
43+
continue;
44+
};
45+
46+
for child in children.iter_descendants(badge) {
47+
if let Ok(mut text) = components.p0().get_mut(child) {
48+
text.0 = name.to_string();
49+
}
50+
}
51+
}
52+
}
53+
54+
fn spawn_people_grid(
55+
mut commands: Commands,
56+
left_page: Single<Entity, With<UITomeLeftPageRoot>>,
57+
player: Single<Entity, With<Player>>,
58+
storage: Query<&Storage>,
59+
people: Query<Entity, With<Person>>,
60+
) {
61+
let people_grid = commands
62+
.spawn((
63+
widgets::list_page(),
64+
ChildOf(*left_page),
65+
DespawnOnExit(TomeTab::People),
66+
))
67+
.id();
68+
69+
for person in storage
70+
.iter_descendants(*player)
71+
.filter(|s| people.contains(*s))
72+
{
73+
commands.spawn((person_badge(person), ChildOf(people_grid)));
74+
}
775
}
876

9-
fn spawn_people_grid(mut commands: Commands, left_page: Single<Entity, With<UITomeLeftPageRoot>>) {
10-
commands.spawn((
11-
widgets::list_page(),
12-
ChildOf(*left_page),
13-
DespawnOnExit(TomeTab::People),
14-
children![Text::new("People")],
15-
));
77+
fn backfill_people_grid(
78+
mut commands: Commands,
79+
people_grid: Single<Entity, With<UIEntryList>>,
80+
player: Single<Entity, With<Player>>,
81+
new_people: Query<(Entity, &StoredBy), (With<Person>, Added<StoredBy>)>,
82+
) {
83+
for (person, stored_by) in new_people {
84+
if stored_by.0 != *player {
85+
continue;
86+
}
87+
88+
commands.spawn((person_badge(person), ChildOf(*people_grid)));
89+
}
1690
}

src/gameplay/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use crate::screens::Screen;
55
pub mod hud;
66
pub mod item;
77
pub mod logistics;
8+
pub mod people;
89
pub mod player;
10+
pub mod random;
911
pub mod recipe;
1012
pub mod sprite_sort;
1113
pub mod storage;
@@ -30,7 +32,9 @@ pub fn plugin(app: &mut App) {
3032
hud::plugin,
3133
item::plugin,
3234
logistics::plugin,
35+
people::plugin,
3336
player::plugin,
37+
random::plugin,
3438
recipe::plugin,
3539
sprite_sort::plugin,
3640
structure::plugin,

src/gameplay/people/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use bevy::prelude::*;
2+
3+
pub mod naming;
4+
5+
pub(super) fn plugin(app: &mut App) {
6+
app.add_plugins((naming::plugin,));
7+
}
8+
9+
#[derive(Component, Reflect, Debug, Default)]
10+
#[reflect(Component)]
11+
pub struct Person;

src/gameplay/people/naming.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use bevy::prelude::*;
2+
use rand::seq::SliceRandom;
3+
use serde::Deserialize;
4+
5+
use crate::{
6+
assets::{loaders::toml::TomlAssetPlugin, tracking::LoadResource},
7+
gameplay::random::Seed,
8+
screens::Screen,
9+
};
10+
11+
const REFILL_THRESHOLD: usize = 50;
12+
13+
pub(super) fn plugin(app: &mut App) {
14+
app.add_plugins(TomlAssetPlugin::<Names>::extensions(&["toml"]));
15+
16+
app.init_asset::<Names>();
17+
18+
app.load_resource::<NameAssets>();
19+
20+
app.init_resource::<NameManager>();
21+
22+
app.add_systems(
23+
Update,
24+
load_names.run_if(in_state(Screen::Gameplay).and(name_queue_low)),
25+
);
26+
}
27+
28+
#[derive(Asset, Reflect, Deserialize)]
29+
pub struct Names {
30+
pub neutral_names: Vec<String>,
31+
}
32+
33+
#[derive(Asset, Resource, Reflect, Debug, Clone)]
34+
#[reflect(Resource)]
35+
pub struct NameAssets {
36+
names: Handle<Names>,
37+
}
38+
39+
impl FromWorld for NameAssets {
40+
fn from_world(world: &mut World) -> Self {
41+
let asset_server = world.resource::<AssetServer>();
42+
Self {
43+
names: asset_server.load("names.toml"),
44+
}
45+
}
46+
}
47+
48+
#[derive(Resource, Reflect, Debug, Default)]
49+
#[reflect(Resource)]
50+
pub struct NameManager {
51+
name_queue: Vec<String>,
52+
}
53+
54+
impl NameManager {
55+
const DEFAULT_NAME: &'static str = "Unknown";
56+
57+
pub fn next(&mut self) -> String {
58+
self.name_queue
59+
.pop()
60+
.unwrap_or(String::from(Self::DEFAULT_NAME))
61+
}
62+
}
63+
64+
fn name_queue_low(name_manager: Res<NameManager>) -> bool {
65+
name_manager.name_queue.len() <= REFILL_THRESHOLD
66+
}
67+
68+
fn load_names(
69+
mut name_manager: ResMut<NameManager>,
70+
name_assets: Res<NameAssets>,
71+
names: Res<Assets<Names>>,
72+
mut seed: ResMut<Seed>,
73+
) {
74+
if let Some(name_collections) = names.get(&name_assets.names) {
75+
let mut shuffled = name_collections.neutral_names.clone();
76+
shuffled.shuffle(&mut seed);
77+
name_manager.name_queue.append(&mut shuffled);
78+
}
79+
}

src/gameplay/player.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
use bevy::prelude::*;
1+
use std::time::Duration;
2+
3+
use bevy::{prelude::*, time::common_conditions::on_timer};
24

35
use crate::{
46
gameplay::{
57
item::{assets::ItemDef, stack::Stack},
8+
people::{Person, naming::NameManager},
69
storage::StoredBy,
710
},
811
screens::Screen,
912
};
1013

1114
pub(super) fn plugin(app: &mut App) {
1215
app.add_systems(OnEnter(Screen::Gameplay), spawn_player);
16+
17+
app.add_systems(
18+
Update,
19+
give_player_a_person
20+
.run_if(in_state(Screen::Gameplay).and(on_timer(Duration::from_secs(5)))),
21+
);
1322
}
1423

1524
#[derive(Component, Reflect, Debug, Default)]
@@ -34,3 +43,18 @@ fn spawn_player(
3443
));
3544
}
3645
}
46+
47+
fn give_player_a_person(
48+
player: Single<Entity, With<Player>>,
49+
mut commands: Commands,
50+
mut name_manager: ResMut<NameManager>,
51+
) {
52+
let name = name_manager.next();
53+
54+
commands.spawn((
55+
Name::new(name.clone()),
56+
Person,
57+
StoredBy(*player),
58+
ChildOf(*player),
59+
));
60+
}

src/gameplay/random.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use bevy::prelude::*;
2+
use rand::{SeedableRng, rngs::StdRng};
3+
4+
pub fn plugin(app: &mut App) {
5+
app.init_resource::<Seed>();
6+
}
7+
8+
#[derive(Resource, Debug, Deref, DerefMut)]
9+
pub struct Seed(pub StdRng);
10+
11+
impl Default for Seed {
12+
fn default() -> Self {
13+
Self(StdRng::from_os_rng())
14+
}
15+
}

0 commit comments

Comments
 (0)