Skip to content
This repository has been archived by the owner on Oct 25, 2021. It is now read-only.

Implement avoid based on seek #62

Merged
merged 1 commit into from May 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions resources/prefabs/factions.ron
Expand Up @@ -6,8 +6,8 @@ Prefab (
name: (
name: "Plants"
),
faction_enemies: (
enemies: [],
faction_preys: (
preys: [],
),
),
),
Expand All @@ -16,8 +16,8 @@ Prefab (
name: (
name: "Herbivores"
),
faction_enemies: (
enemies: ["Plants"],
faction_preys: (
preys: ["Plants"],
),
),
),
Expand All @@ -26,8 +26,8 @@ Prefab (
name: (
name: "Carnivores"
),
faction_enemies: (
enemies: ["Herbivores"],
faction_preys: (
preys: ["Herbivores"],
),
),
),
Expand Down
53 changes: 25 additions & 28 deletions src/components/combat.rs
Expand Up @@ -129,31 +129,28 @@ impl<'a> PrefabData<'a> for HasFaction<String> {
}
}

// Store the faction entities this component's owner is hostile towards
/// The type is generic because we use `FactionEnemies<Entity>` as a component and `FactionEnemies<String>` for the prefab.
// Store the faction entities this component's owner considers to be prey
/// The type is generic because we use `FactionPrey<Entity>` as a component and `FactionPrey<String>` for the prefab.
#[derive(Default, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct FactionEnemies<T> {
pub enemies: Vec<T>,
pub struct FactionPrey<T> {
pub preys: Vec<T>,
}

