Reign
We've had plenty of time to think of a way for our Player
to get back at those pesky Friends
.
Sometimes, the simplest solution is the best.
If the Friends
can overpower the Player
when they are fully grown, we shouldn't let them reach that size.
I'm sure the Player
can overcome Friend
that are smaller than them.
use shipyard::{
Component, EntitiesView, IntoIter, IntoWithId, Unique, UniqueView, UniqueViewMut, View,
ViewMut, World,
};
#[derive(Component)]
struct ToDelete;
fn collision(
entities: EntitiesView,
mut player: UniqueViewMut<Player>,
v_friend: View<Friend>,
mut vm_to_delete: ViewMut<ToDelete>,
) -> Result<(), GameOver> {
for (eid, friend) in v_friend.iter().with_id() {
if friend.0.size == MAX_SIZE && friend.0.collide(&player.square) {
// -- SNIP --
} else if player.square.size >= friend.0.size && player.square.collide(&friend.0) {
player.square.size = (player.square.size + INIT_SIZE / 2.).min(MAX_SIZE - 0.01);
entities.add_component(eid, &mut vm_to_delete, ToDelete);
}
}
Ok(())
}
It appears our Player
can even overcome Friends
of equal size.
By... eating them!?
Remember when we added Friends
to the World
, each one was assigned an EntityId
.
We can iterate over both components and the EntityId
of the entity that owns them by using with_id
.
Then we can use this EntityId
to add another component to the vanquished Friends
.
As you may have noticed we are not modifying entities
. We only need it to check that the eid
is alive.
ToDelete
is not a special component, we still have to make it do its job.
use shipyard::{
AllStoragesViewMut, Component, EntitiesView, IntoIter, IntoWithId, SparseSet, Unique,
UniqueView, UniqueViewMut, View, ViewMut, World,
};
async fn main() {
// -- SNIP --
loop {
clear_background(WHITE);
world.run(move_player);
world.run(move_friends);
world.run(grow);
if world.run(collision).is_err() {
panic!("Murder");
}
world.run(clean_up);
world.run(render);
next_frame().await
}
}
fn clean_up(mut all_storages: AllStoragesViewMut) {
all_storages.delete_any::<SparseSet<ToDelete>>();
}
AllStorages
is the part of World
that stores all components and entities.
We are using it to delete_any
entity that has a ToDelete
component in a SparseSet
storage.
SparseSet
is the storage for all Component
s. Unique
s have a different storage and you can add custom storages to the World
but that's an advanced feature.
It's over
Defeating smaller Friends
is nice but most of the time they've grown by the time the Player
reaches them.
The Player
needs more power.
use shipyard::{
AllStoragesViewMut, Component, EntitiesView, EntitiesViewMut, IntoIter, IntoWithId, SparseSet,
Unique, UniqueView, UniqueViewMut, View, ViewMut, World,
};
const POWER_PELLET_SPAWN_RATE: u32 = 150;
async fn main() {
// -- SNIP --
let player = Player {
square: Square {
x,
y,
size: INIT_SIZE * 3.0,
},
pellet_counter: 0,
};
// -- SNIP --
loop {
// -- SNIP --
world.run(grow);
world.run(counters);
world.run(spawn);
if world.run(collision).is_err() {
panic!("Murder");
}
// -- SNIP --
}
}
struct Player {
square: Square,
pellet_counter: u32,
}
impl Player {
fn power_up(&mut self) {
self.pellet_counter = 120;
}
fn is_powered_up(&self) -> bool {
self.pellet_counter > 0
}
}
#[derive(Component)]
struct PowerPellet(Square);
fn render(player: UniqueView<Player>, v_friend: View<Friend>, v_power_pellets: View<PowerPellet>) {
for pellet in v_power_pellets.iter() {
pellet.0.render(YELLOW);
}
// -- SNIP --
if player.is_powered_up() {
player.square.render(YELLOW);
} else {
player.square.render(BLUE);
}
}
fn collision(
entities: EntitiesView,
mut player: UniqueViewMut<Player>,
v_friend: View<Friend>,
v_power_pellets: View<PowerPellet>,
mut vm_to_delete: ViewMut<ToDelete>,
) -> Result<(), GameOver> {
for (eid, pellet) in v_power_pellets.iter().with_id() {
if player.square.collide(&pellet.0) {
player.power_up();
entities.add_component(eid, &mut vm_to_delete, ToDelete);
}
}
for (eid, friend) in v_friend.iter().with_id() {
if friend.0.size == MAX_SIZE && friend.0.collide(&player.square) {
if player.is_powered_up() {
player.square.size = (player.square.size + INIT_SIZE / 2.).min(MAX_SIZE - 0.01);
entities.add_component(eid, &mut vm_to_delete, ToDelete);
continue;
}
player.square.size -= INIT_SIZE / 2.;
// -- SNIP --
} else if player.square.size >= friend.0.size && player.square.collide(&friend.0) {
// -- SNIP --
}
}
Ok(())
}
fn counters(mut player: UniqueViewMut<Player>) {
player.pellet_counter = player.pellet_counter.saturating_sub(1);
}
fn spawn(mut entities: EntitiesViewMut, mut vm_power_pellets: ViewMut<PowerPellet>) {
let width = screen_width();
let height = screen_height();
let pellet_spawn_rate = if vm_power_pellets.is_empty() {
POWER_PELLET_SPAWN_RATE / 2
} else {
POWER_PELLET_SPAWN_RATE
};
if rand::gen_range(0, pellet_spawn_rate) == 0 {
let x = rand::gen_range(0.0, width - INIT_SIZE);
let y = rand::gen_range(0.0, height - INIT_SIZE);
entities.add_entity(
&mut vm_power_pellets,
PowerPellet(Square {
x,
y,
size: INIT_SIZE * 2.0,
}),
);
}
}
The syntax to add entities is very similar to adding components.
But this time we need EntitiesViewMut
.
With this change the Player
is can now rest, stronger than ever.