17
17
18
18
#include " badguy/zeekling.hpp"
19
19
20
- #include < math.h>
21
-
20
+ #include " math/easing.hpp"
22
21
#include " math/random.hpp"
22
+ #include " math/util.hpp"
23
+ #include " math/vector.hpp"
23
24
#include " object/player.hpp"
24
25
#include " sprite/sprite.hpp"
26
+ #include " supertux/sector.hpp"
27
+
28
+ static const float FLYING_SPEED = 180 .f;
29
+ static const float CHARGING_SPEED = 150 .f;
30
+ static const float DIVING_SPEED = 280 .f;
31
+
32
+ static const float CHARGING_DURATION = 0 .3f ;
33
+ static const float DIVING_DURATION = 1 .5f ;
34
+ static const float RECOVER_DURATION = 2 .8f ;
35
+
36
+ static const float MIN_DETECT_RANGE_Y = 32 .f * 4 .5f ;
37
+ static const float MAX_DETECT_RANGE_Y = 512 .f;
38
+ static const float MIN_DETECT_RANGE_X = 10 .f;
39
+ static const float MAX_DETECT_RANGE_X = 32 .f * 15 .f;
40
+
41
+ static const float CATCH_OFFSET = -10 .f;
25
42
26
43
Zeekling::Zeekling (const ReaderMapping& reader) :
27
44
BadGuy(reader, " images/creatures/zeekling/zeekling.sprite" ),
28
- speed(gameRandom.randf(130 .0f , 171 .0f )),
29
- diveRecoverTimer(),
30
- state(FLYING),
31
- last_player(nullptr ),
32
- last_player_pos(0 .0f , 0 .0f ),
33
- last_self_pos(0 .0f , 0 .0f )
45
+ m_catch_pos(0 .f),
46
+ m_timer(),
47
+ m_state(FLYING)
34
48
{
35
49
m_physic.enable_gravity (false );
50
+ m_physic.set_velocity_x (FLYING_SPEED);
36
51
}
37
52
38
53
void
39
54
Zeekling::initialize ()
40
55
{
41
- m_physic.set_velocity_x (m_dir == Direction::LEFT ? -speed : speed );
56
+ m_physic.set_velocity_x (m_physic. get_velocity_x () * ( m_dir == Direction::LEFT ? -1 : 1 ) );
42
57
set_action (m_dir);
43
58
}
44
59
@@ -54,149 +69,202 @@ Zeekling::collision_squished(GameObject& object)
54
69
}
55
70
56
71
void
57
- Zeekling::onBumpHorizontal ()
58
- {
59
- if (state == FLYING) {
60
- m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT);
61
- set_action (m_dir);
62
- m_physic.set_velocity_x (m_dir == Direction::LEFT ? -speed : speed);
63
- } else
64
- if (state == DIVING) {
65
- m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT);
66
- state = FLYING;
67
- set_action (m_dir);
68
- m_physic.set_velocity (m_dir == Direction::LEFT ? -speed : speed, 0 );
69
- } else
70
- if (state == CLIMBING) {
71
- m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT);
72
- set_action (m_dir);
73
- m_physic.set_velocity_x (m_dir == Direction::LEFT ? -speed : speed);
74
- } else {
75
- assert (false );
76
- }
72
+ Zeekling::on_bump_horizontal ()
73
+ {
74
+ m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT);
75
+ set_action (m_dir);
76
+ m_physic.set_velocity_x (std::abs (m_physic.get_velocity_x ()) * (m_dir == Direction::LEFT ? -1 : 1 ));
77
+
78
+ if (m_state == DIVING)
79
+ {
80
+ // Diving attempt failed. So sad. Return to base.
81
+ recover ();
82
+ }
77
83
}
78
84
79
85
void
80
- Zeekling::onBumpVertical ()
86
+ Zeekling::on_bump_vertical ()
81
87
{
82
- if (BadGuy::get_state () == STATE_BURNING)
83
- {
84
- m_physic.set_velocity (0 , 0 );
85
- return ;
88
+ switch (m_state) {
89
+ case DIVING:
90
+ // Diving attempt failed. So sad. Return to base.
91
+ recover ();
92
+ break ;
93
+
94
+ case RECOVERING:
95
+ // I guess this is my new home now.
96
+ // Set start position to the current position.
97
+ fly ();
98
+ break ;
99
+
100
+ default :
101
+ break ;
86
102
}
87
- if (state == FLYING) {
88
- m_physic.set_velocity_y (0 );
89
- } else
90
- if (state == DIVING) {
91
- state = CLIMBING;
92
- m_physic.set_velocity_y (-speed);
93
- set_action (m_dir);
94
- } else
95
- if (state == CLIMBING) {
96
- state = FLYING;
97
- m_physic.set_velocity_y (0 );
98
- }
99
103
}
100
104
101
105
void
102
106
Zeekling::collision_solid (const CollisionHit& hit)
103
107
{
104
108
if (m_frozen)
105
- BadGuy::collision_solid (hit);
106
- else
107
109
{
108
- if (m_sprite->get_action () == " squished-left" ||
109
- m_sprite->get_action () == " squished-right" )
110
- {
111
- return ;
112
- }
110
+ BadGuy::collision_solid (hit);
111
+ return ;
112
+ }
113
113
114
- if (hit.top || hit.bottom ) {
115
- onBumpVertical ();
116
- }
117
- else if (hit.left || hit.right ) {
118
- onBumpHorizontal ();
119
- }
114
+ if (BadGuy::get_state () == STATE_SQUISHED ||
115
+ BadGuy::get_state () == STATE_BURNING)
116
+ {
117
+ return ;
120
118
}
119
+
120
+ if (hit.top || hit.bottom )
121
+ on_bump_vertical ();
122
+ else if (hit.left || hit.right )
123
+ on_bump_horizontal ();
121
124
}
122
125
123
- /* * Linear prediction of player and badguy positions to decide if we should enter the DIVING state. */
124
126
bool
125
127
Zeekling::should_we_dive ()
126
128
{
129
+ using RaycastResult = CollisionSystem::RaycastResult;
130
+
127
131
if (m_frozen)
128
132
return false ;
129
133
130
- const auto player = get_nearest_player ();
131
- if (player && last_player && (player == last_player)) {
134
+ Player* player = get_nearest_player ();
135
+ if (!player)
136
+ return false ;
132
137
133
- // Get positions and calculate movement .
134
- const Vector& player_pos = player-> get_pos () ;
135
- const Vector player_mov = (player_pos - last_player_pos );
136
- const Vector self_pos = m_col. m_bbox . p1 ();
137
- const Vector self_mov = (self_pos - last_self_pos );
138
+ // Left/rightmost point of the hitbox .
139
+ Vector eye ;
140
+ const Rectf& bbox = get_bbox (). grown ( 1 . f );
141
+ eye = bbox. get_middle ();
142
+ eye. x = m_dir == Direction::LEFT ? bbox. get_left () : bbox. get_right ( );
138
143
139
- // New vertical speed to test with.
140
- float vy = 2 *fabsf (self_mov.x );
144
+ const Vector& playermiddle = player->get_bbox ().get_middle ();
141
145
142
- // Do not dive if we are not above the player.
143
- float height = player_pos.y - self_pos.y ;
144
- if (height <= 0 ) return false ;
146
+ // Do not dive if we are too close to the player.
147
+ float height = player->get_bbox ().get_top () - get_bbox ().get_top ();
148
+ if (height <= MIN_DETECT_RANGE_Y)
149
+ return false ;
145
150
146
- // Do not dive if we are too far above the player.
147
- if (height > 512 ) return false ;
151
+ // Do not dive if we are too far above the player.
152
+ if (height > MAX_DETECT_RANGE_Y)
153
+ return false ;
148
154
149
- // Do not dive if we would not descend faster than the player.
150
- float relSpeed = vy - player_mov. y ;
151
- if (relSpeed <= 0 ) return false ;
155
+ float xdist = std::abs (eye. x - playermiddle. x );
156
+ if (! math::in_bounds (xdist, MIN_DETECT_RANGE_X, MAX_DETECT_RANGE_X))
157
+ return false ;
152
158
153
- // Guess number of frames to descend to same height as player.
154
- float estFrames = height / relSpeed;
159
+ RaycastResult result = Sector::get ().get_first_line_intersection (eye, playermiddle, false , nullptr );
155
160
156
- // Guess where the player would be at this time.
157
- float estPx = (player_pos.x + (estFrames * player_mov.x ));
161
+ auto * resultobj = std::get_if<CollisionObject*>(&result.hit );
162
+
163
+ if (result.is_valid && resultobj &&
164
+ *resultobj == player->get_collision_object ())
165
+ {
166
+ m_target_y = player->get_bbox ().get_top () + CATCH_OFFSET;
167
+ return true ;
168
+ }
158
169
159
- // Guess where we would be at this time.
160
- float estBx = (self_pos. x + (estFrames * self_mov. x ));
170
+ return false ;
171
+ }
161
172
162
- // Allow for slight inaccuracies.
163
- if (fabsf (estPx - estBx) < 8 ) return true ;
164
- }
173
+ void
174
+ Zeekling::set_speed (float speed)
175
+ {
176
+ m_physic.set_velocity_x (speed * (m_dir == Direction::LEFT ? -1 : 1 ));
177
+ }
165
178
166
- // Update the last player tracked, as well as our positions.
167
- last_player = player;
168
- if (player) {
169
- last_player_pos = player->get_pos ();
170
- last_self_pos = m_col.m_bbox .p1 ();
171
- }
179
+ void Zeekling::fly ()
180
+ {
181
+ m_state = FLYING;
182
+ set_speed (FLYING_SPEED);
183
+ m_start_position.y = get_pos ().y ;
184
+ set_action (m_dir);
185
+ }
172
186
173
- return false ;
187
+ void Zeekling::charge ()
188
+ {
189
+ m_state = CHARGING;
190
+ m_timer.start (CHARGING_DURATION);
191
+ set_speed (CHARGING_SPEED);
192
+ set_action (" charge" , m_dir);
193
+ }
194
+
195
+ void Zeekling::dive ()
196
+ {
197
+ m_state = DIVING;
198
+ m_timer.start (DIVING_DURATION);
199
+ set_speed (DIVING_SPEED);
200
+ set_action (" dive" , m_dir);
201
+ }
202
+
203
+ void Zeekling::recover ()
204
+ {
205
+ m_state = RECOVERING;
206
+ m_catch_pos = get_pos ().y ;
207
+ m_timer.start (RECOVER_DURATION);
208
+ set_action (m_dir);
174
209
}
175
210
176
211
void
177
212
Zeekling::active_update (float dt_sec) {
178
- if (state == FLYING) {
179
- if (should_we_dive ()) {
180
- state = DIVING;
181
- m_physic.set_velocity_y (2 *fabsf (m_physic.get_velocity_x ()));
182
- set_action (" dive" , m_dir);
213
+ switch (m_state) {
214
+ case FLYING:
215
+ if (!should_we_dive ())
216
+ break ;
217
+
218
+ charge ();
219
+
220
+ break ;
221
+
222
+ case CHARGING:
223
+ if (m_timer.check ())
224
+ {
225
+ dive ();
226
+ }
227
+ break ;
228
+
229
+ case DIVING:
230
+ {
231
+ if (m_timer.check ())
232
+ {
233
+ recover ();
234
+ break ;
235
+ }
236
+
237
+ const float dist = m_target_y - m_start_position.y ;
238
+ const double progress = CubicEaseIn (static_cast <double >(1 .f - m_timer.get_progress ()));
239
+ const float value = m_target_y - (static_cast <float >(progress) * dist);
240
+ const Vector pos (get_pos ().x , value);
241
+ set_pos (pos);
242
+
243
+ break ;
183
244
}
184
- BadGuy::active_update (dt_sec);
185
- return ;
186
- } else if (state == DIVING) {
187
- BadGuy::active_update (dt_sec);
188
- return ;
189
- } else if (state == CLIMBING) {
190
- // Stop climbing when we're back at initial height.
191
- if (get_pos ().y <= m_start_position.y ) {
192
- state = FLYING;
193
- m_physic.set_velocity_y (0 );
245
+
246
+ case RECOVERING:
247
+ {
248
+ if (m_timer.check ())
249
+ {
250
+ fly ();
251
+ break ;
252
+ }
253
+
254
+ const float dist = m_catch_pos - m_start_position.y ;
255
+ const double progress = QuadraticEaseInOut (static_cast <double >(m_timer.get_progress ()));
256
+ const float value = m_catch_pos - (static_cast <float >(progress) * dist);
257
+ const Vector pos (get_pos ().x , value);
258
+ set_pos (pos);
259
+
260
+ break ;
194
261
}
195
- BadGuy::active_update (dt_sec);
196
- return ;
197
- } else {
198
- assert (false );
262
+
263
+ default :
264
+ break ;
199
265
}
266
+
267
+ BadGuy::active_update (dt_sec);
200
268
}
201
269
202
270
void
@@ -211,7 +279,7 @@ Zeekling::unfreeze(bool melt)
211
279
{
212
280
BadGuy::unfreeze (melt);
213
281
m_physic.enable_gravity (false );
214
- state = FLYING;
282
+ m_state = FLYING;
215
283
initialize ();
216
284
}
217
285
@@ -221,4 +289,10 @@ Zeekling::is_freezable() const
221
289
return true ;
222
290
}
223
291
292
+ void Zeekling::on_flip (float height)
293
+ {
294
+ BadGuy::on_flip (height);
295
+ m_start_position.y = get_pos ().y ;
296
+ }
297
+
224
298
/* EOF */
0 commit comments