Skip to main content

weasel/
ability.rs

1//! Module to manage abilities.
2
3use crate::actor::{Action, ActorRules};
4use crate::battle::{Battle, BattleRules};
5use crate::entity::EntityId;
6use crate::error::{WeaselError, WeaselResult};
7use crate::event::{Event, EventKind, EventProcessor, EventQueue, EventRights, EventTrigger};
8use crate::util::Id;
9#[cfg(feature = "serialization")]
10use serde::{Deserialize, Serialize};
11use std::any::Any;
12
13/// Type to represent an ability.
14///
15/// Abilities are actions that actors can undertake in order to modify an aspect of themselves or
16/// of the world. Typical abilities are movements and attacks.
17pub type Ability<R> = <<R as BattleRules>::AR as ActorRules<R>>::Ability;
18
19/// Alias for `Ability<R>::Id`.
20pub type AbilityId<R> = <Ability<R> as Id>::Id;
21
22/// Type to drive the generation of a given actor's set of abilities.
23pub type AbilitiesSeed<R> = <<R as BattleRules>::AR as ActorRules<R>>::AbilitiesSeed;
24
25/// Type to customize in which way an ability is activated.
26///
27/// For example, this's useful in case you have abilities which can be activated with a
28/// different degree of intensity.
29pub type Activation<R> = <<R as BattleRules>::AR as ActorRules<R>>::Activation;
30
31/// Encapsulatess the data used to describe an alteration of one or more abilities.
32pub type AbilitiesAlteration<R> = <<R as BattleRules>::AR as ActorRules<R>>::AbilitiesAlteration;
33
34/// Event to make an actor activate an ability.
35///
36/// # Examples
37/// ```
38/// use weasel::{
39///     battle_rules, rules::empty::*, ActivateAbility, Battle, BattleRules, CreateCreature,
40///     CreateTeam, EntityId, EventTrigger, Server, StartTurn,
41/// };
42///
43/// battle_rules! {}
44///
45/// let battle = Battle::builder(CustomRules::new()).build();
46/// let mut server = Server::builder(battle).build();
47///
48/// let team_id = 1;
49/// CreateTeam::trigger(&mut server, team_id).fire().unwrap();
50/// let creature_id = 1;
51/// let position = ();
52/// CreateCreature::trigger(&mut server, creature_id, team_id, position)
53///     .fire()
54///     .unwrap();
55/// StartTurn::trigger(&mut server, EntityId::Creature(creature_id))
56///     .fire()
57///     .unwrap();
58///
59/// let ability_id = 99;
60/// let result =
61///     ActivateAbility::trigger(&mut server, EntityId::Creature(creature_id), ability_id)
62///         .fire();
63/// // We get an error because the creature doesn't know this ability.
64/// // The set of abilities known by creatures must defined in 'ActorRules'.
65/// assert!(result.is_err());
66/// ```
67#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
68pub struct ActivateAbility<R: BattleRules> {
69    #[cfg_attr(
70        feature = "serialization",
71        serde(bound(
72            serialize = "EntityId<R>: Serialize",
73            deserialize = "EntityId<R>: Deserialize<'de>"
74        ))
75    )]
76    entity_id: EntityId<R>,
77
78    #[cfg_attr(
79        feature = "serialization",
80        serde(bound(
81            serialize = "AbilityId<R>: Serialize",
82            deserialize = "AbilityId<R>: Deserialize<'de>"
83        ))
84    )]
85    ability_id: AbilityId<R>,
86
87    #[cfg_attr(
88        feature = "serialization",
89        serde(bound(
90            serialize = "Option<Activation<R>>: Serialize",
91            deserialize = "Option<Activation<R>>: Deserialize<'de>"
92        ))
93    )]
94    activation: Option<Activation<R>>,
95}
96
97impl<R: BattleRules> ActivateAbility<R> {
98    /// Returns a trigger for this event.
99    pub fn trigger<P: EventProcessor<R>>(
100        processor: &mut P,
101        entity_id: EntityId<R>,
102        ability_id: AbilityId<R>,
103    ) -> ActivateAbilityTrigger<R, P> {
104        ActivateAbilityTrigger {
105            processor,
106            entity_id,
107            ability_id,
108            activation: None,
109        }
110    }
111
112    /// Returns the id of the actor who is activating the ability.
113    pub fn entity_id(&self) -> &EntityId<R> {
114        &self.entity_id
115    }
116
117    /// Returns the id of the ability to be activated.
118    pub fn ability_id(&self) -> &AbilityId<R> {
119        &self.ability_id
120    }
121
122    /// Returns the activation profile for the ability.
123    pub fn activation(&self) -> &Option<Activation<R>> {
124        &self.activation
125    }
126}
127
128impl<R: BattleRules> std::fmt::Debug for ActivateAbility<R> {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        write!(
131            f,
132            "ActivateAbility {{ entity_id: {:?}, ability_id: {:?}, activation: {:?} }}",
133            self.entity_id, self.ability_id, self.activation
134        )
135    }
136}
137
138impl<R: BattleRules> Clone for ActivateAbility<R> {
139    fn clone(&self) -> Self {
140        ActivateAbility {
141            entity_id: self.entity_id.clone(),
142            ability_id: self.ability_id.clone(),
143            activation: self.activation.clone(),
144        }
145    }
146}
147
148impl<R: BattleRules + 'static> Event<R> for ActivateAbility<R> {
149    fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
150        // Check if this entity is an actor.
151        if !self.entity_id.is_actor() {
152            return Err(WeaselError::NotAnActor(self.entity_id.clone()));
153        }
154        // Verify that the actor exists.
155        if let Some(actor) = battle.entities().actor(&self.entity_id) {
156            // Verify that the actor can act.
157            if !battle.state.rounds.is_acting(&self.entity_id) {
158                return Err(WeaselError::ActorNotReady(self.entity_id.clone()));
159            }
160            // Verify if the creature knowns this ability.
161            if let Some(ability) = actor.ability(&self.ability_id) {
162                // Verify if this ability can be activated.
163                battle
164                    .rules
165                    .actor_rules()
166                    .activable(&battle.state, Action::new(actor, ability, &self.activation))
167                    .map_err(|err| {
168                        WeaselError::AbilityNotActivable(
169                            self.entity_id.clone(),
170                            self.ability_id.clone(),
171                            Box::new(err),
172                        )
173                    })
174            } else {
175                Err(WeaselError::AbilityNotKnown(
176                    self.entity_id.clone(),
177                    self.ability_id.clone(),
178                ))
179            }
180        } else {
181            Err(WeaselError::EntityNotFound(self.entity_id.clone()))
182        }
183    }
184
185    fn apply(&self, battle: &mut Battle<R>, event_queue: &mut Option<EventQueue<R>>) {
186        let actor = battle
187            .state
188            .entities
189            .actor(&self.entity_id)
190            .unwrap_or_else(|| {
191                panic!("constraint violated: entity {:?} not found", self.entity_id)
192            });
193        let ability = actor.ability(&self.ability_id).unwrap_or_else(|| {
194            panic!(
195                "constraint violated: ability {:?} not found in actor {:?}",
196                self.ability_id, self.entity_id
197            )
198        });
199        battle.rules.actor_rules().activate(
200            &battle.state,
201            Action::new(actor, ability, &self.activation),
202            event_queue,
203            &mut battle.entropy,
204            &mut battle.metrics.write_handle(),
205        );
206    }
207
208    fn kind(&self) -> EventKind {
209        EventKind::ActivateAbility
210    }
211
212    fn box_clone(&self) -> Box<dyn Event<R> + Send> {
213        Box::new(self.clone())
214    }
215
216    fn as_any(&self) -> &dyn Any {
217        self
218    }
219
220    fn rights<'a>(&'a self, battle: &'a Battle<R>) -> EventRights<'a, R> {
221        let actor = battle
222            .state
223            .entities
224            .actor(&self.entity_id)
225            .unwrap_or_else(|| {
226                panic!("constraint violated: entity {:?} not found", self.entity_id)
227            });
228        EventRights::Team(actor.team_id())
229    }
230}
231
232/// Trigger to build and fire an `ActivateAbility` event.
233pub struct ActivateAbilityTrigger<'a, R, P>
234where
235    R: BattleRules,
236    P: EventProcessor<R>,
237{
238    processor: &'a mut P,
239    entity_id: EntityId<R>,
240    ability_id: AbilityId<R>,
241    activation: Option<Activation<R>>,
242}
243
244impl<'a, R, P> ActivateAbilityTrigger<'a, R, P>
245where
246    R: BattleRules + 'static,
247    P: EventProcessor<R>,
248{
249    /// Adds an activation profile to customize this ability instance.
250    pub fn activation(&'a mut self, activation: Activation<R>) -> &'a mut Self {
251        self.activation = Some(activation);
252        self
253    }
254}
255
256impl<'a, R, P> EventTrigger<'a, R, P> for ActivateAbilityTrigger<'a, R, P>
257where
258    R: BattleRules + 'static,
259    P: EventProcessor<R>,
260{
261    fn processor(&'a mut self) -> &'a mut P {
262        self.processor
263    }
264
265    /// Returns an `ActivateAbility` event.
266    fn event(&self) -> Box<dyn Event<R> + Send> {
267        Box::new(ActivateAbility {
268            entity_id: self.entity_id.clone(),
269            ability_id: self.ability_id.clone(),
270            activation: self.activation.clone(),
271        })
272    }
273}