1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! [`Ability`]: a move of your game's own (an attack lunge, a ledge grab, a grapple, a
//! pogo), hooked into [`World::step`](super::World::step) without forking the movement.
//!
//! An ability runs twice per step: [`Ability::before`] ahead of the built-in moves, and
//! [`Ability::after`] once they moved the body. It may change anything public on the
//! [`Player`] (speed, facing, refills, [`Player::launch`] and [`Player::teleport`]), read
//! the [`Intent`] (your own buttons arrive in [`Intent::custom`]), read and change the
//! terrain (your own type through [`find_mut`](trait.Terrain.html#method.find_mut): break a
//! block, flip a switch, use up a pickup; the movement sees the change at once; it answers
//! in its own pixels under a `Builder::scale`, so scale the body's rects by
//! [`Step::scale`] first), and report
//! [`Event::Custom`]s (with up to four numbers of data: a knockback, a hit target).
//!
//! Name your modes and events with a [`CustomId`](super::CustomId) after your crate
//! (`CustomId::new("my_crate::lunge")`): abilities from different crates then never have
//! to agree on numbers. Custom buttons ([`Intent::custom`]) belong to the game, which binds
//! them to keys: an ability meant for others takes its button's bit as a parameter.
//!
//! To replace a built-in move, rule it out and do your own in its place. To react to
//! collisions, read [`Player::contacts`] in [`Ability::after`]. In a [`crate::Party`],
//! [`Step::others`] holds the other characters' bodies.
//!
//! To rule out single built-in moves for a step (no dash while attacking, no wall grab
//! while carrying, slow walking while pushing), clear them in [`Step::moves`] in
//! [`Ability::before`].
//!
//! To take the body over for a while, put it in [`Mode::Custom`](super::Mode::Custom):
//! no built-in move starts or runs then (no running, no gravity, no jumps or dashes), but
//! the body still moves by its speed through the collision, lands and is carried by moving
//! ground. The mode's clock counts up on its own; set [`Mode::Normal`](super::Mode::Normal)
//! to hand the body back.
//!
//! Abilities are part of the world: a cloned world clones them, and it must play out
//! identically, so keep their state in the ability (no clocks or randomness from outside)
//! and derive `Clone`.
//!
//! ```
//! use game_toolkit_platformer::sim::{Ability, CustomId, Event, Intent, Mode, Preset, Spot, Step, World};
//!
//! /// Custom button 0 lunges forward on the ground.
//! #[derive(Clone, Debug)]
//! struct Lunge;
//!
//! /// The lunge's mode and event id, named after the crate so no other ability's collides.
//! const LUNGE: CustomId = CustomId::new("my_game::lunge");
//!
//! impl Ability for Lunge {
//! fn before(&mut self, s: &mut Step<'_>) {
//! let p = &mut *s.player;
//! match p.mode {
//! Mode::Normal if s.intent.pressed(0) && p.grounded() => {
//! p.mode = Mode::Custom { id: LUNGE, t: 0.0 };
//! p.vx = 240.0 * p.facing as f32;
//! p.vy = 0.0;
//! s.events.push(Event::custom(LUNGE).with([p.vx, 0.0, 0.0, 0.0]));
//! }
//! Mode::Custom { id: LUNGE, t } if t >= 0.15 => p.mode = Mode::Normal,
//! _ => {}
//! }
//! // No dash out of the lunge's recovery, while custom button 0 is held.
//! s.moves.dash = !s.intent.held(0);
//! }
//! }
//!
//! let mut world = World::new(Preset::Hybrid.tuning(), Spot::TopLeft(20, 88));
//! world.abilities.push(Box::new(Lunge));
//! let mut floor = |r: game_toolkit_platformer::sim::Rect| r.bottom() > 100;
//! world.step(&mut floor, &Intent { custom_pressed: 1, ..Intent::default() });
//! assert!(world.events.iter().any(|e| e.custom_data(LUNGE).is_some()));
//! ```
use Any;
use Debug;
use ;
/// What an [`Ability`] gets to work with in one step.
/// A move of your own, run in every [`World::step`](super::World::step). Derive `Clone`
/// and `Debug`; both hooks default to nothing.
/// Cloning a boxed [`Ability`], so a world with abilities stays `Clone`, and reaching its
/// concrete type ([`super::World::ability`]). Implemented for every `Ability + Clone`; you
/// don't implement it yourself.