Skip to content

Commit

Permalink
Rework object spawning
Browse files Browse the repository at this point in the history
This has the following benefits:

* Future object spawning crates (e.g. controller) won't have to depend
  on map crate and structures which are tight with persistent map format.

* Spawn events no longer need to be buffered and resend.

Relates to #23.
  • Loading branch information
Indy2222 committed Jun 25, 2022
1 parent b54f889 commit 555693d
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 54 deletions.
9 changes: 9 additions & 0 deletions crates/core/src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ pub enum ObjectType {
Inactive(InactiveObjectType),
}

impl fmt::Display for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Active(active) => write!(f, "Active -> {}", active),
Self::Inactive(inactive) => write!(f, "Inactive -> {}", inactive),
}
}
}

#[derive(Copy, Clone, Debug, Component, Serialize, Deserialize, PartialEq, Enum)]
pub enum InactiveObjectType {
Tree,
Expand Down
28 changes: 23 additions & 5 deletions crates/loader/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ use bevy::{
};
use de_camera::MoveFocusEvent;
use de_core::{
assets::asset_path, gconfig::GameConfig, log_full_error, objects::ActiveObjectType,
projection::ToMsl, state::GameState,
assets::asset_path,
gconfig::GameConfig,
log_full_error,
objects::{ActiveObjectType, ObjectType},
projection::ToMsl,
state::GameState,
};
use de_map::{
description::{InnerObject, Map},
io::{load_map, MapLoadingError},
size::MapBounds,
};
use de_objects::SpawnEvent;
use de_objects::Spawn;
use de_terrain::Terrain;
use futures_lite::future;
use iyes_loopless::prelude::*;
Expand Down Expand Up @@ -51,7 +55,6 @@ fn spawn_map(
task: Option<ResMut<MapLoadingTask>>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut spawn_events: EventWriter<SpawnEvent>,
mut move_focus_events: EventWriter<MoveFocusEvent>,
game_config: Res<GameConfig>,
) -> Progress {
Expand Down Expand Up @@ -98,7 +101,22 @@ fn spawn_map(

setup_light(&mut commands);
setup_terrain(&mut commands, &mut meshes, &mut materials, map.bounds());
spawn_events.send_batch(map.objects().iter().cloned().map(SpawnEvent::new));

for object in map.objects() {
let mut entity_commands = commands.spawn();
entity_commands
.insert(Spawn)
.insert(object.placement().to_transform());
let object_type = match object.inner() {
InnerObject::Active(object) => {
entity_commands.insert(object.player());
ObjectType::Active(object.object_type())
}
InnerObject::Inactive(object) => ObjectType::Inactive(object.object_type()),
};
entity_commands.insert(object_type);
}

commands.insert_resource(map.bounds());
true.into()
}
Expand Down
2 changes: 1 addition & 1 deletion crates/objects/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use collider::{ColliderCache, ObjectCollider};
pub use draft::Draft;
use draft::DraftPlugin;
pub use ichnography::{Ichnography, IchnographyCache};
pub use spawner::SpawnEvent;
pub use spawner::Spawn;
use spawner::SpawnerPlugin;

mod cache;
Expand Down
80 changes: 32 additions & 48 deletions crates/objects/src/spawner.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use bevy::{ecs::system::EntityCommands, prelude::*};
use bevy::prelude::*;
use de_core::{
events::ResendEventPlugin,
gconfig::GameConfig,
objects::{ActiveObjectType, MovableSolid, ObjectType, Playable, StaticSolid},
player::Player,
state::GameState,
};
use de_map::description::{ActiveObject, InnerObject, Object};
use iyes_loopless::prelude::*;

use crate::cache::ObjectCache;
Expand All @@ -14,64 +13,49 @@ pub(crate) struct SpawnerPlugin;

impl Plugin for SpawnerPlugin {
fn build(&self, app: &mut App) {
app.add_event::<SpawnEvent>()
.add_plugin(ResendEventPlugin::<SpawnEvent>::default())
.add_system(spawn.run_in_state(GameState::Playing));
app.add_system(spawn.run_in_state(GameState::Playing));
}
}

pub struct SpawnEvent {
object: Object,
}

impl SpawnEvent {
pub fn new(object: Object) -> Self {
Self { object }
}
}
/// Marker component for objects to be spawned on the map.
///
/// An entity with this component must have components
/// [`bevy::transform::components::Transform`],
/// [`de_core::objects::ObjectType`]. Active objects, in addition, must have
/// component [`de_core::player::Player`].
#[derive(Component)]
pub struct Spawn;

fn spawn(
mut commands: Commands,
game_config: Res<GameConfig>,
cache: Res<ObjectCache>,
mut events: EventReader<SpawnEvent>,
to_spawn: Query<(Entity, &ObjectType, Option<&Player>), With<Spawn>>,
) {
for event in events.iter() {
let object = &event.object;
for (entity, &object_type, player) in to_spawn.iter() {
info!("Spawning object {}", object_type);

let transform = object.placement().to_transform();
let global_transform = GlobalTransform::from(transform);
let mut entity_commands = commands.spawn_bundle((global_transform, transform));
let mut entity_commands = commands.entity(entity);
entity_commands.remove::<Spawn>().with_children(|parent| {
parent.spawn_scene(cache.get(object_type).scene());
});

let object_type = match object.inner() {
InnerObject::Active(object) => {
spawn_active(game_config.as_ref(), &mut entity_commands, object);
ObjectType::Active(object.object_type())
match object_type {
ObjectType::Active(active_type) => {
let player = *player.expect("Active object without an associated was spawned.");
if player == game_config.player() {
entity_commands.insert(Playable);
}

if active_type == ActiveObjectType::Attacker {
entity_commands.insert(MovableSolid);
} else {
entity_commands.insert(StaticSolid);
}
}
InnerObject::Inactive(object) => {
info!("Spawning inactive object {}", object.object_type());
ObjectType::Inactive(_) => {
entity_commands.insert(StaticSolid);
ObjectType::Inactive(object.object_type())
}
};

entity_commands.insert(object_type).with_children(|parent| {
parent.spawn_scene(cache.get(object_type).scene());
});
}
}

fn spawn_active(game_config: &GameConfig, commands: &mut EntityCommands, object: &ActiveObject) {
info!("Spawning active object {}", object.object_type());

commands.insert(object.player());
if object.player() == game_config.player() {
commands.insert(Playable);
}

if object.object_type() == ActiveObjectType::Attacker {
commands.insert(MovableSolid);
} else {
commands.insert(StaticSolid);
}
}
}

0 comments on commit 555693d

Please sign in to comment.