impl Component for FactionEnemies<Entity> {
impl Component for FactionPrey<Entity> {
type Storage = HashMapStorage<Self>;
}

impl<T> FactionEnemies<T> {
pub fn is_enemy(&self, other: &T) -> bool
impl<T> FactionPrey<T> {
pub fn is_prey(&self, other: &T) -> bool
where
T: PartialEq,
{
self.enemies.contains(other)
self.preys.contains(other)
}
}

impl<'a> PrefabData<'a> for FactionEnemies<String> {
type SystemData = (
Write<'a, Factions>,
WriteStorage<'a, FactionEnemies<Entity>>,
);
impl<'a> PrefabData<'a> for FactionPrey<String> {
type SystemData = (Write<'a, Factions>, WriteStorage<'a, FactionPrey<Entity>>);
type Result = ();

fn add_to_entity(
Expand All @@ -163,13 +160,13 @@ impl<'a> PrefabData<'a> for FactionEnemies<String> {
_entities: &[Entity],
) -> Result<Self::Result, Error> {
let ref factions = (system_data.0).0;
let enemies: Vec<Entity> = self
.enemies
let preys: Vec<Entity> = self
.preys
.iter()
.map(|enemy| {
let faction = factions.get(enemy);
.map(|prey| {
let faction = factions.get(prey);
if faction.is_none() {
error!("Failed to load faction {:?}", enemy);
error!("Failed to load faction {:?}", prey);
}
faction
})
Expand All @@ -178,7 +175,7 @@ impl<'a> PrefabData<'a> for FactionEnemies<String> {
.collect();
system_data
.1
.insert(entity, FactionEnemies { enemies })
.insert(entity, FactionPrey { preys })
.expect("unreachable: we are inserting");
Ok(())
}
Expand All @@ -188,9 +185,9 @@ impl<'a> PrefabData<'a> for FactionEnemies<String> {
impl<'a> PrefabData<'a> for FactionPrefabData {
type SystemData = (
<Named as PrefabData<'a>>::SystemData,
<FactionEnemies<String> as PrefabData<'a>>::SystemData,
// We can't access Factions here, because Factions is already in use by `FactionEnemies<String>::SystemData`.
// As a workaround we use `Write` in `FactionEnemies<String>::SystemData.0` instead of `Read`
<FactionPrey<String> as PrefabData<'a>>::SystemData,
// We can't access Factions here, because Factions is already in use by `FactionPrey<String>::SystemData`.
// As a workaround we use `Write` in `FactionPrey<String>::SystemData.0` instead of `Read`
// Write<'a, Factions>,
);
type Result = ();
Expand All @@ -201,17 +198,17 @@ impl<'a> PrefabData<'a> for FactionPrefabData {
system_data: &mut Self::SystemData,
entities: &[Entity],
) -> Result<Self::Result, Error> {
let (ref mut named, ref mut factions_enemies) = system_data;
let (ref mut named, ref mut faction_preys) = system_data;

// Update our faction lookup table
if let Some(ref name) = self.name {
(factions_enemies.0).0.insert(name.name.to_string(), entity);
(faction_preys.0).0.insert(name.name.to_string(), entity);
}
self.name
.add_to_entity(entity, named, entities)
.expect("unreachable");
self.faction_enemies
.add_to_entity(entity, factions_enemies, entities)
self.faction_preys
.add_to_entity(entity, faction_preys, entities)
.expect("unreachable");
Ok(())
}
Expand All @@ -222,14 +219,14 @@ impl<'a> PrefabData<'a> for FactionPrefabData {
#[serde(deny_unknown_fields)]
pub struct FactionPrefabData {
name: Option<Named>,
faction_enemies: Option<FactionEnemies<String>>,
faction_preys: Option<FactionPrey<String>>,
}

#[derive(Default)]
pub struct Factions(HashMap<String, Entity>);

// The factions are stored inside the Ron file in a sorted way. They can only define
// factions as enemies that are on top of their definition. For example, 'Plants' cannot define 'Herbivores' as their enemies
// factions as prey that are on top of their definition. For example, 'Plants' cannot define 'Herbivores' as their prey
// because 'Herbivores' is defined after 'Plants'.
pub fn load_factions(world: &mut World) {
let prefab_handle = world.exec(|loader: PrefabLoader<'_, FactionPrefabData>| {
Expand Down
10 changes: 5 additions & 5 deletions src/main.rs
Expand Up @@ -5,6 +5,7 @@ use amethyst;
use amethyst::assets::PrefabLoaderSystem;
use amethyst::{
audio::AudioBundle,
core::frame_limiter::FrameRateLimitStrategy,
core::transform::{Transform, TransformBundle},
core::Named,
ecs::*,
Expand Down Expand Up @@ -101,11 +102,10 @@ fn main() -> amethyst::Result<()> {
.with_bundle(RenderBundle::new(pipe, Some(display_config)))?
.with_bundle(UiBundle::<String, String, NoCustomUi>::new())?;

let mut game = CoreApplication::<GameData, CustomStateEvent, CustomStateEventReader>::new(
resources,
LoadingState::default(),
game_data,
)?;
let mut game: CoreApplication<GameData, CustomStateEvent, CustomStateEventReader> =
CoreApplication::build(resources, LoadingState::default())?
.with_frame_limit(FrameRateLimitStrategy::Sleep, 60)
.build(game_data)?;
game.run();
Ok(())
}
37 changes: 34 additions & 3 deletions src/states/main_game.rs
Expand Up @@ -9,13 +9,16 @@ use amethyst::{
shrev::EventChannel,
State,
};
use rand::{thread_rng, Rng};

use crate::systems::behaviors::decision::{
ClosestSystem, Predator, Prey, QueryPredatorsAndPreySystem, SeekSystem,
};
use crate::{
resources::world_bounds::WorldBounds,
states::{paused::PausedState, CustomStateEvent},
systems::*,
};
use rand::{thread_rng, Rng};
use std::f32::consts::PI;

pub struct MainGameState {
Expand All @@ -27,8 +30,36 @@ impl Default for MainGameState {
fn default() -> Self {
MainGameState {
dispatcher: DispatcherBuilder::new()
.with(decision::DecisionSystem, "decision_system", &[])
.with(wander::WanderSystem, "wander_system", &["decision_system"])
.with(
QueryPredatorsAndPreySystem,
"query_predators_and_prey_system",
&[],
)
.with(
ClosestSystem::<Prey>::default(),
"closest_prey_system",
&["query_predators_and_prey_system"],
)
.with(
ClosestSystem::<Predator>::default(),
"closest_predator_system",
&["query_predators_and_prey_system"],
)
.with(
SeekSystem::<Prey>::new(1.0),
"seek_prey_system",
&["closest_prey_system"],
)
.with(
SeekSystem::<Predator>::new(-1.0),
"avoid_predator_system",
&["closest_predator_system"],
)
.with(
behaviors::wander::WanderSystem,
"wander_system",
&["seek_prey_system", "avoid_predator_system"],
)
.with(
movement::MovementSystem,
"movement_system",
Expand Down