Derive Macro big_brain_derive::Action[][src]

#[derive(Action)]
{
    // Attributes available to this derive:
    #[action]
}

Actions in big-brain are defined through this derive macro. Once defined, they can be freely used in a .ron file. They define actual behaviors that an actor will perform when the Thinker engine picks it as the active action, based on Considerations.

Definition Example

use specs::{Component, Entity, System, WriteStorage};
use big_brain::{Action, ActionState};

// These are your game's components.
use crate::components;

// This will be used to create `Action` components. They MUST implement the
// `specs::Component` trait.
#[derive(Debug, Clone, Component, Action)]
pub struct Eat {
    // All actions **must** have a public `actor` field. This will be populated
    // with the actual actor performing the Action. The `Entity` associated with
    // the `Action` itself is distinct from the actor.
    pub actor: Entity,

    // `default` fields will be populated using default::Default() when the
    // Action is instantiated. These cannot be used as params.
    #[action(default)]
    pub foo: f32,

    // `param` fields will be populated using the value passed in through the
    // `.ron` file.
    #[action(param)]
    pub reduce_by: f32,
}

// Once an Action component is defined, we define a System that can act on it.
pub struct EatSystem;

impl<'a> System<'a> for EatSystem {
    type SystemData = (
        WriteStorage<'a, components::Hunger>,
        // This is the actual Eat component.
        WriteStorage<'a, Eat>,
        // An ActionState component is attached to every Action Entity.
        // It contains the current running status of the Action, and will be
        // updated as needed by the actor's Thinker.
        WriteStorage<'a, ActionState>,
    );
    fn run(&mut self, (mut hungers, mut eat_actions, mut states): Self::SystemData) {
        // You can join the Eat and ActionState together. They're on the same component.
        for (state, eat_action) in (&mut states, &mut eat_actions).join() {
            // Any components attached to the actor must be fetched separately.
            if let Some(hunger) = hungers.get_mut(eat_action.actor.clone()) {
                match state {
                    // At the very least, every Action should handle the
                    // `Requested` state.
                    ActionState::Requested => {
                        hunger.hunger -= eat_action.reduce_by;
                        // Success tells the Thinker that this action succeeded!
                        *state = ActionState::Success;
                    }
                    // Make sure to handle Cancelled for long-running Actions.
                    // The Thinker will not continue until the state is either
                    // Success or Failure.
                    ActionState::Cancelled => {
                        *state = ActionState::Failure;
                    }
                    _ => {}
                }
            }
        }
    }
}

Usage Example

(
    picker: {"FirstToScore": ()},
    // Actions are defined using the `then` param to Choices
    choices: [(
        consider: [{"Hunger": ()}],
        // We can use the param defined in our derive definition here.
        // The `foo` field will be defaulted and cannot be defined here.
        then: {"Eat": (reduce_by: 80.0)},
    )]
)